Skip to content

Commit

Permalink
feat: #3 replace .then with async/await pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
pamapa committed Jul 21, 2021
1 parent f77455d commit 35318a2
Show file tree
Hide file tree
Showing 13 changed files with 780 additions and 853 deletions.
72 changes: 33 additions & 39 deletions src/MetadataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,27 +48,24 @@ export class MetadataService {
this._settings.signingKeys = undefined
}

getMetadata(): Promise<Partial<OidcMetadata>> {
async getMetadata(): Promise<Partial<OidcMetadata>> {
if (this._settings.metadata) {
Log.debug("MetadataService.getMetadata: Returning metadata from settings");
return Promise.resolve(this._settings.metadata);
return this._settings.metadata;
}

if (!this.metadataUrl) {
Log.error("MetadataService.getMetadata: No authority or metadataUrl configured on settings");
return Promise.reject(new Error("No authority or metadataUrl configured on settings"));
throw new Error("No authority or metadataUrl configured on settings");
}

Log.debug("MetadataService.getMetadata: getting metadata from", this.metadataUrl);
const metadata = await this._jsonService.getJson(this.metadataUrl);

return this._jsonService.getJson(this.metadataUrl)
.then(metadata => {
Log.debug("MetadataService.getMetadata: json received");

var seed = this._settings.metadataSeed || {};
this._settings.metadata = Object.assign({}, seed, metadata) as Partial<OidcMetadata>;
return this._settings.metadata;
});
Log.debug("MetadataService.getMetadata: json received");
var seed = this._settings.metadataSeed || {};
this._settings.metadata = Object.assign({}, seed, metadata) as Partial<OidcMetadata>;
return this._settings.metadata;
}

getIssuer() {
Expand Down Expand Up @@ -103,47 +100,44 @@ export class MetadataService {
return this._getMetadataProperty("jwks_uri", optional) as Promise<string | undefined>;
}

_getMetadataProperty(name: keyof OidcMetadata, optional=false) {
async _getMetadataProperty(name: keyof OidcMetadata, optional=false) {
Log.debug("MetadataService.getMetadataProperty for: " + name);

return this.getMetadata().then(metadata => {
Log.debug("MetadataService.getMetadataProperty: metadata recieved");
const metadata = await this.getMetadata();
Log.debug("MetadataService.getMetadataProperty: metadata recieved");

if (metadata[name] === undefined) {
if (optional === true) {
Log.warn("MetadataService.getMetadataProperty: Metadata does not contain optional property " + name);
return undefined;
}
else {
Log.error("MetadataService.getMetadataProperty: Metadata does not contain property " + name);
throw new Error("Metadata does not contain property " + name);
}
if (metadata[name] === undefined) {
if (optional === true) {
Log.warn("MetadataService.getMetadataProperty: Metadata does not contain optional property " + name);
return undefined;
}
else {
Log.error("MetadataService.getMetadataProperty: Metadata does not contain property " + name);
throw new Error("Metadata does not contain property " + name);
}
}

return metadata[name];
});
return metadata[name];
}

getSigningKeys() {
async getSigningKeys() {
if (this._settings.signingKeys) {
Log.debug("MetadataService.getSigningKeys: Returning signingKeys from settings");
return Promise.resolve(this._settings.signingKeys);
return this._settings.signingKeys;
}

return this.getKeysEndpoint(false).then(jwks_uri => {
Log.debug("MetadataService.getSigningKeys: jwks_uri received", jwks_uri);
const jwks_uri = await this.getKeysEndpoint(false);
Log.debug("MetadataService.getSigningKeys: jwks_uri received", jwks_uri);

return this._jsonService.getJson(jwks_uri as string).then(keySet => {
Log.debug("MetadataService.getSigningKeys: key set received", keySet);
const keySet = await this._jsonService.getJson(jwks_uri as string);
Log.debug("MetadataService.getSigningKeys: key set received", keySet);

if (!keySet.keys) {
Log.error("MetadataService.getSigningKeys: Missing keys on keyset");
throw new Error("Missing keys on keyset");
}
if (!keySet.keys) {
Log.error("MetadataService.getSigningKeys: Missing keys on keyset");
throw new Error("Missing keys on keyset");
}

this._settings.signingKeys = keySet.keys;
return this._settings.signingKeys;
});
});
this._settings.signingKeys = keySet.keys;
return this._settings.signingKeys;
}
}
166 changes: 78 additions & 88 deletions src/OidcClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class OidcClient {
return this._metadataService;
}

createSigninRequest({
async createSigninRequest({
response_type, scope, redirect_uri,
// data was meant to be the place a caller could indicate the data to
// have round tripped, but people were getting confused, so i added state (since that matches the spec)
Expand All @@ -45,7 +45,7 @@ export class OidcClient {
resource, request, request_uri, response_mode, extraQueryParams, extraTokenParams, request_type, skipUserInfo
}: any = {},
stateStore?: StateStore
) {
): Promise<SigninRequest> {
Log.debug("OidcClient.createSigninRequest");

let client_id = this._settings.client_id;
Expand All @@ -67,36 +67,33 @@ export class OidcClient {
let authority = this._settings.authority;

if (SigninRequest.isCode(response_type) && response_type !== "code") {
return Promise.reject(new Error("OpenID Connect hybrid flow is not supported"));
throw new Error("OpenID Connect hybrid flow is not supported");
}

return this._metadataService.getAuthorizationEndpoint().then((url) => {
Log.debug("OidcClient.createSigninRequest: Received authorization endpoint", url);

let signinRequest = new SigninRequest({
url,
client_id,
redirect_uri,
response_type,
scope,
data: data || state,
authority,
prompt, display, max_age, ui_locales, id_token_hint, login_hint, acr_values,
resource, request, request_uri, extraQueryParams, extraTokenParams, request_type, response_mode,
client_secret: this._settings.client_secret,
skipUserInfo
});

var signinState = signinRequest.state;
stateStore = stateStore || this._stateStore;

return stateStore.set(signinState.id, signinState.toStorageString()).then(() => {
return signinRequest;
});
const url = await this._metadataService.getAuthorizationEndpoint();
Log.debug("OidcClient.createSigninRequest: Received authorization endpoint", url);

let signinRequest = new SigninRequest({
url,
client_id,
redirect_uri,
response_type,
scope,
data: data || state,
authority,
prompt, display, max_age, ui_locales, id_token_hint, login_hint, acr_values,
resource, request, request_uri, extraQueryParams, extraTokenParams, request_type, response_mode,
client_secret: this._settings.client_secret,
skipUserInfo
});

var signinState = signinRequest.state;
stateStore = stateStore || this._stateStore;
await stateStore.set(signinState.id, signinState.toStorageString());
return signinRequest;
}

readSigninResponseState(url?: string, stateStore: StateStore | null = null, removeState = false) {
async readSigninResponseState(url?: string, stateStore: StateStore | null = null, removeState = false) {
Log.debug("OidcClient.readSigninResponseState");

let useQuery = this._settings.response_mode === "query" ||
Expand All @@ -105,37 +102,34 @@ export class OidcClient {
let delimiter = useQuery ? "?" : "#";

var response = new SigninResponse(url, delimiter);

if (!response.state) {
Log.error("OidcClient.readSigninResponseState: No state in response");
return Promise.reject(new Error("No state in response"));
throw new Error("No state in response");
}

stateStore = stateStore || this._stateStore;

var stateApi = removeState ? stateStore.remove.bind(stateStore) : stateStore.get.bind(stateStore);

return stateApi(response.state).then(storedStateString => {
if (!storedStateString) {
Log.error("OidcClient.readSigninResponseState: No matching state found in storage");
throw new Error("No matching state found in storage");
}
const storedStateString = await stateApi(response.state);
if (!storedStateString) {
Log.error("OidcClient.readSigninResponseState: No matching state found in storage");
throw new Error("No matching state found in storage");
}

let state = SigninState.fromStorageString(storedStateString);
return {state, response};
});
let state = SigninState.fromStorageString(storedStateString);
return {state, response};
}

processSigninResponse(url: string, stateStore: StateStore | null = null) {
async processSigninResponse(url: string, stateStore: StateStore | null = null) {
Log.debug("OidcClient.processSigninResponse");

return this.readSigninResponseState(url, stateStore, true).then(({state, response}) => {
Log.debug("OidcClient.processSigninResponse: Received state from storage; validating response");
return this._validator.validateSigninResponse(state, response);
});
const { state, response } = await this.readSigninResponseState(url, stateStore, true);
Log.debug("OidcClient.processSigninResponse: Received state from storage; validating response");
return this._validator.validateSigninResponse(state, response);
}

createSignoutRequest({
async createSignoutRequest({
id_token_hint, data, state, post_logout_redirect_uri, extraQueryParams, request_type
}: any = {},
stateStore: StateStore | null = null
Expand All @@ -145,36 +139,35 @@ export class OidcClient {
post_logout_redirect_uri = post_logout_redirect_uri || this._settings.post_logout_redirect_uri;
extraQueryParams = extraQueryParams || this._settings.extraQueryParams;

return this._metadataService.getEndSessionEndpoint().then(url => {
if (!url) {
Log.error("OidcClient.createSignoutRequest: No end session endpoint url returned");
throw new Error("no end session endpoint");
}
const url = await this._metadataService.getEndSessionEndpoint();
if (!url) {
Log.error("OidcClient.createSignoutRequest: No end session endpoint url returned");
throw new Error("no end session endpoint");
}

Log.debug("OidcClient.createSignoutRequest: Received end session endpoint", url);
Log.debug("OidcClient.createSignoutRequest: Received end session endpoint", url);

let request = new SignoutRequest({
url,
id_token_hint,
post_logout_redirect_uri,
data: data || state,
extraQueryParams,
request_type
});
let request = new SignoutRequest({
url,
id_token_hint,
post_logout_redirect_uri,
data: data || state,
extraQueryParams,
request_type
});

var signoutState = request.state;
if (signoutState) {
Log.debug("OidcClient.createSignoutRequest: Signout request has state to persist");
var signoutState = request.state;
if (signoutState) {
Log.debug("OidcClient.createSignoutRequest: Signout request has state to persist");

stateStore = stateStore || this._stateStore;
stateStore.set(signoutState.id, signoutState.toStorageString());
}
stateStore = stateStore || this._stateStore;
stateStore.set(signoutState.id, signoutState.toStorageString());
}

return request;
});
return request;
}

readSignoutResponseState(url: string, stateStore: StateStore | null = null, removeState = false)
async readSignoutResponseState(url?: string, stateStore: StateStore | null = null, removeState = false)
: Promise<{ state: undefined | State, response: SignoutResponse }> {
Log.debug("OidcClient.readSignoutResponseState");

Expand All @@ -184,42 +177,39 @@ export class OidcClient {

if (response.error) {
Log.warn("OidcClient.readSignoutResponseState: Response was error: ", response.error);
return Promise.reject(new ErrorResponse(response));
throw new ErrorResponse(response);
}

return Promise.resolve({state: undefined, response});
return {state: undefined, response};
}

var stateKey = response.state;

stateStore = stateStore || this._stateStore;

var stateApi = removeState ? stateStore.remove.bind(stateStore) : stateStore.get.bind(stateStore);
return stateApi(stateKey).then(storedStateString => {
if (!storedStateString) {
Log.error("OidcClient.readSignoutResponseState: No matching state found in storage");
throw new Error("No matching state found in storage");
}

let state = State.fromStorageString(storedStateString);
const storedStateString = await stateApi(stateKey);
if (!storedStateString) {
Log.error("OidcClient.readSignoutResponseState: No matching state found in storage");
throw new Error("No matching state found in storage");
}

return {state, response};
});
let state = State.fromStorageString(storedStateString);
return {state, response};
}

processSignoutResponse(url: string, stateStore: StateStore | null = null) {
async processSignoutResponse(url: string, stateStore: StateStore | null = null) {
Log.debug("OidcClient.processSignoutResponse");

return this.readSignoutResponseState(url, stateStore, true).then(({state, response}) => {
if (state) {
Log.debug("OidcClient.processSignoutResponse: Received state from storage; validating response");
return this._validator.validateSignoutResponse(state, response);
}
else {
Log.debug("OidcClient.processSignoutResponse: No state from storage; skipping validating response");
return response;
}
});
const {state, response} = await this.readSignoutResponseState(url, stateStore, true);
if (state) {
Log.debug("OidcClient.processSignoutResponse: Received state from storage; validating response");
return this._validator.validateSignoutResponse(state, response);
}
else {
Log.debug("OidcClient.processSignoutResponse: No state from storage; skipping validating response");
return response;
}
}

clearStaleState(stateStore: StateStore | null = null): Promise<void> {
Expand Down
Loading

0 comments on commit 35318a2

Please sign in to comment.