Mobile Application with PKCE (Proof Key for Code Exchange)

The Authorization Code flow is initiated by redirecting the user in the web browser to the cidaas /authz endpoint. cidaas will then display the cidaas login page, allowing the user to enter their credentials or alternatively sign in with any other configured Identity Provider.

After the user has authenticated, cidaas will redirect the browser back to the Redirect URI (also called Callback URL), passing along an authorization_code parameter in the query string of the Callback URL. This code can then be exchanged for an access_token by making a request to the /token-srv/token endpoint.

The access_token is a JSON Web Token (JWT) and contains various attributes - referred to as Claims - regarding the user, such as the user's name, email address, profile picture etc. The access_token can be decoded to extract the claims and you are free to use these inside of your application, to display a user's name and profile image for example.

  1. The Client initiates the flow and redirects the user to the Authorization Server.

  2. The user authenticates.

  3. The Authorization Server redirects to the redirect_uri with an authorization_code in the query string.

  4. The Client sends the authorization_code together with the redirect_uri and the Client Id/Client Secret to the Authorization Server.

  5. The Authorization Server validates this information and returns an access_token.

Adding extra layer support with PKCE

PKCE (RFC 7636) is a technique to secure public clients that don't use a client secret.

It is primarily used by native and mobile apps, but the technique can be applied to any public client as well. It requires additional support by the authorization server, so it is only supported on certain providers.

This specification adds additional parameters to the OAuth 2.0 Authorization and Access Token Requests, shown in abstract form in above Figure.

  • (A) The client creates and records a secret named the "code_verifier" and derives a transformed version "t(code_verifier)" (referred to as the "code_challenge"), which is sent in the OAuth 2.0 Authorization Request along with the transformation method "t_m".

  • (B) The Authorization Endpoint responds as usual but records "t(code_verifier)" and the transformation method.

  • (C) The client then sends the authorization code in the Access Token Request as usual but includes the "code_verifier" secret generated at (A).

  • (D) The authorization server transforms "code_verifier" and compares it to "t(code_verifier)" from (B). Access is denied if they are not equal.

An attacker who intercepts the authorization code at (B) is unable to redeem it for an access token, as they are not in possession of the "code_verifier" secret.

More info visit https://tools.ietf.org/html/rfc7636

Execute an Authorization Code Grant Flow

Register your App

The first thing you need to do is to create a new client in cidaas. A cidaas client maps to your application and allows it to use cidaas for authentication.

Navigate to the cidaas dashboard and click on the Apps menu option on the left. Create a new App by clicking on the Create App button.

The Create App page will open, allowing you to enter the name of your new application. Choose Webapplication app type and click save button.

1. Create a Code Verifier

