diff --git a/.eslintignore b/.eslintignore index 852e28135..a8407e387 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,7 +2,6 @@ /.pnp.* node_modules/ dist/ -/lib/ -/samples/ -/test/conformance/ -/types/ +docs/pages/ +lib/ +samples/ diff --git a/README.md b/README.md index 9836863d6..c491bb53f 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Library to provide OpenID Connect (OIDC) and OAuth2 protocol support for client- applications. Also included is support for user session and access token management. This is a forked version of the [oidc-client-js](https://github.com/IdentityModel/oidc-client-js) library, which has -been archived and is no longer maintained. This version has been refactored from JavaScript to TypeScript. Trying to keep the API as compatible as possible. The support for the outdated implicit flow has been removed. +been archived and is no longer maintained. This version has been refactored from JavaScript to TypeScript. Trying to keep the API as compatible as possible. The support for the outdated implicit flow has been removed. When migrating see [here](docs/migration.md). **Contributions and help is much appreciated!** @@ -23,7 +23,7 @@ been archived and is no longer maintained. This version has been refactored from ## Documentation -Some initial docs are [here](https://github.com/IdentityModel/oidc-client-js/wiki). +Additional documentation can be found [here](https://authts.github.io/oidc-client-ts/). ## Installation diff --git a/docs/index.md b/docs/index.md index edf5dfcd4..243826e3b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,131 +1,64 @@ -# oidc-client -oidc-client is a JavaScript library intended to run in browsers (and possibly Cordova style applications). It provides protocol support for OIDC and OAuth2, as well as management functions for user sessions and access tokens management. +oidc-client-ts is a TypeScript library intended to be used by web applications and run in browsers. It provides protocol support for OIDC and OAuth2, as well as management functions for user sessions and access tokens management. If you are unfamiliar with OpenID Connect, then you should learn the [protocol](https://openid.net/specs/openid-connect-core-1_0.html) first. This library is designed as a spec-compliant protocol library. There are two main classes that you might want to use depend on the level at with you use to use the library: -The `UserManager` class provides a higher level API for signing a user in, signing out, managing the user's claims returned from the OIDC provider, and managing an access token returned from the OIDC/OAuth2 provider. The `UserManager` is the primary feature of the library. +- [UserManager](classes/UserManager.html) provides a higher level API for signing a user in, signing out, managing the user's claims +- [OidcClient](classes/OidcClient.html) provides the raw OIDC/OAuth2 protocol support -The `OidcClient` class provides the raw OIDC/OAuth2 protocol support for the authorization endpoint and the end session endpoint in the authorization server. It provides a bare-bones protocol implementation and is used by the `UserManager` class. Only use this class if you simply want protocol support without the additional management features of the `UserManager` class. +The remainder of this document will primarily focus on the [UserManager](classes/UserManager.html). -The remainder of this document will primarily focus on the `UserManager`. ## UserManager ### Configuration -The `UserManager` constructor requires a settings object as a parameter. The settings has these properties: +The [UserManager](classes/UserManager.html) constructor requires a settings object as a parameter: -#### Required Settings -* authority (string): The URL of the OIDC/OAuth2 provider. -* client_id (string): Your client application's identifier as registered with the OIDC/OAuth2 provider. -* redirect_uri (string): The redirect URI of your client application to receive a response from the OIDC/OAuth2 provider. -* response_type (string, default: `'code'`): The type of response desired from the OIDC/OAuth2 provider. -* scope (string, default: `'openid'`): The scope being requested from the OIDC/OAuth2 provider. +- [UserManagerSettings](interfaces/UserManagerSettings.html) which extends +- [OidcClientSettings](interfaces/OidcClientSettings.html) + +#### Required settings +* [authority](interfaces/OidcClientSettings.html#authority): The URL of the OIDC/OAuth2 provider. +* [client_id](interfaces/OidcClientSettings.html#client_id): Your client application's identifier as registered with the OIDC/OAuth2 provider. +* [redirect_uri](interfaces/OidcClientSettings.html#redirect_uri): The redirect URI of your client application to receive a response from the OIDC/OAuth2 provider. #### Provider settings if CORS not supported on OIDC/OAuth2 provider metadata endpoint -The `authority` URL setting is used to make HTTP requests to discover more information about the OIDC/OAuth2 provider and populate a `metadata` property on the settings. If the server does not allow CORS on the metadata endpoint, then these additional settings can be manually configured. These values can be found on the metadata endpoint of the provider: -* metadata property which contains: - * issuer - * authorization_endpoint - * userinfo_endpoint - * end_session_endpoint - * jwks_uri -* signingKeys (which is the `keys` property of the `jwks_uri` endpoint) -* metadataSeed can be used to seed or add additional values to the results of the discovery request. - -#### Optional Authorization Request Settings -* prompt -* display -* max_age -* ui_locales -* login_hint -* acr_values - -#### Other Optional Settings -* clockSkew (number, default: `300`): The window of time (in seconds) to allow the current time to deviate when validating token's `iat`, `nbf`, and `exp` values. -* loadUserInfo (boolean, default: `true`): Flag to control if additional identity data is loaded from the user info endpoint in order to populate the user's `profile`. -* filterProtocolClaims (boolean, default: `true`): Should OIDC protocol claims be removed from `profile`. -* post_logout_redirect_uri (string): The OIDC/OAuth2 post-logout redirect URI. -* popup_redirect_uri (string): The URL for the page containing the call to `signinPopupCallback` to handle the callback from the OIDC/OAuth2 -* popupWindowFeatures (string, default: `'location=no,toolbar=no,width=500,height=500,left=100,top=100'`): The `features` parameter to `window.open` for the popup signin window. -* popupWindowTarget (string, default: `'_blank'`): The `target` parameter to `window.open` for the popup signin window. -* silent_redirect_uri (string): The URL for the page containing the code handling the silent renew. -* automaticSilentRenew (boolean, default: `false`): Flag to indicate if there should be an automatic attempt to renew the access token prior to its expiration. The attempt is made as a result of the `accessTokenExpiring` event being raised. -* silentRequestTimeout (number, default: `10000`): Number of milliseconds to wait for the silent renew to return before assuming it has failed or timed out. -* accessTokenExpiringNotificationTime (number, default: `60`): The number of seconds before an access token is to expire to raise the `accessTokenExpiring` event. -* stateStore: (default: local storage): Storage object used to persist interaction state. E.g. `userStore: new WebStorageStateStore({ store: window.localStorage })` -* userStore: (default: session storage): Storage object used to persist `User` for currently authenticated user. E.g. `userStore: new WebStorageStateStore({ store: window.localStorage })` -* monitorSession [1.1.0]: (default: `true`): Will raise events for when user has performed a signout at the OP. -* checkSessionInterval: (default: `2000`): Interval, in ms, to check the user's session. -* revokeAccessTokenOnSignout [1.2.1] (default: `false`): Will invoke the revocation endpoint on signout if there is an access token for the user. -* staleStateAge (default: `300`): Number (in seconds) indicating the age of state entries in storage for authorize requests that are considered abandoned and thus can be cleaned up. -* extraQueryParams: (object): An object containing additional query string parameters to be including in the authorization request. E.g, when using Azure AD to obtain an access token an additional resource parameter is required. extraQueryParams: `{resource:"some_identifier"}` -* mergeClaims [1.11.0] (default: `false`): Indicates if objects returned from the user info endpoint as claims (e.g. `address`) are merged into the claims from the id token as a single object. Otherwise, they are added to an array as distinct objects for the claim type. -* client_authentication [1.11.0] (default: `client_secret_post`): Indicates when sending client secret if sent as a post param or in `Authorization` header using HTTP Basic (use `client_secret_basic`). -* clockService [1.11.0]: Service that can be configured to get the clock time. Used to deal with client machines with incorrect clocks. - -### Methods -* getUser: Returns promise to load the `User` object for the currently authenticated user. -* removeUser: Returns promise to remove from any storage the currently authenticated user. -* signinRedirect: Returns promise to trigger a redirect of the current window to the authorization endpoint. -* signinRedirectCallback: Returns promise to process response from the authorization endpoint. The result of the promise is the authenticated `User`. -* signinSilent: Returns promise to trigger a silent request (via an iframe) to the authorization endpoint. The result of the promise is the authenticated `User`. -* signinSilentCallback: Returns promise to notify the parent window of response from the authorization endpoint. -* signinPopup: Returns promise to trigger a request (via a popup window) to the authorization endpoint. The result of the promise is the authenticated `User`. -* signinPopupCallback: Returns promise to notify the opening window of response from the authorization endpoint. -* signoutRedirect: Returns promise to trigger a redirect of the current window to the end session endpoint. -* signoutRedirectCallback: Returns promise to process response from the end session endpoint. -* signoutPopup [1.4.0]: Returns promise to trigger a redirect of a popup window window to the end session endpoint. -* signoutPopupCallback [1.4.0]: Returns promise to process response from the end session endpoint from a popup window. -* querySessionStatus [1.1.0]: Returns promise to query OP for user's current signin status. Returns object with session_state and subject identifier. -* startSilentRenew [1.4.0]: Enables silent renew for the `UserManager`. -* stopSilentRenew [1.4.0]: Disables silent renew for the `UserManager`. -* clearStaleState: Removes stale state entries in storage for incomplete authorize requests. - -### Properties -* settings: Returns the settings used to configure the `UserManager`. -* events: Returns an object used to register for events raised by the `UserManager`. -* metadataService: Returns an object used to access the metadata configuration of the OIDC provider. +The [authority](interfaces/OidcClientSettings.html#authority) URL setting is used to make HTTP requests to discover more information about the OIDC/OAuth2 provider and populate a `metadata` property on the settings. If the server does not allow CORS on the metadata endpoint, then these additional settings can be manually configured. These values can be found on the metadata endpoint of the provider: +- metadata property which contains: + - issuer + - authorization_endpoint + - userinfo_endpoint + - end_session_endpoint + - jwks_uri +- [signingKeys](interfaces/UserManagerSettings.html#signingKeys) (which is the `keys` property of the `jwks_uri` endpoint) +- [metadataSeed](interfaces/UserManagerSettings.html#metadataSeed) can be used to seed or add additional values to the results of the discovery request. ### Events -The `UserManager` will raise various events about the user's session: +The [UserManager](classes/UserManager.html) will raise various events about the user's session: -* userLoaded: Raised when a user session has been established (or re-established). -* userUnloaded: Raised when a user session has been terminated. -* accessTokenExpiring: Raised prior to the access token expiring. -* accessTokenExpired: Raised after the access token has expired. -* silentRenewError: Raised when the automatic silent renew has failed. -* userSignedIn [1.9.0]: Raised when the user is signed in. -* userSignedOut [1.1.0]: Raised when the user's sign-in status at the OP has changed. -* userSessionChanged: Raised when the user session changed (when `monitorSession` is set) +- [UserManagerEvents](classes/UserManagerEvents.html) which extends +- [AccessTokenEvents](classes/AccessTokenEvents.html) -To register for the events, there is an `events` property on the `UserManager` with `addXxx` and `removeXxx` APIs to add/remove callbacks for the events. An example: +To register for the events, there is an `events` property on the [UserManager](classes/UserManager.html) with `addXxx` and `removeXxx` APIs to add/remove callbacks for the events. An example: ``` var mgr = new UserManager(); -mgr.events.addAccessTokenExpiring(function(){ +mgr.events.addAccessTokenExpiring(function() { console.log("token expiring..."); }); ``` + ## User -The `User` type is returned from the `UserManager`'s `getUser` API. It contains these properties: +The [User](classes/User.html) type is returned from the [UserManager](classes/UserManager.html)'s [getUser](classes/UserManager.html#getUser) API. -* profile: The claims represented by a combination of the `token` and the user info endpoint. -* session_state: The session state value returned from the OIDC provider. -* access_token: The access token returned from the OIDC provider. -* scope: The scope returned from the OIDC provider. -* expires_at: The expires at returned from the OIDC provider. -* expires_in: Calculated number of seconds the access token has remaining. -* expired: Calculated value indicating if the access token is expired. -* scopes: Array representing the parsed values from the `scope`. ## Logging -The oidc-client-js library supports logging. You can set a logger by assigning `Oidc.Log.logger` to anything that supports a `info`, `warn`, and `error` methods that accept a params array. By default, no logger is configured. +The oidc-client-ts library supports logging. You can set a logger by assigning `Oidc.Log.logger` to anything that supports a `info`, `warn`, and `error` methods that accept a params array. By default, no logger is configured. The `console` object in the browser supports these, so a common way to easily enable logging in the browser is to simply add this code: @@ -135,20 +68,12 @@ Oidc.Log.logger = console; Also, logging has levels so you can control the verbosity by setting the `Oidc.Log.level` to one of `Oidc.Log.NONE`, `Oidc.Log.ERROR`, `Oidc.Log.WARN`, or `Oidc.Log.INFO`. The default is `Oidc.Log.INFO`. + ## Samples using oidc-client -* [Angular2](https://github.com/jmurphzyo/Angular2OidcClient) -* [Aurelia](https://github.com/shaunluttin/aurelia-open-id-connect) -* [ReactJS & Redux](https://github.com/maxmantz/redux-oidc) -* [Blog post on Angular](https://www.scottbrady91.com/Angular/SPA-Authentiction-using-OpenID-Connect-Angular-CLI-and-oidc-client) -* [Vue.js](https://github.com/joaojosefilho/vuejsOidcClient) -* [Quasar Framework](https://github.com/patrickmonteiro/quasarOidcClient) -* [Angular2](https://github.com/fileless/ng-oidc-client) -* [Vue/Vuex](https://github.com/perarnborg/vuex-oidc) -* [React Context & React Redux](https://github.com/AxaGuilDEv/react-oidc) -* [Angular and Keycloak](https://robferguson.org/blog/2019/12/29/angular-openid-connect-keycloak/) -* [React Helper Library](https://github.com/bjerkio/oidc-react) +- [React Helper Library](https://github.com/authts/react-oidc-context) + ## Training -* [Securing Angular Apps with OpenID and OAuth2](https://noyes.me/ng-openid-oauth2) +- [Securing Angular Apps with OpenID and OAuth2](https://noyes.me/ng-openid-oauth2) diff --git a/docs/oidc-client-ts.api.md b/docs/oidc-client-ts.api.md index bb9732840..a77a203d3 100644 --- a/docs/oidc-client-ts.api.md +++ b/docs/oidc-client-ts.api.md @@ -12,9 +12,7 @@ export class AccessTokenEvents { constructor({ expiringNotificationTimeInSeconds }: { expiringNotificationTimeInSeconds: number; }); - // (undocumented) addAccessTokenExpired(cb: AccessTokenCallback): void; - // (undocumented) addAccessTokenExpiring(cb: AccessTokenCallback): void; // (undocumented) load(container: User): void; @@ -22,9 +20,7 @@ export class AccessTokenEvents { // // (undocumented) protected _logger: Logger; - // (undocumented) removeAccessTokenExpired(cb: AccessTokenCallback): void; - // (undocumented) removeAccessTokenExpiring(cb: AccessTokenCallback): void; // (undocumented) unload(): void; @@ -79,7 +75,6 @@ export interface CreateSigninRequestArgs { scope?: string; // (undocumented) skipUserInfo?: boolean; - // (undocumented) state?: unknown; // (undocumented) ui_locales?: string; @@ -182,7 +177,7 @@ export class MetadataService { resetSigningKeys(): void; } -// @public (undocumented) +// @public export class OidcClient { constructor(settings: OidcClientSettings); // (undocumented) @@ -219,45 +214,35 @@ export class OidcClient { // @public (undocumented) export interface OidcClientSettings { - // (undocumented) acr_values?: string; authority: string; - // (undocumented) client_authentication?: string; client_id: string; // (undocumented) client_secret?: string; clockSkewInSeconds?: number; - // (undocumented) display?: string; extraQueryParams?: Record; // (undocumented) extraTokenParams?: Record; filterProtocolClaims?: boolean; loadUserInfo?: boolean; - // (undocumented) max_age?: number; - // (undocumented) mergeClaims?: boolean; metadata?: Partial; metadataSeed?: Partial; // (undocumented) metadataUrl?: string; post_logout_redirect_uri?: string; - // (undocumented) prompt?: string; redirect_uri: string; - // (undocumented) resource?: string; - // (undocumented) response_mode?: "query" | "fragment"; response_type?: string; scope?: string; signingKeys?: SigningKey[]; staleStateAgeInSeconds?: number; - // (undocumented) stateStore?: StateStore; - // (undocumented) ui_locales?: string; // (undocumented) userInfoJwtIssuer?: "ANY" | "OP" | string; @@ -491,7 +476,6 @@ export interface SigninRequestArgs { scope: string; // (undocumented) skipUserInfo?: boolean; - // (undocumented) state_data?: unknown; // (undocumented) ui_locales?: string; @@ -533,7 +517,6 @@ export class SigninResponse { get scopes(): string[]; // (undocumented) session_state: string | undefined; - // (undocumented) state: unknown | undefined; // (undocumented) readonly state_id: string | undefined; @@ -627,7 +610,6 @@ export class SignoutResponse { error_description: string | undefined; // (undocumented) error_uri: string | undefined; - // (undocumented) state: unknown | undefined; // (undocumented) readonly state_id: string | undefined; @@ -648,7 +630,6 @@ export class State { static clearStaleState(storage: StateStore, age: number): Promise; // (undocumented) readonly created: number; - // (undocumented) readonly data: unknown | undefined; // (undocumented) static fromStorageString(storageString: string): State; @@ -694,30 +675,21 @@ export class User { expires_at?: number; state?: unknown; }); - // (undocumented) access_token: string; - // (undocumented) get expired(): boolean | undefined; - // (undocumented) expires_at: number | undefined; - // (undocumented) get expires_in(): number | undefined; set expires_in(value: number | undefined); // (undocumented) static fromStorageString(storageString: string): User; // (undocumented) id_token: string | undefined; - // (undocumented) profile: UserProfile; // (undocumented) refresh_token: string | undefined; - // (undocumented) scope: string | undefined; - // (undocumented) get scopes(): string[]; - // (undocumented) session_state: string | undefined; - // (undocumented) readonly state: unknown | undefined; // (undocumented) token_type: string; @@ -728,18 +700,15 @@ export class User { // @public (undocumented) export type UserLoadedCallback = (user: User) => Promise | void; -// @public (undocumented) +// @public export class UserManager { constructor(settings: UserManagerSettings); - // (undocumented) clearStaleState(): Promise; // (undocumented) protected readonly _client: OidcClient; - // (undocumented) get events(): UserManagerEvents; // (undocumented) protected readonly _events: UserManagerEvents; - // (undocumented) getUser(): Promise; // Warning: (ae-forgotten-export) The symbol "IFrameNavigator" needs to be exported by the entry point index.d.ts // @@ -749,19 +718,16 @@ export class UserManager { protected _loadUser(): Promise; // (undocumented) protected readonly _logger: Logger; - // (undocumented) get metadataService(): MetadataService; // Warning: (ae-forgotten-export) The symbol "PopupNavigator" needs to be exported by the entry point index.d.ts // // (undocumented) protected readonly _popupNavigator: PopupNavigator; - // (undocumented) querySessionStatus(args?: QuerySessionStatusArgs): Promise; // Warning: (ae-forgotten-export) The symbol "RedirectNavigator" needs to be exported by the entry point index.d.ts // // (undocumented) protected readonly _redirectNavigator: RedirectNavigator; - // (undocumented) removeUser(): Promise; // (undocumented) revokeAccessToken(): Promise; @@ -773,7 +739,6 @@ export class UserManager { protected _revokeRefreshTokenInternal(refresh_token: string | undefined, required: boolean): Promise; // (undocumented) protected readonly _sessionMonitor: SessionMonitor | null; - // (undocumented) readonly settings: UserManagerSettingsStore; // Warning: (ae-forgotten-export) The symbol "IWindow" needs to be exported by the entry point index.d.ts // @@ -785,17 +750,11 @@ export class UserManager { protected _signinCallback(url: string | undefined, navigator: IFrameNavigator | PopupNavigator): Promise; // (undocumented) protected _signinEnd(url: string, verifySub?: string): Promise; - // (undocumented) signinPopup(args?: SigninPopupArgs): Promise; - // (undocumented) signinPopupCallback(url?: string): Promise; - // (undocumented) signinRedirect(args?: SigninRedirectArgs): Promise; - // (undocumented) signinRedirectCallback(url?: string): Promise; - // (undocumented) signinSilent(args?: SigninSilentArgs): Promise; - // (undocumented) signinSilentCallback(url?: string): Promise; // Warning: (ae-forgotten-export) The symbol "NavigateResponse" needs to be exported by the entry point index.d.ts // @@ -807,13 +766,9 @@ export class UserManager { signoutCallback(url?: string, keepOpen?: boolean): Promise; // (undocumented) protected _signoutEnd(url: string): Promise; - // (undocumented) signoutPopup(args?: SignoutPopupArgs): Promise; - // (undocumented) signoutPopupCallback(url?: string, keepOpen?: boolean): Promise; - // (undocumented) signoutRedirect(args?: SignoutRedirectArgs): Promise; - // (undocumented) signoutRedirectCallback(url?: string): Promise; // (undocumented) protected _signoutStart(args: CreateSignoutRequestArgs | undefined, handle: IWindow): Promise; @@ -821,9 +776,7 @@ export class UserManager { // // (undocumented) protected readonly _silentRenewService: SilentRenewService; - // (undocumented) startSilentRenew(): void; - // (undocumented) stopSilentRenew(): void; // (undocumented) storeUser(user: User | null): Promise; @@ -844,39 +797,27 @@ export class UserManager { // @public (undocumented) export class UserManagerEvents extends AccessTokenEvents { constructor(settings: UserManagerSettingsStore); - // (undocumented) addSilentRenewError(cb: SilentRenewErrorCallback): void; - // (undocumented) addUserLoaded(cb: UserLoadedCallback): void; - // (undocumented) addUserSessionChanged(cb: UserSessionChangedCallback): void; - // (undocumented) addUserSignedIn(cb: UserSignedInCallback): void; - // (undocumented) addUserSignedOut(cb: UserSignedOutCallback): void; - // (undocumented) addUserUnloaded(cb: UserUnloadedCallback): void; // (undocumented) load(user: User, raiseEvent?: boolean): void; - // (undocumented) + // @internal (undocumented) _raiseSilentRenewError(e: Error): void; - // (undocumented) + // @internal (undocumented) _raiseUserSessionChanged(): void; - // (undocumented) + // @internal (undocumented) _raiseUserSignedIn(): void; - // (undocumented) + // @internal (undocumented) _raiseUserSignedOut(): void; - // (undocumented) removeSilentRenewError(cb: SilentRenewErrorCallback): void; - // (undocumented) removeUserLoaded(cb: UserLoadedCallback): void; - // (undocumented) removeUserSessionChanged(cb: UserSessionChangedCallback): void; - // (undocumented) removeUserSignedIn(cb: UserSignedInCallback): void; - // (undocumented) removeUserSignedOut(cb: UserSignedOutCallback): void; - // (undocumented) removeUserUnloaded(cb: UserUnloadedCallback): void; // (undocumented) unload(): void; diff --git a/src/AccessTokenEvents.ts b/src/AccessTokenEvents.ts index 7a729acc4..93d33592f 100644 --- a/src/AccessTokenEvents.ts +++ b/src/AccessTokenEvents.ts @@ -65,16 +65,28 @@ export class AccessTokenEvents { this._expiredTimer.cancel(); } + /** + * Add callback: Raised prior to the access token expiring. + */ public addAccessTokenExpiring(cb: AccessTokenCallback): void { this._expiringTimer.addHandler(cb); } + /** + * Remove callback: Raised prior to the access token expiring. + */ public removeAccessTokenExpiring(cb: AccessTokenCallback): void { this._expiringTimer.removeHandler(cb); } + /** + * Add callback: Raised after the access token has expired. + */ public addAccessTokenExpired(cb: AccessTokenCallback): void { this._expiredTimer.addHandler(cb); } + /** + * Remove callback: Raised after the access token has expired. + */ public removeAccessTokenExpired(cb: AccessTokenCallback): void { this._expiredTimer.removeHandler(cb); } diff --git a/src/ErrorResponse.ts b/src/ErrorResponse.ts index 65c3e1670..1cc5d987a 100644 --- a/src/ErrorResponse.ts +++ b/src/ErrorResponse.ts @@ -15,7 +15,7 @@ export class ErrorResponse extends Error { public readonly session_state: string | undefined; - // custom "state", which can be used by a caller to have "data" round tripped + /** custom "state", which can be used by a caller to have "data" round tripped */ public state: unknown | undefined; public constructor(args: { diff --git a/src/OidcClient.ts b/src/OidcClient.ts index 43de7437b..c135d286d 100644 --- a/src/OidcClient.ts +++ b/src/OidcClient.ts @@ -21,7 +21,7 @@ export interface CreateSigninRequestArgs { response_type?: string; scope?: string; - // custom "state", which can be used by a caller to have "data" round tripped + /** custom "state", which can be used by a caller to have "data" round tripped */ state?: unknown; prompt?: string; @@ -48,6 +48,11 @@ export interface CreateSigninRequestArgs { export type CreateSignoutRequestArgs = Omit & { state?: unknown }; /** + * Provides the raw OIDC/OAuth2 protocol support for the authorization endpoint and the end session endpoint in the + * authorization server. It provides a bare-bones protocol implementation and is used by the UserManager class. + * Only use this class if you simply want protocol support without the additional management features of the + * UserManager class. + * * @public */ export class OidcClient { diff --git a/src/OidcClientSettings.ts b/src/OidcClientSettings.ts index 8aa217b38..ab92ca57c 100644 --- a/src/OidcClientSettings.ts +++ b/src/OidcClientSettings.ts @@ -7,9 +7,9 @@ import type { StateStore } from "./StateStore"; const DefaultResponseType = "code"; const DefaultScope = "openid"; -const DefaultClientAuthentication = "client_secret_post"; // The default value must be client_secret_basic, as explained in https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication +const DefaultClientAuthentication = "client_secret_post"; const DefaultResponseMode = "query"; -const DefaultStaleStateAgeInSeconds = 60 * 15; // seconds +const DefaultStaleStateAgeInSeconds = 60 * 15; const DefaultClockSkewInSeconds = 60 * 5; /** @@ -42,14 +42,30 @@ export interface OidcClientSettings { redirect_uri: string; /** The OIDC/OAuth2 post-logout redirect URI */ post_logout_redirect_uri?: string; + + /** + * Client authentication method that is used to authenticate when using the token endpoint (default: "client_secret_post") + * - "client_secret_basic": using the HTTP Basic authentication scheme + * - "client_secret_post": including the client credentials in the request body + * + * See https://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication + */ client_authentication?: string; + /** optional protocol param */ prompt?: string; + /** optional protocol param */ display?: string; + /** optional protocol param */ max_age?: number; + /** optional protocol param */ ui_locales?: string; + /** optional protocol param */ acr_values?: string; + /** optional protocol param */ resource?: string; + + /** optional protocol param (default: "query") */ response_mode?: "query" | "fragment"; /** Should OIDC protocol claims be removed from profile (default: true) */ @@ -61,12 +77,25 @@ export interface OidcClientSettings { /** The window of time (in seconds) to allow the current time to deviate when validating token's iat, nbf, and exp values (default: 300) */ clockSkewInSeconds?: number; userInfoJwtIssuer?: "ANY" | "OP" | string; + + /** + * Indicates if objects returned from the user info endpoint as claims (e.g. `address`) are merged into the claims from the id token as a single object. + * Otherwise, they are added to an array as distinct objects for the claim type. (default: false) + */ mergeClaims?: boolean; + /** + * Storage object used to persist interaction state (default: local storage). + * E.g. `stateStore: new WebStorageStateStore({ store: window.localStorage })` + */ stateStore?: StateStore; - /** An object containing additional query string parameters to be including in the authorization request */ + /** + * An object containing additional query string parameters to be including in the authorization request. + * E.g, when using Azure AD to obtain an access token an additional resource parameter is required. extraQueryParams: `{resource:"some_identifier"}` + */ extraQueryParams?: Record; + extraTokenParams?: Record; } diff --git a/src/SigninRequest.ts b/src/SigninRequest.ts index 4eafaa54d..420333b4e 100644 --- a/src/SigninRequest.ts +++ b/src/SigninRequest.ts @@ -34,7 +34,7 @@ export interface SigninRequestArgs { extraTokenParams?: Record; skipUserInfo?: boolean; - // custom "state", which can be used by a caller to have "data" round tripped + /** custom "state", which can be used by a caller to have "data" round tripped */ state_data?: unknown; } diff --git a/src/SigninResponse.ts b/src/SigninResponse.ts index d101108ef..0ced884d6 100644 --- a/src/SigninResponse.ts +++ b/src/SigninResponse.ts @@ -28,7 +28,7 @@ export class SigninResponse { public expires_at: number | undefined; // set by ResponseValidator - // custom "state", which can be used by a caller to have "data" round tripped + /** custom "state", which can be used by a caller to have "data" round tripped */ public state: unknown | undefined; // set by ResponseValidator diff --git a/src/SignoutResponse.ts b/src/SignoutResponse.ts index 5a2c256b2..b9043bdad 100644 --- a/src/SignoutResponse.ts +++ b/src/SignoutResponse.ts @@ -15,7 +15,7 @@ export class SignoutResponse { public error_uri: string | undefined; // set by ResponseValidator - // custom "state", which can be used by a caller to have "data" round tripped + /** custom "state", which can be used by a caller to have "data" round tripped */ public state: unknown | undefined; public constructor(url?: string) { diff --git a/src/State.ts b/src/State.ts index 895543814..1b3649983 100644 --- a/src/State.ts +++ b/src/State.ts @@ -12,7 +12,7 @@ export class State { public readonly created: number; public readonly request_type: string | undefined; - // custom "state", which can be used by a caller to have "data" round tripped + /** custom "state", which can be used by a caller to have "data" round tripped */ public readonly data: unknown | undefined; public constructor(args: { diff --git a/src/User.ts b/src/User.ts index 070adf77b..7600cab49 100644 --- a/src/User.ts +++ b/src/User.ts @@ -19,16 +19,27 @@ export interface UserProfile { */ export class User { public id_token: string | undefined; + + /** The session state value returned from the OIDC provider. */ public session_state: string | undefined; + + /** The access token returned from the OIDC provider. */ public access_token: string; + public refresh_token: string | undefined; public token_type: string; + + /** The scope returned from the OIDC provider. */ public scope: string | undefined; + + /** The claims represented by a combination of the `id_token` and the user info endpoint. */ public profile: UserProfile; + + /** The expires at returned from the OIDC provider. */ public expires_at: number | undefined; - // custom "state", which can be used by a caller to have "data" round tripped + /** custom "state", which can be used by a caller to have "data" round tripped */ public readonly state: unknown | undefined; public constructor(args: { @@ -49,6 +60,7 @@ export class User { this.state = args.state; } + /** Calculated number of seconds the access token has remaining. */ public get expires_in(): number | undefined { if (this.expires_at) { const now = Timer.getEpochTime(); @@ -64,6 +76,7 @@ export class User { } } + /** Calculated value indicating if the access token is expired. */ public get expired(): boolean | undefined { const expires_in = this.expires_in; if (expires_in !== undefined) { @@ -72,6 +85,7 @@ export class User { return undefined; } + /** Array representing the parsed values from the `scope`. */ public get scopes(): string[] { return (this.scope || "").split(" "); } diff --git a/src/UserManager.ts b/src/UserManager.ts index 2f8268118..bfacbf282 100644 --- a/src/UserManager.ts +++ b/src/UserManager.ts @@ -56,9 +56,13 @@ export type SignoutRedirectArgs = RedirectParams & ExtraSignoutRequestArgs; export type SignoutPopupArgs = PopupWindowParams & ExtraSignoutRequestArgs; /** + * Provides a higher level API for signing a user in, signing out, managing the user's claims returned from the OIDC provider, + * and managing an access token returned from the OIDC/OAuth2 provider. + * * @public */ export class UserManager { + /** Returns the settings used to configure the `UserManager`. */ public readonly settings: UserManagerSettingsStore; protected readonly _logger: Logger; @@ -101,14 +105,19 @@ export class UserManager { this._tokenClient = new TokenClient(this.settings, this.metadataService); } + /** Returns an object used to register for events raised by the `UserManager`. */ public get events(): UserManagerEvents { return this._events; } + /** Returns an object used to access the metadata configuration of the OIDC provider. */ public get metadataService(): MetadataService { return this._client.metadataService; } + /** + * Returns promise to load the `User` object for the currently authenticated user. + */ public async getUser(): Promise { const user = await this._loadUser(); if (user) { @@ -121,12 +130,18 @@ export class UserManager { return null; } + /** + * Returns promise to remove from any storage the currently authenticated user. + */ public async removeUser(): Promise { await this.storeUser(null); this._logger.info("removeUser: user removed from storage"); this._events.unload(); } + /** + * Returns promise to trigger a redirect of the current window to the authorization endpoint. + */ public async signinRedirect(args: SigninRedirectArgs = {}): Promise { const { redirectMethod, @@ -140,6 +155,9 @@ export class UserManager { this._logger.info("signinRedirect: successful"); } + /** + * Returns promise to process response from the authorization endpoint. The result of the promise is the authenticated `User`. + */ public async signinRedirectCallback(url = window.location.href): Promise { const user = await this._signinEnd(url); if (user.profile && user.profile.sub) { @@ -152,6 +170,9 @@ export class UserManager { return user; } + /** + * Returns promise to trigger a request (via a popup window) to the authorization endpoint. The result of the promise is the authenticated `User`. + */ public async signinPopup(args: SigninPopupArgs = {}): Promise { const { popupWindowFeatures, @@ -182,6 +203,9 @@ export class UserManager { return user; } + /** + * Returns promise to notify the opening window of response from the authorization endpoint. + */ public async signinPopupCallback(url?: string): Promise { try { await this._signinCallback(url, this._popupNavigator); @@ -192,6 +216,10 @@ export class UserManager { } } + /** + * Returns promise to trigger a silent request (via an iframe) to the authorization endpoint. + * The result of the promise is the authenticated `User`. + */ public async signinSilent(args: SigninSilentArgs = {}): Promise { const { silentRequestTimeoutInSeconds, @@ -288,6 +316,9 @@ export class UserManager { } } + /** + * Returns promise to notify the parent window of response from the authorization endpoint. + */ public async signinSilentCallback(url?: string): Promise { await this._signinCallback(url, this._iframeNavigator); this._logger.info("signinSilentCallback: successful"); @@ -322,6 +353,9 @@ export class UserManager { } } + /** + * Returns promise to query OP for user's current signin status. Returns object with session_state and subject identifier. + */ public async querySessionStatus(args: QuerySessionStatusArgs = {}): Promise { const { silentRequestTimeoutInSeconds, @@ -427,6 +461,9 @@ export class UserManager { await navigator.callback(url, false, delimiter); } + /** + * Returns promise to trigger a redirect of the current window to the end session endpoint. + */ public async signoutRedirect(args: SignoutRedirectArgs = {}): Promise { const { redirectMethod, @@ -440,12 +477,18 @@ export class UserManager { }, handle); this._logger.info("signoutRedirect: successful"); } + /** + * Returns promise to process response from the end session endpoint. + */ public async signoutRedirectCallback(url = window.location.href): Promise { const response = await this._signoutEnd(url); this._logger.info("signoutRedirectCallback: successful"); return response; } + /** + * Returns promise to trigger a redirect of a popup window window to the end session endpoint. + */ public async signoutPopup(args: SignoutPopupArgs = {}): Promise { const { popupWindowFeatures, @@ -468,6 +511,9 @@ export class UserManager { }, handle); this._logger.info("signoutPopup: successful"); } + /** + * Returns promise to process response from the end session endpoint from a popup window. + */ public async signoutPopupCallback(url?: string, keepOpen = false): Promise { const delimiter = "?"; await this._popupNavigator.callback(url, keepOpen, delimiter); @@ -574,10 +620,16 @@ export class UserManager { return true; } + /** + * Enables silent renew for the `UserManager`. + */ public startSilentRenew(): void { void this._silentRenewService.start(); } + /** + * Disables silent renew for the `UserManager`. + */ public stopSilentRenew(): void { this._silentRenewService.stop(); } @@ -609,6 +661,9 @@ export class UserManager { } } + /** + * Removes stale state entries in storage for incomplete authorize requests. + */ public async clearStaleState(): Promise { await this._client.clearStaleState(); } diff --git a/src/UserManagerEvents.ts b/src/UserManagerEvents.ts index 765a65d97..fc2897c21 100644 --- a/src/UserManagerEvents.ts +++ b/src/UserManagerEvents.ts @@ -67,59 +67,107 @@ export class UserManagerEvents extends AccessTokenEvents { this._userUnloaded.raise(); } + /** + * Add callback: Raised when a user session has been established (or re-established). + */ public addUserLoaded(cb: UserLoadedCallback): void { this._userLoaded.addHandler(cb); } + /** + * Remove callback: Raised when a user session has been established (or re-established). + */ public removeUserLoaded(cb: UserLoadedCallback): void { this._userLoaded.removeHandler(cb); } + /** + * Add callback: Raised when a user session has been terminated. + */ public addUserUnloaded(cb: UserUnloadedCallback): void { this._userUnloaded.addHandler(cb); } + /** + * Remove callback: Raised when a user session has been terminated. + */ public removeUserUnloaded(cb: UserUnloadedCallback): void { this._userUnloaded.removeHandler(cb); } + /** + * Add callback: Raised when the automatic silent renew has failed. + */ public addSilentRenewError(cb: SilentRenewErrorCallback): void { this._silentRenewError.addHandler(cb); } + /** + * Remove callback: Raised when the automatic silent renew has failed. + */ public removeSilentRenewError(cb: SilentRenewErrorCallback): void { this._silentRenewError.removeHandler(cb); } + /** + * @internal + */ public _raiseSilentRenewError(e: Error): void { this._logger.debug("_raiseSilentRenewError", e.message); this._silentRenewError.raise(e); } + /** + * Add callback: Raised when the user is signed in. + */ public addUserSignedIn(cb: UserSignedInCallback): void { this._userSignedIn.addHandler(cb); } + /** + * Remove callback: Raised when the user is signed in. + */ public removeUserSignedIn(cb: UserSignedInCallback): void { this._userSignedIn.removeHandler(cb); } + /** + * @internal + */ public _raiseUserSignedIn(): void { this._logger.debug("_raiseUserSignedIn"); this._userSignedIn.raise(); } + /** + * Add callback: Raised when the user's sign-in status at the OP has changed. + */ public addUserSignedOut(cb: UserSignedOutCallback): void { this._userSignedOut.addHandler(cb); } + /** + * Remove callback: Raised when the user's sign-in status at the OP has changed. + */ public removeUserSignedOut(cb: UserSignedOutCallback): void { this._userSignedOut.removeHandler(cb); } + /** + * @internal + */ public _raiseUserSignedOut(): void { this._logger.debug("_raiseUserSignedOut"); this._userSignedOut.raise(); } + /** + * Add callback: Raised when the user session changed (when `monitorSession` is set) + */ public addUserSessionChanged(cb: UserSessionChangedCallback): void { this._userSessionChanged.addHandler(cb); } + /** + * Remove callback: Raised when the user session changed (when `monitorSession` is set) + */ public removeUserSessionChanged(cb: UserSessionChangedCallback): void { this._userSessionChanged.removeHandler(cb); } + /** + * @internal + */ public _raiseUserSessionChanged(): void { this._logger.debug("_raiseUserSessionChanged"); this._userSessionChanged.raise(); diff --git a/src/UserManagerSettings.ts b/src/UserManagerSettings.ts index f92a91d74..3e8ae0452 100644 --- a/src/UserManagerSettings.ts +++ b/src/UserManagerSettings.ts @@ -14,8 +14,9 @@ export interface UserManagerSettings extends OidcClientSettings { /** The URL for the page containing the call to signinPopupCallback to handle the callback from the OIDC/OAuth2 */ popup_redirect_uri?: string; popup_post_logout_redirect_uri?: string; - /** The features parameter to window.open for the popup signin window. - * default: 'location=no,toolbar=no,width=500,height=500,left=100,top=100' + /** + * The features parameter to window.open for the popup signin window. + * (default: "location=no,toolbar=no,width=500,height=500,left=100,top=100;") */ popupWindowFeatures?: string; /** The target parameter to window.open for the popup signin window (default: "_blank") */ @@ -47,7 +48,10 @@ export interface UserManagerSettings extends OidcClientSettings { /** The number of seconds before an access token is to expire to raise the accessTokenExpiring event (default: 60) */ accessTokenExpiringNotificationTimeInSeconds?: number; - /** Storage object used to persist User for currently authenticated user (default: session storage) */ + /** + * Storage object used to persist User for currently authenticated user (default: session storage). + * E.g. `userStore: new WebStorageStateStore({ store: window.localStorage })` + */ userStore?: WebStorageStateStore; }