UNPKG

oidc-client

Version:

OpenID Connect (OIDC) & OAuth2 client library

202 lines (160 loc) 7.93 kB
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. import { Log } from './Log.js'; import { OidcClientSettings } from './OidcClientSettings.js'; import { ErrorResponse } from './ErrorResponse.js'; import { SigninRequest } from './SigninRequest.js'; import { SigninResponse } from './SigninResponse.js'; import { SignoutRequest } from './SignoutRequest.js'; import { SignoutResponse } from './SignoutResponse.js'; import { SigninState } from './SigninState.js'; import { State } from './State.js'; export class OidcClient { constructor(settings = {}) { if (settings instanceof OidcClientSettings) { this._settings = settings; } else { this._settings = new OidcClientSettings(settings); } } get _stateStore() { return this.settings.stateStore; } get _validator() { return this.settings.validator; } get _metadataService() { return this.settings.metadataService; } get settings() { return this._settings; } get metadataService() { return this._metadataService; } 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) // and so now if data is not passed, but state is then state will be used data, state, prompt, display, max_age, ui_locales, id_token_hint, login_hint, acr_values, resource, request, request_uri, response_mode, extraQueryParams } = {}, stateStore ) { Log.debug("OidcClient.createSigninRequest"); let client_id = this._settings.client_id; response_type = response_type || this._settings.response_type; scope = scope || this._settings.scope; redirect_uri = redirect_uri || this._settings.redirect_uri; // id_token_hint, login_hint aren't allowed on _settings prompt = prompt || this._settings.prompt; display = display || this._settings.display; max_age = max_age || this._settings.max_age; ui_locales = ui_locales || this._settings.ui_locales; acr_values = acr_values || this._settings.acr_values; resource = resource || this._settings.resource; response_mode = response_mode || this._settings.response_mode; extraQueryParams = extraQueryParams || this._settings.extraQueryParams; 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")); } 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, response_mode }); var signinState = signinRequest.state; stateStore = stateStore || this._stateStore; return stateStore.set(signinState.id, signinState.toStorageString()).then(() => { return signinRequest; }); }); } processSigninResponse(url, stateStore) { Log.debug("OidcClient.processSigninResponse"); let useQuery = this._settings.response_mode === "query" || (!this._settings.response_mode && SigninRequest.isCode(this._settings.response_type)); let delimiter = useQuery ? "?" : "#"; var response = new SigninResponse(url, delimiter); if (!response.state) { Log.error("OidcClient.processSigninResponse: No state in response"); return Promise.reject(new Error("No state in response")); } stateStore = stateStore || this._stateStore; return stateStore.remove(response.state).then(storedStateString => { if (!storedStateString) { Log.error("OidcClient.processSigninResponse: No matching state found in storage"); throw new Error("No matching state found in storage"); } let state = SigninState.fromStorageString(storedStateString); Log.debug("OidcClient.processSigninResponse: Received state from storage; validating response"); return this._validator.validateSigninResponse(state, response); }); } createSignoutRequest({id_token_hint, data, state, post_logout_redirect_uri, extraQueryParams } = {}, stateStore ) { Log.debug("OidcClient.createSignoutRequest"); 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"); } 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 }); 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()); } return request; }); } processSignoutResponse(url, stateStore) { Log.debug("OidcClient.processSignoutResponse"); var response = new SignoutResponse(url); if (!response.state) { Log.debug("OidcClient.processSignoutResponse: No state in response"); if (response.error) { Log.warn("OidcClient.processSignoutResponse: Response was error: ", response.error); return Promise.reject(new ErrorResponse(response)); } return Promise.resolve(response); } var stateKey = response.state; stateStore = stateStore || this._stateStore; return stateStore.remove(stateKey).then(storedStateString => { if (!storedStateString) { Log.error("OidcClient.processSignoutResponse: No matching state found in storage"); throw new Error("No matching state found in storage"); } let state = State.fromStorageString(storedStateString); Log.debug("OidcClient.processSignoutResponse: Received state from storage; validating response"); return this._validator.validateSignoutResponse(state, response); }); } clearStaleState(stateStore) { Log.debug("OidcClient.clearStaleState"); stateStore = stateStore || this._stateStore; return State.clearStaleState(stateStore, this.settings.staleStateAge); } }