In the previous part of this blog post series, we looked at how the authorization code grant flow with PKCE works and why your single page applications should consider moving to this authentication flow instead of the implicit grant flow.
For a quick recap, look at the image below to understand the token flow with PKCE.
Now, Let’s look at a sample implementation of this using a vanilla JavaScript single page app to access the Microsoft Graph API.
Azure AD App Registration
First, register a new client application in Azure AD. The registration steps slightly differs when compared to the registration for the implicit flow. This will enable CORS on the token endpoint.
-
In the Azure portal, under App Registration click on New Registration
-
Enter the display name of the app and for this demo we will choose the ‘Supported Account Types’ as single tenant.
-
Under Redirect Uri, select Single Page Application and enter the redirect URI as http://localhost:3000/ . Click on Register.
After the application is registered, you can navigate to the Authentication section and verify that your app is setup for Auth flow with PKCE.
Code for our single page application
Download or clone the sample code from here. In the authConfig.js file update the following
ClientId : (the application id of the app that you registered)
Authority : https://login.microsoftonline.com/<tenant id>
redirectUri : http://localhost:3000
Run npm install and then npm start. Navigate to the url http://localhost:3000
The logic on how the code works is already explained well in MS docs here.The goal is to understand the token flow as shown above. For this let’s look at the network trace.
After you have clicked on the sign in button and entered your credentials, the network trace looks like this (follow this flow as per the token flow image above).
1. GET request to the authorize endpoint with the code challenge generated ( by encoding the code verifier with sha256)
GET https://login.microsoftonline.com/<tenantid>/oauth2/v2.0/authorize?client_id=ae84022f****b036cf900e56&scope=User.Read+openid+profile&redirect_uri=http%3a%2f%2flocalhost%3a3000&client-request-id=e5fa252c-92d6-40cf-bdba-02637f0c0c62&response_mode=fragment&response_type=code&x-client-SKU=msal.js.browser&x-client-VER=2.0.1&x-client-OS=&x-client-CPU=&client_info=1&code_challenge=ryL0VvB5OpFiK7lZUmLRPgKBJj3dKThmZ-Ptqx_P3rI&code_challenge_method=S256&nonce=699b9232***da2a9af&state=eyJp*wIn19&sso_nonce=AQABAAAAAAAGV_bv21o***hKLVenWWxkG7SAA&mscrid=e5fa252c-92d6-40cf-bdba-02637f0c0c62
2. The server responds with the authorization code at the redirect URI
http://localhost:3000/#code=OAQABAAIAAAAGV_bv21oQQ4ROqh0_1-tA0Nx7f8_******_ZiKuzvGna0N8TPZjESAA\u0026client_info=eyJ1aW***mNkYzcwIn0\u0026state=eyJpZCI6Im**SI6InBvcHVwIn19\u0026session_state=b4567b14-8caf-4ed2-bb32-883df01a4f46
3. A post request is made to the token endpoint with the code verifier and the authorization code
POST https://login.microsoftonline.com/<tenantid>/oauth2/v2.0/token HTTP/1.1
client_id=ae840**00e56&redirect_uri=http%3A%2F%2Flocalhost%3A3000&scope=User.Read%20openid%20profile&code=OAQABAAIAAAAGV_bv21oQQ4ROqh0_1-tA0Nx7f8_****_ZiKuzvGna0N8TPZjESAA&code_verifier=IACCpwpfMQS4jEDMQ9PTpn_VQDc93ivCxIgQZg3UnhQ&grant_type=authorization_code&client_info=1&client-request-id=e5fa252c-92d6-40cf-bdba-02637f0c0c62
4. The authorization server responds with an access token and a refresh token. The access token expires in one hour while the refresh token will expire in 24 hours.
{"token_type":"Bearer","scope":"Mail.Read openid profile User.Read email","expires_in":3599,"ext_expires_in":3599,"access_token":"eyJ0eXAiOiJKV1QiLCJub******_uFGqLu0r54POY6Yy0fgBhAORdIa5GmVFEC8L_NsluPhzQXfY_hOm3wo-ufM4g","refresh_token":"OAQABAAAAAAAGV_bv21oQQ4ROqh0_1***gkslqrXfb5jJwoIAA","id_token":"eyJ0eXAiOiJKV1QiLCJ****_Ub9oPrecG4Zw","client_info":"eyJ1aWQiO****0YmNkYzcwIn0"}
5. We can now use the access token to send requests to our graph endpoint. Hit the “Read Mails” button.
6. The graph API responds with the user’s emails.
What would happen after the access token expires? Keep the browser idle for an hour (see the expires in attribute in the response – 3599 seconds). After that, click on read emails again and observe the network trace. (do it within 24 hours though). After the access token expires, a request for new access token is made to the token endpoint by passing the refresh token this is called Refresh Token Rotation
7. A post request is made to the token endpoint with the refresh token
POST https://login.microsoftonline.com/<tenantid>/oauth2/v2.0/token HTTP/1.1
client_id=ae84022f-***cf900e56&scope=User.Read%20Mail.Read&grant_type=refresh_token&client_info=1&client-request-id=658a3078-c2dd-4a48-b8ec-b744dab611db&refresh_token=OAQABAAAAAAAGV_bv21oQQ4ROqh0_1****gkslqrXfb5jJwoIAA
8. The response is a new access token and a new refresh token. This also invalidates the old refresh token.
{"token_type":"Bearer","scope":"Mail.Read openid profile User.Read email","expires_in":3599,"ext_expires_in":3599,"access_token":"eyJ0eXAiOiJKV1QiLCJub25jZSI6InAzMXdr****2g5veIHADWAhS2MbBa94vWJPQ","refresh_token":"OAQABAAAAAAAGV_bv21oQQ4ROqh0_1****_4AAiVWbx8WY_Hyyqx_N4LAgAA","id_token":"eyJ0eXAiOiJKV1QiLCJ*******J6aOGcY4nP0D1mGQg","client_info":"eyJ1aWQ***mNkYzcwIn0"}
9. You can now use the new access token to get data from the graph API
You can try and keep your browser idle for 24 hours and then click on “Read emails” again. Observe the trace. An attempt will be made for new refresh token using the old one just like above but this time, it fails with the error below. This is because refresh tokens have a lifetime of 24 hours.
The app then falls back to acquiring the token interactively by logging in again (start from step 1)
{"error":"invalid_grant","error_description":"AADSTS700081: The refresh token has expired due to maximum lifetime. The token was issued on 2020-08-25T06:40:25.3849613+00:00 and the maximum allowed lifetime for this application is 1.00:00:00.\r\nTrace ID: a6d8b83e-5892-4e74-ae92-b2188511fe01\r\nCorrelation ID: ae0eb927-1744-4bbd-9686-aa3610f3b7c2\r\nTimestamp: 2020-08-26 11:38:33Z","error_codes":[700081],"timestamp":"2020-08-26 11:38:33Z","trace_id":"a6d8b83e-5892-4e74-ae92-b2188511fe01","correlation_id":"ae0eb927-1744-4bbd-9686-aa3610f3b7c2","error_uri":"https://login.microsoftonline.com/error?code=700081","suberror":"bad_token"}
If you need to know how to implement this in your SharePoint SPFx solutions then have a look at this blog post.
If you use single page applications either in SharePoint online or Microsoft Teams or standalone single page apps, you will most likely require remediation of such applications to transition to the new model. Infotechtion can help you with your transition through our technical experts and application modernization approach. Read more about our approach to modernize SharePoint online or contact us for further discussion.