var verifier = crypto.randomBytes(32).toString('base64') 
.replace(/\+/g, '-') 
.replace(/\//g, '_') 
.replace(/=/g, '');

2. Create Code Challenge

var code_challenge = crypto.createHash('sha256').update(verifier).digest();

3. Get the User's Authorization

To begin an Authorization Code flow, your web application should first send the user to the authorization URL with code_challenge :

https:///authz-srv/authz/?
response_type=code&
client_id=YOUR_CLIENT_D&
redirect_uri=https://your_app/callback&
state=YOUR_OPAQUE_VALUE&
code_challenge=erdf...

Request Parameters

The authz request will contain the following parameters.

response_type: The response type specifies the Grant Type you want to use. This can be either code or token. For mobile applications using the Implicit Grant Flow this must be set to token

client_id: The Client ID of the Client you registered in cidaas. This can be found on the Settings tab of your Client in the cidaas Dashboard.

state: The state parameter will be sent back should be used for XSRF and contextual information (like a return url).

redirect_uri: The URL to which cidaas will redirect the browser after authorization has been granted by the user. The Authorization Code will be available in the code URL parameter. This URL must be specified as a valid callback URL under your Client's Settings.

code_challenge: To reduce implementation complexity, salting is not used in the production of the code challenge, as the code verifier contains sufficient entropy to prevent brute-force attacks. Concatenating a publicly known value to a code verifier (containing 256 bits of entropy) and then hashing it with SHA256 to produce a code challenge would not increase the number of attempts necessary to brute force a valid value for code verifier.

For example:

https:///authz-srv/authz/?&response_type=code&client_id=your client_id&redirect_uri=https://your_app/callback&code_challenge=erdf...

4. Obtain access token by Authorization Code

After the user has authenticated, cidaas will call back to the URL specified in the redirecturi query string parameter which was passed to the /authz endpoint. When calling back to this URL, cidaas will pass along the code in the query string of the URL, e.g.

https://your\_app/callback?code=your_authorization_code....

You application will need to handle the request to this callback URL, extract the access_code from the code query string parameter and call the /token-srv/token endpoint of the cidaas Authentication API in order to exchange the access_code for the access_token:

curl --request POST \
--url 'https:///token-srv/token' \
--header 'content-type: application/json' \
--data '
{
"grant_type":"authorization_code",
"client_id": "YOUR_CLIENT_ID",
"code": "YOUR_AUTHORIZATION_CODE",
"redirect_uri": "https://your_app/callback",
"code_verifier":"xxxx"
}'

Request Parameters

The access_token request will contain the following parameters.

grant_type: This must be authorization_code.

client_id: Your application's Client ID.

code: The Authorization Code received from the initial authorize call.

redirect_uri: The URL must match exactly the redirect_uri passed to /authorize.

code_verifier: The security model relies on the fact that the code verifier is not learned or guessed by the attacker. It is vitally important to adhere to this principle. As such, the code verifier has to be created in such a manner that it is cryptographically random and has high entropy that it is not practical for the attacker to guess.

The response from /token-srv/token contains an access_token, refresh_token, id_token(only for openid scope), scope, expires_in and token_type values (and also potentially a refresh_token), for example:

{
"access_token": "eyJz93a...k4laUWw",
"refresh_token": "GEbRxBN...edjnXbL",
"id_token": "eyJ0XAi...4faeEoQ",
"token_type": "Bearer"
}

The access_token will be a JSON Web Token (JWT) containing information about the user. You can extract both of these values from the URL using basic string manipulation techniques in whatever programming language you are using.

As mentioned, the access_token is a JWT and you will need to decode this token in order to read the claims (i.e. attributes) of the user.

Once the JWT is decoded, you can extract the information about the user from the Payload of the access_token. This is a JSON structure and will contain the claims (attributes) about the user as well as some other metadata.

The access_token Payload

An example payload for an access_token may look something like this:

{
"sub": "2c68b74b-b8a3-4f2d-b8d5-b5c27d9db9c9",
"role": "USER,SITE_A_USER",
"auth_time": 1486898362350,
"iss": "http://issuerdomain",
"exp": 1486984762000,
"iat": 1486898362350,
"uuid": "c2fee8e2-0b87-49d6-8eac-fc110fb9368b",
"exp_in": 86400
}

The payload above contains the following claims:

Parameter Description
sub The unique identifier of the user. This is guaranteed to be unique per user and will be in the format (identity provider)(unique id in the provider), e.g. 2c68b74b-b8a3-4f2d-b8d5-b5c27d9db9c9.
role Comma separated string value , contains roles of the user.
auth_time Hold the value when the authorization happens.
iss The issuer. A case-sensitive string or URI that uniquely identifies the party that issued the JWT. For an cidaas issued access_token, this will be the URL of your cidaas tenant.
exp The expiration time. holds the milliseconds.
iat The issued at time. holds the milliseconds.
uuid Unique id for the token
exp_in The expires in seconds.

5. Call the API

Once the access_token has been obtained it can be used to make calls to the API by passing it as a Bearer Token in the Authorization header of the HTTP request:

curl --request GET \
--url https:///api \
--header 'authorization: Bearer ACCESS_TOKEN' \
--header 'content-type: application/json'

6. Verify the Token

When an API receives a request with a bearer access token, the first thing to do is to validate the token. This consists of a series of steps, and if any of these fails then the request must be rejected. for more information.

This document lists all the validations that your API should perform:

  1. Check that the JWT is well formed

  2. Check the signature

  3. Validate the standard claims

  4. Check the Client permissions (scopes)

Parse the JWT

First, the API needs to parse the JSON Web Token (JWT) to make sure it's well formed. If this fails the token is considered invalid and the request must be rejected.

A well formed JWT, consists of three strings separated by dots (.): the header, the payload and the signature. Typically it looks like the following:

The header and the payload are Base64Url encoded. The signature is created using these two, a secret and the hashing algorithm being used (as specified in the header: HMAC, SHA256 or RSA).

Check the Signature Algorithm

The API needs to check if the algorithm, as specified by the JWT header (property alg), matches the one expected by the API. If not, the token is considered invalid and the request must be rejected.

In this case the mismatch might be due to mistake (it is common that the tokens are signed using the HS256 signing algorithm, but your API is configured for RS256, or vice versa), but it could also be due to an attack, hence the request has to be rejected.

Validate the Claims

Once the API verifies the token's signature, the next step is to validate the standard claims of the token's payload. The following validations need to be made:

Token expiration: The current date/time must be before the expiration date/time listed in the exp claim (which is a Unix timestamp). If not, the request must be rejected.

Token issuer: The iss claim denotes the issuer of the JWT. The value must match the one configured in your API. For JWTs issued by cidaas, iss holds your cidaas domain with a https:// prefix and a / suffix: https://someAPI.com/

Check the Permissions

By now you have verified that the JWT is valid. The last step is to verify that the client has the permissions required to access the protected resources.

To do so, you need to check the scopes of the decoded JWT. This claim is part of the payload and it is a space-separated list of strings.

To check the permissions granted to the client, you need to check the contents of the scope.

For example, a user management API might provide three endpoints to read, create or delete a user record: /create, /read and /delete. We have configured this API, so each endpoint requires a specific permission (or scope):

The read:users scope provides access to the /read endpoint.

The create:users scope provides access to the /create endpoint.

The delete:users scope provides access to the /delete endpoint.

If a request requests to access the /create endpoint, but the scope claim does NOT include the value create:users, then the API should reject the request with 403 Forbidden.



results matching ""

    No results matching ""