- 05 Nov 2024
- 4 Minutes to read
- Print
- DarkLight
- PDF
Single Page Applications
- Updated on 05 Nov 2024
- 4 Minutes to read
- Print
- DarkLight
- PDF
Single-page apps (or browser-based apps) run entirely in the browser after loading the Javascript and HTML source code from a web page. As the entire source is available to the browser, they cannot maintain the confidentiality of a client secret. So the secret is not used for these apps. Instead, these applications must implement the Proof Key for Code Exchange (PKCE) extension recommended for public clients.
The Proof Key for Code Exchange (PKCE, pronounced pixie) extension describes a technique for public clients to mitigate the threat of having the authorization code intercepted. The technique involves the client first creating a secret, and then using that secret again when exchanging the authorization code for an access token. This way if the code is intercepted, it will not be useful since the token request relies on the initial secret.
Step 1: Authorization request
Before starting the authorization process, public applications create what is known as the code verifier. This is a cryptographically random string using the characters A-Z, a-z, 0-9, and the punctuation characters -._~ (hyphen, period, underscore, and tilde), between 43 and 128 characters long.
Once the app has generated the code verifier it uses that to create a code challenge. The code challenge is a BASE64-URL-encoded string of the SHA256 hash of the code verifier.
Now that the app has a code challenge, it includes that as parameter in the authorization request together with a parameter which indicates the method used to generate the challenge.
Name | Example Value | Required | Description |
---|---|---|---|
client_id | demoapp | yes | The Client ID set when application was registered. Identifies which app is making the request. |
response_type | code | yes | Determines whether the OAuth 2.0 endpoint returns an authorization code. Always set this to the code. |
scope | openid email profile offline_access visma_api:read The “visma_api:read” scope-name above is just an example - check your API documentation for scope(s) to use. | yes | Identifies the user information that your application is requesting. One scope openid is required. The values passed in this parameter inform the consent screen that is shown to the user. |
redirect_uri | https://demoapp.example.com/oauthcallback | yes | Determines where the OAuth Callback response is sent back to your application. The value of this parameter must exactly match the value added on application registration. This includes the https scheme, the same case. |
code_challenge | The code challenge generated | yes | Base64-URL-encoded string of SHA256 hash of the Code Verifier. |
code_challenge_method | S256 or plain | yes | Whether the challenge is the SHA256 hash of the string or the plain verifier string. |
state | This field can be a Base64 encoded JSON object that can hold multiple values | no | Authorization protocols provide a state parameter. During authentication, the application sends this parameter in the authorization request, and the Authorization Server will return this parameter unchanged in the response. Your application can use this parameter in order to make sure that the response belongs to a request that was initiated by the same user. Therefore, state helps mitigate CSRF attacks. Restore the previous state of your application. Read more |
response_mode | query | no | Specifies that the response parameters are returned to client in a query string. |
ui_locales | nb-NO sv-SE en-GB | no | End-User's preferred languages and scripts for the user interface, represented as a space-separated list of BCP47 [RFC5646] language tag values, ordered by preference. Values currently supported:
|
tenant_hint | 604ad704-772a-46f7-8926-571769f067e7 | no | Tenant ID (context) for Applications doing authorization of user-roles as part of Visma Connect. |
Examples:
GET https://connect.visma.com/connect/authorize?client_id=demoapp&response_type=code&response_mode=query&scope=openid+email+profile&redirect_uri=http://demoapp.example.com/oauthcallback&code_challenge=YOUR_CODE_CHALLENGE&code_challenge_method=S256&state=CfDJ8NbGuiMeKnBKlosjbaGWcBzxsyHJjSmlXdcP5HT0Jp_qH...tE8u1Ws&ui_locales=nb-NO+sv-SE+en-GB
Step 2: User consent
In this step, the user decides whether to grant your application the requested access or not. At this stage, Visma Connect displays a consent window with the name of your application, seeking permission to access with the user’s authorization credentials. The user can then consent or refuse to grant access to your application.
Your application doesn’t need to do anything at this stage as it waits for the response from Visma Connect Authentication server indicating whether the access was granted or not.
Step 3: Authentication Response
Since the response mode for SPA applications is query, the authorization server sends the following response back to your application:
https://demoapp.example.com/oauthcallback?code=94c99b73c13c1e39f7b0a7d259628338&state=CfDJ8NbGuiMeKnBKlosjbaGWcBzxsyHJjSmlXdcP5HT0Jp_qH...tE8u1Ws
Step 4: Exchange Authorization Code for Tokens
After the app receives the authorization response, it exchanges the code for an Access Token and ID Token. The token request must include the code verifier parameter that was generated by your app before starting the authorization process. Sample request:
curl --request POST --url https://connect.visma.com/connect/token --header 'content-type: application/x-www-form-urlencoded' --data 'grant_type=authorization_code&redirect_uri=https%3A%2F%2Fdemoapp.example.com/oauthcallback%2Fcallback&code=94c99b73c13c1e39f7b0a7d259628338&client_id=demoapp&code_verifier=YOUR_CODE_VERIFIER'
Name | Example Value | Required | Description |
---|---|---|---|
grant_type | authorization_code | yes | As defined in the OAuth 2.0 specification, this field must contain a value of authorization_code. |
redirect_uri | https://demoapp.example.com/oauthcallback | yes | The same value used on the authorization request. |
code | 94c99b73c13c1e39f7b0a7d259628338 | yes | The authentication code returned from the initial request. |
client_id | demoapp | yes | The Client ID set when application was registered. Identifies which app is making the request. |
code_verifier | The generated verifier | yes | The code verifier for the PKCE request, that the app originally generated before the authorization request. This should be between 43 and 128 characters in length. |
If successful, this call will return a neatly packaged token that will contain the following fields:
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjVENDc....7MTOBbdd5mgb2CHzxL0RFjs24pqC1pCeUqOjbg",
"id_token": "eyJhbG....dsaeKOJdIHS988MLKJdsaCeUqOjbg",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "openid email profile"
}
OAuth/OIDC library: Token verification and time on user device
If your OAuth/OIDC library use the local time of the device for Token-verification, then you rely on the end-users device clock to be accurate in order for the Token-verification to succeed.
SPA applications cannot have offline access support, therefore refresh tokens are never issued to these applications.
When Identity Scopes are used in the authentication request your application can retrieve additional information about the authenticated user from UserInfo Endpoint.