A straightforward implementation of OpenID authentication with plain PHP and AWS Cognito

PLEASE SHARE

This article helps developers understand how OpenID Connect authentication works with the help of a single PHP page. We are consuming AWS Cognito service for this demo.

We start by setting up AWS Cognito and PHP OpenID Client Application. At the end of this article, we explain how the flow actually works.

PREREQUISITES

  1. Familiar with how OpenID Connect protocol works. (If you are a beginner, simply search OpenID in google or youtube you will have tons of tutorials)
  2. Access to the AWS console (https://console.aws.amazon.com/)
  3. A PHP web server such as XAMPP (I am using XAMPP with PHP version 7.4.18)
  4. Composer tool (I am using composer version 2.1.3)

Creating Amazon Cognito User Pool

A Cognito user pool manages user data such as username, password, email, phone number, etc. It allows users to sign in to your web or mobile application through Hosted UI. Here are the instructions for creating a user pool.

  1. Login into AWS console (https://console.aws.amazon.com/)
  2. Go to the Services menu and click on the Cognito link under the “Security, Identity, & Compliance” section.
  3. Click on the “Manage User Pools” button.
  4. Click on the “Create a user pool” button.
  5. Enter a pool name (Ex: DEMO_USER_POOL)
  6. Click on the “Review defaults” button.
  7. Click on the “Edit” button for Required attributes as highlighted below,

8. Make sure email & phone number attributes are selected as shown below,

9. Click on the “Next step” button.

10. Click on the “Review” from the left side of the screen and click on the “Create pool” button.

11. A message will be displayed as “Your user pool was created successfully”. 

Configuring Domain Name

  1. Login into AWS Console and open the user pool created above (Ex: DEMO_USER_POOL)
  2. Click on the “App integration” => “Domain name” link from the left side menu.
  3. Enter a domain prefix (Ex: demo-hosted-UI)
  4. Make sure to check the availability of the domain by clicking on the “Check availability” button.
  5. Click on the “Save Changes” button.

Note: Hereafter, this domain name will be referred to as <AWS_COGNITO_DOMAIN> in this article.

Creating Client ID and Secret

  1. Login into AWS Console and open the user pool created above (Ex: DEMO_USER_POOL)
  2. Click on the “General Settings” => “App clients” from the left side navigation menu.
  3. Click on the “Add another app client” link.
  4. Enter an “App client name”. For Example, “samplephpclient”.
  5. Make sure the “Generate client secret” checkbox is selected
  6. Make sure the “ALLOW_USER_PASSWORD_AUTH” checkbox is selected.
  7. Leave the other default options and click on the “Create app client” button.
  8. Note down the “App client id” generated(Hereafter this value will be referred to as “<APP_CLIENT_ID>” in this article)
  9. Note down the “App client secret” generated(Hereafter this value will be referred to as “<APP_CLIENT_SECRET>” in this article)

Configuring App Client Settings

  1. Login into AWS Console and open the user pool created above (Ex: DEMO_USER_POOL)
  2. Click on the “App integration” => “App client settings” link from the left side menu.
  3. Edit the configuration for the App Client created above. (For example, samplephpclient)
  4. Enter the “Callback URL” as “http://localhost/aws_cognito_openid_auth_demo/index.php?has_authorization_code=true” (We will be installing this demo PHP application in a section down below)
  5. Enter the “Sign out URL” as “http://localhost/aws_cognito_openid_auth_demo/index.php?logout=true”
  6. Select the “Authorization code grant” checkbox under “Allowed OAuth Flows”.
  7. Select the “phone”, “email”, “openid”, “aws.cognito.signin.user.admin” and “profile” checkboxes under “Allowed OAuth Scopes”.
  8. Refer to the screenshot below,

9. Click on the “Save changes” button.

Adding Users Into Cognito User Pool

  1. Here’s the simple way to add users into the Cognito user pool using UI.
  2. Login into AWS Console and open the user pool created above (Ex: DEMO_USER_POOL)
  3. Click on the “General settings” => “Users and groups” from the left side menu.
  4. Click on the “Create user” button.
  5. Enter the details as per the below screenshot and click on “Create user”.

Note: This username & password will be used to login into the PHP application.

DEVELOPING THE OpenID Client PHP application

  1. Create a folder “aws_cognito_openid_auth_demo” under “xampp\htdocs” directory.

2. Change directory to “aws_cognito_openid_auth_demo”.

3. Create the file “composer.json” with the following content,

{
    "require": {
        "guzzlehttp/guzzle": "^7.2"
    }
}

4. Execute the command “composer update”.

5. Create “index.php” file with following code,

<?php
include __DIR__ . '/vendor/autoload.php';
use GuzzleHttp\Client;

session_start();
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <title>OpenID Client Application</title>
    

</head>
<body>
<h2>OpenID Client Application</h2>
<?php
//Specify the AWS Cognito Domain Name (Ex: https://rajesh.auth.ap-south-1.amazoncognito.com)
$cognito_domain = '<AWS_COGNITO_DOMAIN>';
//Specify the App Client ID (Ex: 4ctesq6erlgo5g45kjq3bmt94d)
$client_id = "<APP_CLIENT_ID>";
//Specify the App Client Secret (Ex: 1tgog47jvd8qd8qv9rnd5mrusg3236p05f64te0esno02qvo72q8)
$client_secret = "<APP_CLIENT_SECRET>";

//URL of the web server hosting OpenID PHP Client application
$openid_client_domain = 'http://localhost';

$response_type = "code";
//The URL for which authorization code will be returned by Authorization Endpoint
$redirect_uri = $openid_client_domain . '/aws_cognito_openid_auth_demo/index.php?has_authorization_code=true';
$scope = "openid email profile phone aws.cognito.signin.user.admin";
$state = "123456";

//AWS Cognito Authorization Endpoint URL
$authorization_endpint_url = $cognito_domain . '/oauth2/authorize?' .
 'client_id=' . $client_id . 
 '&response_type=' . $response_type . 
 '&scope=' . $scope . 
 '&state=' . $state . 
 '&redirect_uri=' . $redirect_uri;

//User is redirected to this URL once AWS Cognito logout the user
$logout_url = $cognito_domain . '/logout?' . 'client_id=' .
 $client_id . '&logout_uri=' .
 $openid_client_domain . '/aws_cognito_openid_auth_demo/index.php?logout=true';

//Check if authorization endpoint is sending the authorization code
if (isset($_GET['has_authorization_code']) && $_GET['has_authorization_code'] == 'true')
{
    $client = new \GuzzleHttp\Client(['verify' => false]);
    $authorization_code = $_REQUEST['code'];
	
	//Get access token from token endpoint
	//this communication happens directly between servers, 
	//not through the browser since we are sending the client_secret
    $accessTokenResponse = $client->request('POST', $cognito_domain . '/oauth2/token', ['form_params' => 
	[
		'client_id' => $client_id,
		'client_secret' => $client_secret,
		'grant_type' => 'authorization_code',
		'code' => $authorization_code,
		'redirect_uri' => $redirect_uri, ]
	]);

    $accessToenResponseBody = $accessTokenResponse->getBody(true);
    $responseArr = json_decode($accessToenResponseBody, true);
    $accessToken = $responseArr['access_token'];
	//Store the access_token in PHP Session
    $_SESSION['access_token'] = $accessToken;

}

//Check if Cognito is redirecting to logout URL.
if (isset($_GET['logout']) && $_GET['logout'] == 'true')
{
	//Remove the access_token from PHP Session
    $_SESSION['access_token'] = '';
	//Invalidate the user Session
    session_unset();
    session_destroy();

}
//Check if access_token is available.otherwise display the Login link
if (isset($_SESSION['access_token']) && $_SESSION['access_token'] <> '')
{
	//Fetch user profile info by sending access_token to the User Info endpoint
	$userInfo = $client->request('POST', $cognito_domain . '/oauth2/userInfo', ['headers' => 
	[
		'Authorization' => 'Bearer ' . $_SESSION['access_token'],
	]]);

    $APIResponseBody = $userInfo->getBody(true);
	$APIResponseArr = json_decode($APIResponseBody, true);

?>
		<BR>
		<h4>USER INFO : </h4>
		<ul>
			<li>username: <?php echo $APIResponseArr['username']; ?></li>
			<li>email: <?php echo $APIResponseArr['email']; ?></li>
			<li>phone_number: <?php echo $APIResponseArr['phone_number']; ?></li>
		</ul>
		
		<!--
			By clicking on this link a logout request will be sent to AWS Cognito
		-->
        <BR><a href="<?php echo $logout_url; ?>">Logout</a>

<?php
}
else
{
?>
        <!-- 
		By clicking on this link a request is sent to Cognito Authorization endpoint.
		We won’t be sending the secret to the authorization endpoint as 
		it’s not secure to send secret via browser
		-->
		<BR><a href="<?php echo $authorization_endpint_url; ?>">Login With UPSSO</a>
		
<?php
}
?>
    
</body>
</html>

Testing the PHP OpenID Authentication Client

  1. Request the PHP page from the browser (URL = http://localhost/aws_cognito_openid_auth_demo/index.php)

2. Click on the “Login With UPSSO” link. This will send a request for an authorization code to the Cognito Authorization Endpoint.

3. AWS Cognito will force the user to sign in. Enter credentials and click on the “Sign in” button.

4. Upon successful authentication, the Cognito Authorization endpoint will return an authorization code to PHP by Redirect URI.

5. Upon receiving the authorization code, PHP will further request the Token endpoint and get Access Token. This token will be stored in the user session for further use.

6. Further, PHP will send the Access Token to the User Info endpoint and retrieve the user profile data.

7. Now, the user is logged in.

8. Click on the “Logout” link. This will send a request to log out endpoint of Cognito. Then, Cognito will sign out the user and redirect the user to the Logout URI configured.

9. Upon receiving the logout request, PHP will remove the Access Token from the session and invalidate the user session

How it works

The following diagram will help you understand authentication flow.

1. User clicks on the “Login using UPSSO” link. Next, the Cognito authorization endpoint(/oauth2/authorize) is called with client_id, response_type, scope, state and redirect_uri(Note: We won’t be sending the secret to the authorization endpoint as it’s not secure to send secret via browser)

2. Authorization endpoint is returning the “Code” to Redirect URL (/aws_cognito_openid_auth_demo/index.php?has_authorization_code=true). This code, along with client_id & client_secret, is used to fetch the access token.

3. Upon receiving the authorization code, the PHP script sends a request to Access token endpoint with authorization code(got from step2), client_id, client_secret, grant_type and redirect_uri. (Note: this communication happens directly between servers, not through the browser since we are sending the client_secret)

4. The access token received above is saved in the PHP session.

5. Upon receiving the access token, the PHP makes a call to the User Info endpoint with “access_token” (got from step 4)

6. User Info endpoint returns the user profile data such as email, phone and other user-specific data. By completing this step, the user is logged in to the application. The user info and a “Logout” link are displayed on the browser.

7. The user clicks on the “Logout” link. Next, the Logout endpoint is called with client_id and logout URL.

8. Upon receiving the logout request, the Logout endpoint terminates the user session and redirect to the logout URL. First, the PHP script receives the logout request. Next, the “access_token” is removed from the PHP session, invalidating the session.

Source Code

The source code of this demo project can be pulled from this GitHub link,

https://github.com/rajeshkumarraj82/aws_cognito_openid_auth_demo

Follow Me

Leave a Reply

Your email address will not be published. Required fields are marked *