Persist API key creds for token refresh. (#414)

* Persist API key creds for token refresh.

* Linter fixes
This commit is contained in:
Matt Gibson 2021-06-21 18:48:06 -04:00 committed by GitHub
parent 5e24a70a87
commit 78ae9383fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 43 deletions

View File

@ -1,6 +1,8 @@
export abstract class ApiKeyService {
setInformation: (clientId: string) => Promise<any>;
setInformation: (clientId: string, clientSecret: string) => Promise<any>;
clear: () => Promise<any>;
getClientId: () => Promise<string>;
getClientSecret: () => Promise<string>;
getEntityType: () => Promise<string>;
getEntityId: () => Promise<string>;
isAuthenticated: () => Promise<boolean>;

View File

@ -1312,8 +1312,8 @@ export class ApiService implements ApiServiceAbstraction {
async getActiveBearerToken(): Promise<string> {
let accessToken = await this.tokenService.getToken();
if (this.tokenService.tokenNeedsRefresh()) {
const tokenResponse = await this.doRefreshToken();
accessToken = tokenResponse.accessToken;
await this.doRefreshToken();
accessToken = await this.tokenService.getToken();
}
return accessToken;
}
@ -1358,6 +1358,43 @@ export class ApiService implements ApiServiceAbstraction {
}
}
protected async doRefreshToken(): Promise<void> {
const refreshToken = await this.tokenService.getRefreshToken();
if (refreshToken == null || refreshToken === '') {
throw new Error();
}
const headers = new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Accept': 'application/json',
'Device-Type': this.deviceType,
});
if (this.customUserAgent != null) {
headers.set('User-Agent', this.customUserAgent);
}
const decodedToken = this.tokenService.decodeToken();
const response = await this.fetch(new Request(this.identityBaseUrl + '/connect/token', {
body: this.qsStringify({
grant_type: 'refresh_token',
client_id: decodedToken.client_id,
refresh_token: refreshToken,
}),
cache: 'no-store',
credentials: this.getCredentials(),
headers: headers,
method: 'POST',
}));
if (response.status === 200) {
const responseJson = await response.json();
const tokenResponse = new IdentityTokenResponse(responseJson);
await this.tokenService.setTokens(tokenResponse.accessToken, tokenResponse.refreshToken);
} else {
const error = await this.handleError(response, true, true);
return Promise.reject(error);
}
}
private async send(method: 'GET' | 'POST' | 'PUT' | 'DELETE', path: string, body: any,
authed: boolean, hasResponse: boolean, apiUrl?: string,
alterHeaders?: (headers: Headers) => void): Promise<any> {
@ -1427,44 +1464,6 @@ export class ApiService implements ApiServiceAbstraction {
return new ErrorResponse(responseJson, response.status, tokenError);
}
private async doRefreshToken(): Promise<IdentityTokenResponse> {
const refreshToken = await this.tokenService.getRefreshToken();
if (refreshToken == null || refreshToken === '') {
throw new Error();
}
const headers = new Headers({
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
'Accept': 'application/json',
'Device-Type': this.deviceType,
});
if (this.customUserAgent != null) {
headers.set('User-Agent', this.customUserAgent);
}
const decodedToken = this.tokenService.decodeToken();
const response = await this.fetch(new Request(this.identityBaseUrl + '/connect/token', {
body: this.qsStringify({
grant_type: 'refresh_token',
client_id: decodedToken.client_id,
refresh_token: refreshToken,
}),
cache: 'no-store',
credentials: this.getCredentials(),
headers: headers,
method: 'POST',
}));
if (response.status === 200) {
const responseJson = await response.json();
const tokenResponse = new IdentityTokenResponse(responseJson);
await this.tokenService.setTokens(tokenResponse.accessToken, tokenResponse.refreshToken);
return tokenResponse;
} else {
const error = await this.handleError(response, true, true);
return Promise.reject(error);
}
}
private qsStringify(params: any): string {
return Object.keys(params).map(key => {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);

View File

@ -6,6 +6,7 @@ import { Utils } from '../misc/utils';
const Keys = {
clientId: 'clientId',
clientSecret: 'clientSecret',
entityType: 'entityType',
entityId: 'entityId',
};
@ -13,13 +14,15 @@ const Keys = {
export class ApiKeyService implements ApiKeyServiceAbstraction {
private clientId: string;
private clientSecret: string;
private entityType: string;
private entityId: string;
constructor(private tokenService: TokenService, private storageService: StorageService) { }
async setInformation(clientId: string) {
async setInformation(clientId: string, clientSecret: string) {
this.clientId = clientId;
this.clientSecret = clientSecret;
const idParts = clientId.split('.');
if (idParts.length !== 2 || !Utils.isGuid(idParts[1])) {
@ -31,6 +34,21 @@ export class ApiKeyService implements ApiKeyServiceAbstraction {
await this.storageService.save(Keys.clientId, this.clientId);
await this.storageService.save(Keys.entityId, this.entityId);
await this.storageService.save(Keys.entityType, this.entityType);
await this.storageService.save(Keys.clientSecret, this.clientSecret);
}
async getClientId(): Promise<string> {
if (this.clientId == null) {
this.clientId = await this.storageService.get<string>(Keys.clientId);
}
return this.clientId;
}
async getClientSecret(): Promise<string> {
if (this.clientSecret == null) {
this.clientSecret = await this.storageService.get<string>(Keys.clientSecret);
}
return this.clientSecret;
}
async getEntityType(): Promise<string> {
@ -49,10 +67,11 @@ export class ApiKeyService implements ApiKeyServiceAbstraction {
async clear(): Promise<any> {
await this.storageService.remove(Keys.clientId);
await this.storageService.remove(Keys.clientSecret);
await this.storageService.remove(Keys.entityId);
await this.storageService.remove(Keys.entityType);
this.clientId = this.entityId = this.entityType = null;
this.clientId = this.clientSecret = this.entityId = this.entityType = null;
}
async isAuthenticated(): Promise<boolean> {