Added SAML-Toolkits php-saml to composer and installed it to the vendor folder. Implemented SSO with JumpCloud
🚀 Deploy website on push / 🎉 Deploy (push) Successful in 31s

Signed-off-by: rodude123 <rodude123@gmail.com>
This commit is contained in:
2024-01-01 13:52:30 +00:00
parent 430e1c65ca
commit 7b8e81e1f7
14 changed files with 351 additions and 72 deletions
+34
View File
@@ -5,6 +5,7 @@ namespace api\user;
use Firebase\JWT\JWT;
use PDO;
use function api\utils\dbConn;
use function api\utils\getSAMLSettings;
use function api\utils\getSecretKey;
require_once __DIR__ . "/../utils/config.php";
@@ -138,5 +139,38 @@ class userData
return false;
}
/**
* Get the SAML settings
* @return array - SAML settings
*/
public function getSamlConf(): array
{
return getSAMLSettings();
}
/**
* Check if the SAML user exists
* @param string $username - Username
* @param string $email - Email
* @return bool - True if the user exists, false if not
*/
public function checkSAMLUser(string $username, string $email): bool
{
$conn = dbConn();
$stmt = $conn->prepare("SELECT * FROM users WHERE username = :username AND email = :email");
$stmt->bindParam(":username", $username);
$stmt->bindParam(":email", $email);
$stmt->execute();
// set the resulting array to associative
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($result)
{
return true;
}
return false;
}
}
+76 -24
View File
@@ -5,6 +5,8 @@ require_once __DIR__ . "/../utils/routesInterface.php";
require_once "userData.php";
use api\utils\routesInterface;
use OneLogin\Saml2\Auth;
use OneLogin\Saml2\Error;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\App;
@@ -12,14 +14,17 @@ use Slim\App;
class userRoutes implements routesInterface
{
private userData $user;
private Auth $samlAuth;
/**
* constructor used to instantiate a base user routes, to be used in the index.php file.
* @param App $app - the slim app used to create the routes
* @throws Error
*/
public function __construct(App $app)
{
$this->user = new userData();
$this->samlAuth = new Auth($this->user->getSamlConf());
$this->createRoutes($app);
}
@@ -30,31 +35,9 @@ class userRoutes implements routesInterface
*/
public function createRoutes(App $app): void
{
$app->post("/user/login", function (Request $request, Response $response)
$app->get("/user/login", function (Request $request, Response $response)
{
// get request data
$data = $request->getParsedBody();
if (empty($data["username"]) || empty($data["password"]))
{
// uh oh user sent empty data
return $response->withStatus(400);
}
if ($this->user->checkUser($data["username"], $data["password"]))
{
// yay, user is logged in
$_SESSION["token"] = $this->user->createToken($data["username"]);
$_SESSION["username"] = $data["username"];
$inactive = 60 * 60 * 48; // 2 days
$_SESSION["timeout"] = time() + $inactive;
$response->getBody()->write(json_encode(array("token" => $_SESSION["token"])));
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Unauthorised")));
return $response->withStatus(401);
$this->samlAuth->login();
});
$app->get("/user/logout", function (Request $request, Response $response)
@@ -92,6 +75,20 @@ class userRoutes implements routesInterface
});
$app->get("/user/metadata", function (Request $request, Response $response)
{
$settings = $this->samlAuth->getSettings();
$metadata = $settings->getSPMetadata();
$errors = $settings->validateMetadata($metadata);
if (empty($errors))
{
$response->getBody()->write($metadata);
return $response->withHeader("Content-Type", "text/xml");
}
$response->getBody()->write(json_encode(array("error" => $errors)));
return $response->withStatus(500);
});
$app->get("/user/checkResetEmail/{email}", function (Request $request, Response $response, array $args)
{
if (empty($args["email"]))
@@ -139,6 +136,61 @@ class userRoutes implements routesInterface
return $response->withStatus(401);
});
$app->post("/user/login", function (Request $request, Response $response)
{
// get request data
$data = $request->getParsedBody();
if (empty($data["username"]) || empty($data["password"]))
{
// uh oh user sent empty data
return $response->withStatus(400);
}
if ($this->user->checkUser($data["username"], $data["password"]))
{
// yay, user is logged in
$_SESSION["token"] = $this->user->createToken($data["username"]);
$_SESSION["username"] = $data["username"];
$inactive = 60 * 60 * 48; // 2 days
$_SESSION["timeout"] = time() + $inactive;
$response->getBody()->write(json_encode(array("token" => $_SESSION["token"])));
return $response;
}
$response->getBody()->write(json_encode(array("error" => "Unauthorised")));
return $response->withStatus(401);
});
$app->post("/user/acs", function (Request $request, Response $response)
{
$this->samlAuth->processResponse();
$attributes = $this->samlAuth->getAttributes();
// $username = $attributes["username"][0];
// $email = $attributes["email"][0];
$response->getBody()->write(json_encode($attributes));
return $response;
// if ($this->user->checkSAMLUser($username, $email))
// {
// // yay, user is logged in
// $_SESSION["token"] = $this->user->createToken($username);
// $_SESSION["username"] = $username;
// $_SESSION["email"] = $email;
//
// $inactive = 60 * 60 * 48; // 2 days
// $_SESSION["timeout"] = time() + $inactive;
//
// return $response->withHeader("Location", "https://rohitpai.co.uk/editor/editor.html")->withStatus(302);
// }
//
// $response->getBody()->write(json_encode(array("error" => "Unauthorised")));
// return $response->withStatus(401);
});
$app->post("/user/changePassword", function (Request $request, Response $response)
{
if (empty($_SESSION["resetToken"]) && empty($_SESSION["resetEmail"]))
+4
View File
@@ -190,6 +190,10 @@ div.categories {
width: 100%;
}
div.form input[type="submit"] {
align-self: flex-start;
}
.image img, .image_resized img {
max-width: 100%;
-webkit-border-radius: 10px;
-5
View File
@@ -162,11 +162,6 @@ div.form .formControl.passwordControl {
display: block;
}
form input[type="submit"],
div.form input[type="submit"] {
align-self: flex-start;
}
form .formControl input:not([type="submit"]), form .formControl textarea,
form .formControl .ck.ck-editor__top .ck-sticky-panel .ck-toolbar,
form .formControl .ck.ck-editor__main .ck-content, div.menu input:not([type="submit"]),
+14 -6
View File
@@ -66,14 +66,22 @@ div#login input[type=submit]{
margin: 0;
}
div.btnContainer {
width: 100%;
width: 60%;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
flex-direction: column;
justify-content: center;
align-items: stretch;
gap: 1em;
}
div.btnContainer a {
justify-content: center;
text-transform: unset;
}
form div.btnContainer input[type="submit"] {
align-self: stretch;
}
div.btnContainer a:not(.btn) {
+9 -5
View File
@@ -32,8 +32,9 @@
<div class="btnContainer">
<input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut">
<a href="#" id="resetPwd">Reset Password</a>
<a href="/api/user/login" class="btn btnPrimary boxShadowIn boxShadowOut">Login with Jump Cloud</a>
<button type="button" id="resetPwd" class="btn btnPrimary boxShadowIn boxShadowOut">Reset Password
</button>
</div>
</form>
</div>
@@ -54,7 +55,8 @@
<div class="btnContainer">
<input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut">
<a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="loginBtn">Login</a>
<button type="button" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="loginBtn">Login
</button>
</div>
</form>
</div>
@@ -75,7 +77,9 @@
<div class="btnContainer">
<input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut">
<a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="resendEmail">Resend Email</a>
<button type="button" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut" id="resendEmail">Resend
Email
</button>
</div>
</form>
</div>
@@ -102,7 +106,7 @@
<div class="btnContainer">
<input type="submit" value="Submit" class="btn btnPrimary boxShadowIn boxShadowOut">
<a href="#" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut">Login</a>
<button type="button" class="loginBtn btn btnPrimary boxShadowIn boxShadowOut">Login</button>
</div>
</form>
</div>