Skip to main content
Version: 5.0

Refresh the Session

Overview

When a user logs in using Auth Connect the application receives an AuthResult that represents the authentication session. The AuthResult object provides access to several types of tokens:

  • ID Token: The ID token contains information pertaining to the identity of the authenticated user. The information within this token is typically consumed by the client application.
  • Access Token: The access token signifies that the user has properly authenticated. This token is typically sent to the application's backend APIs as a bearer token. The token is verified by the API to grant access to the protected resources exposed by the API. Since this token is used in communications with the backend API, a common security practice is to give it a very limited lifetime.
  • Refresh Token: Since access tokens typically have a short lifetime, longer lived refresh tokens are used to extend the length of a user's authentication session by allowing the access tokens to be refreshed.

The key take away is that OIDC authentication servers are typically set up to return a short lived access token along with an much longer lived refresh token. For example, the access token may expire after one hour with the refresh token expiring after five days. This allows the application to use the access token for a period of time and then refresh it after that time has elapsed. As such, the application needs to be able to detect that the access token has expired and request a refresh of the session.

We will build upon the application we created in the getting started tutorial in order to implement a token refresh workflow.

Let's Code

As mentioned previously, this tutorial builds upon the application created when doing the getting started tutorial. If you have the code from when you performed that tutorial, then you are good to go. If you need the code you can make a copy from our GitHub repository.

The refresh strategy used in this tutorial is:

  1. If a session exists, determine if the access token is expired.
  2. If the session token is expired it can be refreshed if a refresh token is available.
  3. If a refresh token is available, refresh the session and replace the existing stale session with the refreshed one. The user's authentication has been made valid again.
  4. If a refresh cannot be performed or the refresh fails, throw away the invalid stale session. The user is no longer authenticated.

Let's see how this looks in our AuthenticationService.

src/app/core/authentication.service.ts

_10
private async getAuthResult(): Promise<AuthResult | null> {
_10
return this.session.getSession();
_10
}

We currently have a getAuthResult() method that is a simple pass-through to the session service.

src/app/core/authentication.service.ts

_10
private async getAuthResult(): Promise<AuthResult | null> {
_10
return this.session.getSession();
_10
}
_10
_10
private async refreshAuthResult(authResult: AuthResult): Promise<AuthResult | null> {
_10
return await AuthConnect.refreshSession(this.provider, authResult);
_10
}

Add a refreshAuthResult() method. For now it can be a simple pass-through to AuthConnect.refreshSession().

src/app/core/authentication.service.ts

_15
private async getAuthResult(): Promise<AuthResult | null> {
_15
let authResult = await this.session.getSession();
_15
if (
_15
authResult &&
_15
(await AuthConnect.isAccessTokenAvailable(authResult)) &&
_15
(await AuthConnect.isAccessTokenExpired(authResult))
_15
) {
_15
authResult = await this.refreshAuthResult(authResult);
_15
}
_15
return authResult;
_15
}
_15
_15
private async refreshAuthResult(authResult: AuthResult): Promise<AuthResult | null> {
_15
return await AuthConnect.refreshSession(this.provider, authResult);
_15
}

If an AuthResult exists and contains an expired access token, then we need to attempt a refresh.

src/app/core/authentication.service.ts

_19
private async getAuthResult(): Promise<AuthResult | null> {
_19
let authResult = await this.session.getSession();
_19
if (
_19
authResult &&
_19
(await AuthConnect.isAccessTokenAvailable(authResult)) &&
_19
(await AuthConnect.isAccessTokenExpired(authResult))
_19
) {
_19
authResult = await this.refreshAuthResult(authResult);
_19
}
_19
return authResult;
_19
}
_19
_19
private async refreshAuthResult(authResult: AuthResult): Promise<AuthResult | null> {
_19
let newAuthResult: AuthResult | null = null;
_19
if (await AuthConnect.isRefreshTokenAvailable(authResult)) {
_19
newAuthResult = await AuthConnect.refreshSession(this.provider, authResult);
_19
}
_19
return newAuthResult;
_19
}

We should only refresh the session if a refresh token exists. Otherwise the refresh should return null signifying that we no longer have a valid session.

src/app/core/authentication.service.ts

_23
private async getAuthResult(): Promise<AuthResult | null> {
_23
let authResult = await this.session.getSession();
_23
if (
_23
authResult &&
_23
(await AuthConnect.isAccessTokenAvailable(authResult)) &&
_23
(await AuthConnect.isAccessTokenExpired(authResult))
_23
) {
_23
authResult = await this.refreshAuthResult(authResult);
_23
}
_23
return authResult;
_23
}
_23
_23
private async refreshAuthResult(authResult: AuthResult): Promise<AuthResult | null> {
_23
let newAuthResult: AuthResult | null = null;
_23
if (await AuthConnect.isRefreshTokenAvailable(authResult)) {
_23
try {
_23
newAuthResult = await AuthConnect.refreshSession(this.provider, authResult);
_23
} catch (err) {
_23
null;
_23
}
_23
}
_23
return newAuthResult;
_23
}

In rare instances, the refresh could fail. For example, if the refresh token has expired or is otherwise invalid. In such cases we should also return null signifying that we no longer have a valid session.

src/app/core/authentication.service.ts

_24
private async getAuthResult(): Promise<AuthResult | null> {
_24
let authResult = await this.session.getSession();
_24
if (
_24
authResult &&
_24
(await AuthConnect.isAccessTokenAvailable(authResult)) &&
_24
(await AuthConnect.isAccessTokenExpired(authResult))
_24
) {
_24
authResult = await this.refreshAuthResult(authResult);
_24
}
_24
return authResult;
_24
}
_24
_24
private async refreshAuthResult(authResult: AuthResult): Promise<AuthResult | null> {
_24
let newAuthResult: AuthResult | null = null;
_24
if (await AuthConnect.isRefreshTokenAvailable(authResult)) {
_24
try {
_24
newAuthResult = await AuthConnect.refreshSession(this.provider, authResult);
_24
} catch (err) {
_24
null;
_24
}
_24
}
_24
await this.saveAuthResult(newAuthResult);
_24
return newAuthResult;
_24
}

Save the refreshed value. If newAuthResult is null, indicating that the refresh failed, the session will be cleared.

We currently have a getAuthResult() method that is a simple pass-through to the session service.

Add a refreshAuthResult() method. For now it can be a simple pass-through to AuthConnect.refreshSession().

If an AuthResult exists and contains an expired access token, then we need to attempt a refresh.

We should only refresh the session if a refresh token exists. Otherwise the refresh should return null signifying that we no longer have a valid session.

In rare instances, the refresh could fail. For example, if the refresh token has expired or is otherwise invalid. In such cases we should also return null signifying that we no longer have a valid session.

Save the refreshed value. If newAuthResult is null, indicating that the refresh failed, the session will be cleared.

src/app/core/authentication.service.ts

_10
private async getAuthResult(): Promise<AuthResult | null> {
_10
return this.session.getSession();
_10
}

Next Steps

This code represents the basic flow needed for an Auth Connect session refresh. It can easily be expanded to support more advanced scenarios.

For example, let's say that rather than only refreshing expired sessions our application would like to also refresh sessions that are about to expire, let's say within the next five seconds. Rather than using AuthConnect.isAccessTokenExpired(), your application could use a combination of receivedAt and expiresIn to calculate when to access token is due to expire and use the current time to detect if the access token has either expired or is about to expire shortly. The code can then preemptively perform the refresh in those cases.

We suggesting starting with the flow documented here and modifying it as needed to fit your requirements.

Happy coding!! 🤓