Authentication - Single Page App
A high-level overview of how authentication works with a single page app.
Login
Steps:
- The user clicks the Login button on your website or application.
- The user is sent to the Login Route implemented in your SPA.
- The SPA creates an Authorization Request and redirects the user to the Wristband Authorize Endpoint.
- Wristband validates and records the Authorization Request and redirects the user to the Wristband-hosted Login page.
- The user provides their credentials to authenticate.
- The Login Page redirects to your SPA's Callback Route with an authorization code.
- The Callback function calls Wristband's Token Endpoint to exchange the authorization code for an access token.
- The Callback function stores the access token and refresh token locally in the browser.
- The user is redirected to your SPA's entry point.
Logout
Steps:
- The user clicks the Logout button within your SPA.
- The SPA invokes a
logout()
function, destroying the session and tokens stored locally in the browser. - The SPA calls the Revoke Token Endpoint to revoke the refresh token.
- The user is redirected to the Wristband Logout Endpoint.
- Wristband destroys the authentication session and redirects the user to your SPA's Login route.
Refreshing Access Tokens
There are a couple of approaches your single-page application can take to handle refreshing tokens:
Approach | How it works | Advantages | Limitations |
---|---|---|---|
Refreshing on API Requests | The SPA checks if the access token is near expiration before API calls, refreshing it if needed before proceeding with the request. | Minimizes unnecessary refreshes and is simple to implement. | The first API request after a period of inactivity may lag due to the token being refreshed. Need to implement locking to ensure concurrent API requests don't trigger token refreshes simultaneously. |
Using a background timer to trigger token refreshes | The SPA uses a timer to schedule a token refresh before the access token expires. | Mitigates the extra latency incurred by performing a token refresh on API calls. | Adds unnecessary refreshes during inactivity. Timers add complexity to track token expiry. |
Best of Both Worlds
You can also combine both strategies to get the best of both worlds. Combining both strategies ensures tokens are refreshed during idle periods while outgoing checks handle edge cases like missed timers or unexpected expiration.
For many SPAs, only refreshing during outgoing requests is sufficient and simplifies the implementation. However, using a timer to refresh tokens in the background becomes valuable for applications that want to ensure latency isn't affected by token refreshes during outbound API calls. The choice ultimately depends on your application’s needs.
Below are the steps that an application should take to handle refreshing tokens before an API request:
- Before the SPA calls a protected API, it first checks whether the access token has expired using the
expiresIn
value received from Wristband's Token Endpoint. - If the access token has expired or is near expiration, the SPA makes a request to Wristband's Token Endpoint to refresh the access token. The request includes the refresh token in the request body.
- Wristband verifies the refresh token's validity. If valid, it generates a new access token and includes it in the response to the backend server. Assuming that refresh token rotation is enabled for the SPA, Wristband will also send back a new refresh token as part of the same response.
- The SPA then stores the new access token and refresh token locally.
- The SPA retries the original API request using the refreshed access token in the Authorization header (e.g.,
Authorization: Bearer <access_token>
).
Access Token Expiry Monitoring
The SPA can optionally track token validity using the newest
expiresIn
value and refresh proactively by setting a timer to refresh the token independently of other API requests just before expiration.
Additional Considerations
Access tokens should be refreshed slightly before their expiration time is reached in order to prevent situations where the access token expires while being processed by downstream API servers, resulting in a 401 being returned to the app. While the app should be able to recover from the 401 by refreshing the access token and retrying the request, it results in extra latency due to having to repeat the same request.
Refresh Token Rotation
Wristband enables refresh token rotation by default for SPAs to enhance security. This mechanism is critical for SPAs, as tokens can be exfiltrated from the browser by malicious javascript code injected through XSS. With refresh token rotation enabled, a new refresh token is generated every time an access token is refreshed. Old refresh tokens become invalid after they are used, reducing the time window that an attacker could use a compromised refresh token. Furthermore, an attempt to refresh an access token with an old refresh token will cause all refresh tokens generated from the same family to be revoked. So, if an attacker had a valid refresh token and the legitimate user attempted to use their refresh token (which is now invalid), it would result in the attacker's refresh token being revoked, preventing them from using it.
When users log in across multiple browser tabs, ensure that all instances consistently store and use the most up-to-date refresh token. Since refresh token rotation invalidates old tokens after use, a mismatch can occur if one tab attempts to use an outdated token, potentially causing refresh failures. To prevent this, use a shared storage mechanism, such as localStorage, to synchronize tokens across tabs. Furthermore, to prevent race conditions from occurring where multiple tabs attempt to update the refresh token concurrently, Web Locks can be used to synchronize the token refresh calls.
Handling Refresh Failures
A failed token refresh in SPAs indicates that the refresh token could not be used to obtain a new access token from Wristband. This can occur due to several reasons:
- The refresh token’s expiration time has been exceeded.
- The refresh token has been revoked.
- The refresh token has been tampered with and is no longer valid.
- An invalid request, such as incorrect client credentials.
- Network issues or server errors (e.g., 500 Internal Server Error).
Retrying Refresh Requests
For transient issues like network or server errors, it is advised to retry the refresh request to minimize disruption for the end user. We recommend implementing retry logic with a fixed or exponential backoff.
When the refresh token operation fails, the SPA can handle it in various ways based on your UX and security requirements.
- Redirect to Login Without Destroying the Wristband Auth Session: Send the user to the Wristband login page without calling the Wristband logout endpoint. If the user still has a valid Wristband auth session, they will automatically be re-authenticated without having to enter their credentials.
- Full Logout and Re-authentication: Clean up any local session data from the browser, then redirect the user to Wristband’s Logout Endpoint to terminate the Wristband auth session. This will force the user to re-authenticate the next time they try to access your application.
Updated 2 days ago