Authorize Single-Page Application (SPA)

Introduction

In this tutorial you will create a Single-Page Application (SPA), learn how authorization code flow works with Proof Key for Code Exchange (PKCE), and how to get an access token with it. In the end we will double check if the token is truly valid by making a request to one of the iTwin Platform API endpoints.

Info

Skill level:

Basic

Duration:

10 minutes

1. Register a Single-Page Application (SPA)

What is a Single-Page Application (SPA)?

A Single-Page Application (SPA) is a web application or website that interacts with the user by dynamically rewriting the current web page with new data from the web server, instead of the default method of a web browser loading entire new pages.

Sign in

To be able to register an application you need to be signed in. If you are not already you can do that by clicking the Sign In button in the action pane or in the top right corner of the page.

Register a Single-Page Application (SPA)

You will need to register an application to use the iTwin Platform APIs. You can use the Register Application button to automatically create your first Single-Page Application (SPA). This will allow you to configure Authorization Code Flow for your Single-Page Application (SPA) and get the correct access token.

Once generated, you will be shown a few lines of code under the button:

  • client_id - This is the unique identifier for your application. It is displayed on the application details page as Client ID.
  • redirectUris - Specifies where users are redirected after they have chosen whether or not to authenticate your app. Displayed on the application details page as one of Redirect URIs.
  • scope - List of accesses granted to the application. Displayed on the application details page as Scopes.

For future use: Register and configure your application manually following the instructions in the Register and modify an Application tutorial.

2. Get an Access Token

What is an access token?

An Access Token is token which contains a string that can be used to make authenticated requests to an API to access protect resources (in this case iTwin Platform APIs).The string has no meaning to the application using it, but represents that the user has authorized the application to access their account. The token is bounded by an appropriate lifetime, scopes, and other information that the server may require.

2.1. Authorization Code Flow with Proof Key for Code Exchange (PKCE)

This flow provides a way to authorize with an Authorization Server by redirecting to the authorization server, getting a code back, and exchanging it for an access token in a subsequent call.

You can find more information here, but it is more technical.

Steps to get an access token using this flow:

  1. Generate code_verifier and code_challenge,
  2. Navigate to the authorization server,
  3. The authorization server will redirect back to your application (one of redirect URIs) with code query parameter,
  4. The application exchange code for an access_token via the Token endpoint.

2.1.1. Code Verifier and Code Challenge

PKCE (RFC 7636) is an extension to the Authorization Code flow to prevent CSRF and authorization code injection attacks.

code_verifier is a random string between 43 and 128 characters. It provides proof that you initiated the redirect to the authorization endpoint.

code_challenge: is a string that is generated from code_verifier and it is passed to the authorization endpoint as a query parameter. Pseudo code to generate code challenge:

base64urlEncode(SHA256(ASCII(code_verifier)));

Read more about: ASCII, SHA256, base64urlEncode

You can generate them by clicking Generate Code Verifier and Code Challenge. This is required for the following steps.

Generate Code Verifier and Code Challenge


2.1.2. Navigate To the Authorization Server

Application should make interactive redirect/navigate to authorization server. This allow user to authorize agents authorization server and give consent to allow access part of his data through your application. If you follow the interactive part you can click the Navigate button and it will open popup window that will hit authorization endpoint with required query parameters.

The URL will be built in this way:

Authorization endpoint: https://ims.bentley.com/connect/authorize

With query parameters:

  • client_id this is the unique identifier for your application. Displayed on the application details page as Client ID (e.g. spa-example)
  • redirect_uri Specify there you want go back after successful authorization (e.g. http://localhost:3000/redirect)
  • scope list of accesses granted to the application. Displayed on the application details page as Scopes. (e.g. users:read)
  • response_type For this flow code it is only option and that indicates, that you will get code as query parameter then user will come back from authorization server.
  • code_challenge Code challenge that was generated from code code_verifier
  • code_challenge_method Code challenge method type it should be S256

So in the end it should look something like this: https://ims.bentley.com/connect/authorize?client_id=CLIENT_ID&redirect_uri=http%3A%2F%2Flocalhost%3A3000&scope=users%3Aread&response_type=code&code_challenge=CODE_CHALLENGE&code_challenge_method=S256

Navigate to the Authorization Server


TEXT
https://ims.bentley.com/connect/authorize?client_id=CLIENT_ID&redirect_uri=http%3A%2F%2Flocalhost%3A3000&scope=users%3Aread&response_type=code&code_challenge=CODE_CHALLENGE&code_challenge_method=S256

2.1.3. Receive the code

The code will be placed inside the redirect URL query parameter (e.g. http://localhost:3000/redirect?code=random-string). The application should take care of code query parameter extraction. If you follow the interactive steps the code should appear in the action pane.


2.1.4. Receive an access token

You need to call the token endpoint with the Content-Type header equal to application/x-www-form-urlencoded.

Request body (with URL encoded characters):

  • client_id: Identification that is generated when the application is created. You can find it in the My Apps page. If you generated it during this tutorial you can find it in the first step.
  • grant_type: authorization_code indicates that the application will use a code to get an access_token.
  • code_verifier: It is for verification purposes that the same application that redirected to the authorization server is calling this endpoint.
  • code: Code that the application got after the redirect from the authorization server back to the application itself.
  • redirect_uri: redirect URI there was code send.

Code is short lived: If your code has expired please Navigate to the authorization server one more time.

Request to Token endpoint


HTTP
POST https://ims.bentley.com/connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

client_id=CLIENT_ID&code_verifier=CODE_VERIFIER&grant_type=authorization_code&code=CODE&redirect_uri=http%3A%2F%2Flocalhost%3A3000

Response from the Token endpoint

Response contains:

  • token_type: Bearer. This is part of the Authorization header that is constructed like Authorize: token_type access_token. You can read more here.
  • access_token: Access token itself (in JWT format) that will be passed in the Authorize header for API calls as Bearer JWT_TOKEN.
  • expires_in: Lifespan of the access token in seconds.

Response from the Token endpoint


JSON
{
  "access_token": "JWT_TOKEN",
  "token_type": "Bearer",
  "expires_in": 3599
}

3.1. Make the request to the /users/me endpoint

This request will show that the newly created access token is valid and how to use it. The /users/me endpoint will retrieve the logged in user’s profile information. If you follow the interactive part you can click the Send request button.

Request to /users/me endpoint


HTTP
GET https://api.bentley.com/users/me HTTP/1.1
Authorization: Bearer JWT_TOKEN
Accept: application/vnd.bentley.itwin-platform.v1+json

3.2. Response

This request will return user information (name, email, country and etc.).

Response from /users/me endpoint


JSON
{
  "user": {
    "displayName": "FirstName LastName",
    "givenName": "FirstName",
    "surname": "LastName",
    "email": "FirstName.LastName@domain.com",
    "alternateEmail": "FirstName.LastName@alternative.com",
    "city": "CityName",
    "country": "US",
    "language": "EN",
    "createdDateTime": "2021-08-27T10:19:07.2510000Z"
  }
}