Merge branch 'main' into bugfix/content-type-design-editor-fixes-20240416

This commit is contained in:
Niels Lyngsø
2024-04-16 13:34:27 +02:00
committed by GitHub
2 changed files with 62 additions and 40 deletions

View File

@@ -56,7 +56,7 @@ export interface TokenErrorJson {
}
// constants
const AUTH_EXPIRY_BUFFER = 10 * 60 * -1; // 10 mins in seconds
const AUTH_EXPIRY_BUFFER = 0; // 0 seconds buffer
/**
* Returns the instant of time in seconds.

View File

@@ -94,8 +94,7 @@ export class UmbAuthFlow {
readonly #scope: string;
// tokens
#refreshToken: string | undefined;
#accessTokenResponse: TokenResponse | undefined;
#tokenResponse?: TokenResponse;
constructor(
openIdConnectUrl: string,
@@ -141,7 +140,7 @@ export class UmbAuthFlow {
codeVerifier = request.internal.code_verifier;
}
await this.#makeRefreshTokenRequest(response.code, codeVerifier);
await this.#makeTokenRequest(response.code, codeVerifier);
await this.performWithFreshTokens();
await this.#saveTokenState();
@@ -173,8 +172,9 @@ export class UmbAuthFlow {
if (tokenResponseJson) {
const response = new TokenResponse(JSON.parse(tokenResponseJson));
if (response.isValid()) {
this.#accessTokenResponse = response;
this.#refreshToken = this.#accessTokenResponse.refreshToken;
this.#tokenResponse = response;
} else {
this.signOut();
}
}
}
@@ -233,7 +233,7 @@ export class UmbAuthFlow {
* @returns true if the user is logged in, false otherwise.
*/
isAuthorized(): boolean {
return !!this.#accessTokenResponse && this.#accessTokenResponse.isValid();
return !!this.#tokenResponse && this.#tokenResponse.isValid();
}
/**
@@ -243,8 +243,7 @@ export class UmbAuthFlow {
await this.#storageBackend.removeItem(UMB_STORAGE_TOKEN_RESPONSE_NAME);
// clear the internal state
this.#accessTokenResponse = undefined;
this.#refreshToken = undefined;
this.#tokenResponse = undefined;
}
/**
@@ -254,25 +253,27 @@ export class UmbAuthFlow {
const signOutPromises: Promise<unknown>[] = [];
// revoke the access token if it exists
if (this.#accessTokenResponse) {
if (this.#tokenResponse) {
const tokenRevokeRequest = new RevokeTokenRequest({
token: this.#accessTokenResponse.accessToken,
token: this.#tokenResponse.accessToken,
client_id: this.#clientId,
token_type_hint: 'access_token',
});
signOutPromises.push(this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest));
}
// revoke the refresh token if it exists
if (this.#refreshToken) {
const tokenRevokeRequest = new RevokeTokenRequest({
token: this.#refreshToken,
client_id: this.#clientId,
token_type_hint: 'refresh_token',
});
// revoke the refresh token if it exists
if (this.#tokenResponse.refreshToken) {
const refreshTokenRevokeRequest = new RevokeTokenRequest({
token: this.#tokenResponse.refreshToken,
client_id: this.#clientId,
token_type_hint: 'refresh_token',
});
signOutPromises.push(this.#tokenHandler.performRevokeTokenRequest(this.#configuration, tokenRevokeRequest));
signOutPromises.push(
this.#tokenHandler.performRevokeTokenRequest(this.#configuration, refreshTokenRevokeRequest),
);
}
}
// clear the internal token state
@@ -286,7 +287,16 @@ export class UmbAuthFlow {
// which will redirect the user back to the client
// and the client will then try and log in again (if the user is not logged in)
// which will redirect the user to the login page
location.href = `${this.#configuration.endSessionEndpoint}?post_logout_redirect_uri=${this.#postLogoutRedirectUri}`;
const postLogoutRedirectUri = new URL(this.#postLogoutRedirectUri, window.origin);
const endSessionEndpoint = this.#configuration.endSessionEndpoint;
if (!endSessionEndpoint) {
location.href = postLogoutRedirectUri.href;
return;
}
const postLogoutLocation = new URL(endSessionEndpoint, this.#redirectUri);
postLogoutLocation.searchParams.set('post_logout_redirect_uri', postLogoutRedirectUri.href);
location.href = postLogoutLocation.href;
}
/**
@@ -296,14 +306,15 @@ export class UmbAuthFlow {
* @returns The access token for the user.
*/
async performWithFreshTokens(): Promise<string> {
if (!this.#refreshToken) {
console.log('Missing refreshToken.');
return Promise.resolve('Missing refreshToken.');
// if the access token is valid, return it
if (this.#tokenResponse?.isValid()) {
return Promise.resolve(this.#tokenResponse.accessToken);
}
if (this.#accessTokenResponse && this.#accessTokenResponse.isValid()) {
// do nothing
return Promise.resolve(this.#accessTokenResponse.accessToken);
// if the refresh token is not set (maybe the provider doesn't support them), sign out
if (!this.#tokenResponse?.refreshToken) {
this.signOut();
return Promise.reject('Missing refreshToken.');
}
const request = new TokenRequest({
@@ -311,31 +322,30 @@ export class UmbAuthFlow {
redirect_uri: this.#redirectUri,
grant_type: GRANT_TYPE_REFRESH_TOKEN,
code: undefined,
refresh_token: this.#refreshToken,
refresh_token: this.#tokenResponse.refreshToken,
extras: undefined,
});
const response = await this.#tokenHandler.performTokenRequest(this.#configuration, request);
this.#accessTokenResponse = response;
return response.accessToken;
await this.#performTokenRequest(request);
return this.#tokenResponse
? Promise.resolve(this.#tokenResponse.accessToken)
: Promise.reject('Missing accessToken.');
}
/**
* Save the current token response to local storage.
*/
async #saveTokenState() {
if (this.#accessTokenResponse) {
await this.#storageBackend.setItem(
UMB_STORAGE_TOKEN_RESPONSE_NAME,
JSON.stringify(this.#accessTokenResponse.toJson()),
);
if (this.#tokenResponse) {
await this.#storageBackend.setItem(UMB_STORAGE_TOKEN_RESPONSE_NAME, JSON.stringify(this.#tokenResponse.toJson()));
}
}
/**
* This method will make a token request to the server using the authorization code.
*/
async #makeRefreshTokenRequest(code: string, codeVerifier: string | undefined): Promise<void> {
async #makeTokenRequest(code: string, codeVerifier: string | undefined): Promise<void> {
const extras: StringMap = {};
if (codeVerifier) {
@@ -352,8 +362,20 @@ export class UmbAuthFlow {
extras: extras,
});
const response = await this.#tokenHandler.performTokenRequest(this.#configuration, request);
this.#refreshToken = response.refreshToken;
this.#accessTokenResponse = response;
await this.#performTokenRequest(request);
}
/**
* This method will make a token request to the server using the refresh token.
* If the request fails, it will sign the user out (clear the token state).
*/
async #performTokenRequest(request: TokenRequest): Promise<void> {
try {
this.#tokenResponse = await this.#tokenHandler.performTokenRequest(this.#configuration, request);
} catch (error) {
// If the token request fails, it means the refresh token is invalid, so we sign the user out.
console.error('Token request error', error);
this.signOut();
}
}
}