Sample implementation of Single Sign-On
Process flow diagram
Below we present a step-by-step example implementation of single sign-on.
1. Generate auhorization url
At the beginning, you need to generate a URL to the authorization endpoint. For this purpose, you need to know the client_id of the application.
//GENERATTE STATE HERE AND SAVE
$state = md5(time()); // Not great example ;-)
$authenticationUri = $mavryxApiService->api()->OAuth2->getAuthorizationUrl([
'client_id' => "c940b0ba-d81d-4739-9872-ca762b57ef69",
'response_type' => "code",
'state' => $state,
'redirect_uri' => "https://someapp.co.uk/authenticate",
]);
//example authenticationUri
// https://auth.mavryx.software/oauth2/signin?client_id=c940b0ba-d81d-4739-9872-ca762b57ef69&response_type=code&state=123131234&redirect_uri=https://someapp.co.uk/authenticate
Parameter name | Type | Related | Description |
---|---|---|---|
client_id | Mavryx/Types/Uuid | Mavryx/Client | Application Client ID |
response_type | Mavryx/Types/String | “code” | |
state | Mavryx/Types/String | Your application state | |
redirect_uri | Mavryx/Types/String | Where to redirect the user after successful login |
2. Redirect user to authorization endpoint
Next, you need to redirect the user to the authorization endpoint.
return $this->redirect($authenticationUri);
3. User authenticate
If the user hasn’t previously logged into the Mavryx platform, they will see the following login screen. If the user has previously logged in, they won’t see this login screen and will be automatically redirected to the target application with the generated code token.
4. Validate response and authorization code token
After successful login, the user will be redirected to the application, and the code token will be available in the URL parameters.
// Example url https://someapp.co.uk/authenticate?code=fsadf097asf9fa7sf7sad0f0sa&state=123131234
$code = $request->get('code');
$state = $request->get('state');
//VALIDATE STATE AND AUTHORIZATION CODE TOKEN HERE
Parameter name | Type | Related | Description |
---|---|---|---|
code | Mavryx/Types/String | Authorization code token | |
state | Mavryx/Types/String | Your application state |
5. Exchange code token to access token
Then, you can exchange the authorization code token for an access token. Remember that you have a limited time to perform this exchange (default: 120 seconds).
If the authorization is successful, you should receive an access token and a refresh token in the response.
The access token and refresh token are 100% compliant with JWT. Therefore, you can use any JWT-compliant library for token manipulation and validation. Ex. https://web-token.spomky-labs.com
# POST:
# client_id = "APP_CLIENT_ID",
# client_secret = "APP_SECRET",
# grant_type = 'authorization_code',
# code = "CODE"
# https://auth-dev.mavryx.solutions/oauth2/token
$response = $mavryxApiService->api()->OAuth2->authorize([
'client_id' => "c940b0ba-d81d-4739-9872-ca762b57ef69",
'client_secret' => "APP_SECRET",
'grant_type' => 'authorization_code',
'code' => $code,
]);
$accessToken = $response->getAccessToken();
$refreshToken = $response->getRefreshToken();
if($accessToken && $refreshToken && $accessToken->isValid() && $refreshToken->isValid())
{
} else die();
Request parameters
Parameter name | Type | Related | Description |
---|---|---|---|
client_id | Mavryx/Types/Uuid | Mavryx/Client | Application Client ID |
client_secret | Mavryx/Types/String | Your application secret | |
grant_type | Mavryx/Types/String | “authorization_code” | |
code | Mavryx/Types/String | Mavryx/CodeToken | Authorization code token |
Response objects
Parameter name | Type | Related | Description |
---|---|---|---|
access_token | Mavryx/Types/String | Mavryx/AccesToken | Access token |
refresh_token | Mavryx/Types/String | Mavryx/RefreshToken | Refresh token |
6. Save token and retrieve user from database
At this stage, you can save the access token and refresh token in a secure location, such as sessions. Additionally, you can automatically log in the user to the application.
$mavryxUser = $mavryxApiService->api()->OAuth2->setToken($userToken)->user()->me()()->item();
$email = $mavryxUser->client_id;
$user = $entityManager->getRepository(User::class)->findOneBy(['email' => $email]);
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
Code example:
<?php
namespace App\Controller;
use App\Entity\User;
use App\Manager\UserPasswordManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler;
use App\Security\Oauth2Authenticator;
use Symfony\Component\Security\Http\Event\LogoutEvent;
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Mavryx\Bundle\Service\MavryxApiService;
class SecurityController extends AbstractController
{
/**
* @Route("/authenticate", name="app_authenticate")
*/
public function authenticate(AuthenticationUtils $authenticationUtils,Request $request,MavryxApiService $mavryxApiService): Response
{
$userToken = $request->cookies->get('US');
if(!$userToken)
{
$code = $request->get('code');
$state = $request->get('state');
//VALIDATE STATE HERE
$settings = $this->getParameter("mavryx");
$response = $mavryxApiService->api()->OAuth2->authorize([
'client_id' => $settings['credentials']['client_id'],
'client_secret' => $settings['credentials']['client_secret'],
'grant_type' => 'authorization_code',
'code' => $code,
]);
$userToken = $response->getAccessToken();
$response = new RedirectResponse("/");
$cookie = NULL;
if($userToken && $userToken->isValid())
{
$cookie = new Cookie(
"US", // Cookie name
$userToken->toString(), // Cookie content
$userToken->getExpresIn(), // Expiration date
"/", // Path
"your_app_domain.com", // Domain
$request->getScheme() === 'https', // Secure
false, // HttpOnly
true, // Raw
// 'Strict' // SameSite policy
);
} else {
$cookie = new Cookie(
"US", // Cookie name
"", // Cookie content
time()-1000, // Expiration date
"/", // Path
"your_app_domain.com", // Domain
$request->getScheme() === 'https', // Secure
false, // HttpOnly
true, // Raw
// 'Strict' // SameSite policy
);
}
$response->headers->setCookie($cookie);
$response->setTargetUrl('/');
return $response;
} else {
$cookie = new Cookie(
"US", // Cookie name
"", // Cookie content
time()-1000, // Expiration date
"/", // Path
"your_app_domain.com", // Domain
$request->getScheme() === 'https', // Secure
false, // HttpOnly
true, // Raw
// 'Strict' // SameSite policy
);
$response = new RedirectResponse("/");
$response->headers->setCookie($cookie);
return $response;
}
}
/**
* @Route("/logout", name="authentication_logout")
*/
public function logoutAction(Request $request,
EventDispatcherInterface $eventDispatcher,
TokenStorageInterface $tokenStorage)
{
$logoutEvent = new LogoutEvent($request, $tokenStorage->getToken());
$eventDispatcher->dispatch($logoutEvent);
$tokenStorage->setToken(null);
$cookie = new Cookie(
"US", // Cookie name
"", // Cookie content
time()-1000, // Expiration date
"/", // Path
"your_app_domain.com", // Domain
$request->getScheme() === 'https', // Secure
false, // HttpOnly
true, // Raw
// 'Strict' // SameSite policy
);
$response = new RedirectResponse("/");
$response->headers->setCookie($cookie);
$response->setTargetUrl('https://auth-dev.mavryx.software/oauth2/signout');
return $response;
}
/**
* @Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils,Request $request,EntityManagerInterface $entityManager,GuardAuthenticatorHandler $guardHandler,Oauth2Authenticator $authenticator, MavryxApiService $mavryxApiService): Response
{
$userToken = $request->cookies->get('US');
if(!$userToken)
{
$settings = $this->getParameter("mavryx");
//GENERATTE STATE HERE AND SAVE
$state = md5(time());
$authenticationUri = $mavryxApiService->api()->OAuth2->getAuthorizationUrl([
'client_id' => $settings['credentials']['client_id'],
'response_type' => $settings['credentials']['response_type'],
'state' => $state,
'redirect_uri' => $settings['credentials']['redirect_uri'],
]);
return $this->redirect($authenticationUri);
}
$user = $mavryxApiService->api()->OAuth2->setToken($userToken)->user()->me()()->item();
$email = $user->client_id;
$user = $entityManager->getRepository(User::class)->findOneBy(['email' => $email]);
return $guardHandler->authenticateUserAndHandleSuccess(
$user,
$request,
$authenticator,
'main' // firewall name in security.yaml
);
}
}