UNPKG

@lando/platformsh

Version:

A Lando plugin that provides a tight integration with Platform.sh.

329 lines (328 loc) 13.4 kB
/** * The type of the configuration object used to create a `OAuth2PopupFlow` * * Each property has a JSDOC description to explain what it does. */ export interface OAuth2PopupFlowOptions<TokenPayload extends { exp: number; }> { /** * REQUIRED * The full URI of the authorization endpoint provided by the authorization server. * * e.g. `https://example.com/oauth/authorize` */ authorizationUri: string; /** * REQUIRED * The client ID of your application provided by the authorization server. * * This client ID is sent to the authorization server using `authorizationUrl` endpoint in the * query portion of the URL along with the other parameters. * This value will be URL encoded like so: * * `https://example.com/oauth/authorize?client_id=SOME_CLIENT_ID_VALUE...` */ clientId: string; /** * REQUIRED * The URI that the authorization server will to redirect after the user has been authenticated. * This redirect URI *must* be a URI from *your application* and it must also be registered with * the authorization server. Some authorities call this a "callback URLs" or "login URLs" etc. * * > e.g. `http://localhost:4200/redirect` for local testing * > * > or `https://my-application.com/redirect` for prod * * This redirect URI is sent to the authorization server using `authorizationUrl` endpoint in the * query portion of the URL along with the other parameters. * This value will be URL encoded like so: * * `https://example.com/oauth/authorize?redirect_URI=http%3A%2F%2Flocalhost%2Fredirect...` */ redirectUri: string; /** * REQUIRED * A list permission separated by spaces that is the scope of permissions your application is * requesting from the authorization server. If the user is logging in the first time, it may ask * them to approve those permission before authorizing your application. * * > e.g. `openid profile` * * The scopes are sent to the authorization server using `authorizationUrl` endpoint in the * query portion of the URL along with the other parameters. * This value will be URL encoded like so: * * `https://example.com/oauth/authorize?scope=openid%20profile...` */ scope: string; /** * OPTIONAL * `response_type` is an argument to be passed to the authorization server via the * `authorizationUri` endpoint in the query portion of the URL. * * Most implementations of oauth2 use the default value of `token` to tell the authorization * server to start the implicit grant flow but you may override that value with this option. * * For example, Auth0--an OAuth2 authority/authorization server--requires the value `id_token` * instead of `token` for the implicit flow. * * The response type is sent to the authorization server using `authorizationUrl` endpoint in the * query portion of the URL along with the other parameters. * This value will be URL encoded like so: * * `https://example.com/oauth/authorize?response_type=token...` */ responseType?: string; /** * OPTIONAL * The key used to save the token in the given storage. The default key is `token` so the token * would be persisted in `localStorage.getItem('token')` if `localStorage` was the configured * `Storage`. */ accessTokenStorageKey?: string; /** * OPTIONAL * During `handleRedirect`, the method will try to parse `window.location.hash` to an object using * `OAuth2PopupFlow.decodeUriToObject`. After that object has been decoded, this property * determines the key to use that will retrieve the token from that object. * * By default it is `access_token` but you you may need to change that e.g. Auth0 uses `id_token`. */ accessTokenResponseKey?: string; /** * OPTIONAL * The storage implementation of choice. It can be `localStorage` or `sessionStorage` or something * else. By default, this is `localStorage` and `localStorage` is the preferred `Storage`. */ storage?: Storage; /** * OPTIONAL * The `authenticated` method periodically checks `loggedIn()` and resolves when `loggedIn()` * returns `true`. * * This property is how long it will wait between checks. By default it is `200`. */ pollingTime?: number; /** * OPTIONAL * Some oauth authorities require additional parameters to be passed to the `authorizationUri` * URL in order for the implicit grant flow to work. * * For example: [Auth0--an OAuth2 authority/authorization server--requires the parameters * `nonce`][0] * be passed along with every call to the `authorizationUri`. You can do that like so: * * ```ts * const auth = new OAuth2PopupFlow({ * authorizationUri: 'https://example.com/oauth/authorize', * clientId: 'foo_client', * redirectUri: 'http://localhost:8080/redirect', * scope: 'openid profile', * // this can be a function or static object * additionalAuthorizationParameters: () => { * // in prod, consider something more cryptographic * const nonce = Math.floor(Math.random() * 1000).toString(); * localStorage.setItem('nonce', nonce); * return { nonce }; * // `nonce` will now be encoded in the URL like so: * // https://example.com/oauth/authorize?client_id=foo_client...nonce=1234 * }, * // the token returned by Auth0, has the `nonce` in the payload * // you can add this additional check now * tokenValidator: ({ payload }) => { * const storageNonce = parseInt(localStorage.getItem('nonce'), 10); * const payloadNonce = parseInt(payload.nonce, 10); * return storageNonce === payloadNonce; * }, * }); * ``` * * [0]: https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce */ additionalAuthorizationParameters?: (() => { [key: string]: string; }) | { [key: string]: string; }; /** * OPTIONAL * This function intercepts the `loggedIn` method and causes it to return early with `false` if * this function itself returns `false`. Use this function to validate claims in the token payload * or token. * * [For example: validating the `nonce`:][0] * * ```ts * const auth = new OAuth2PopupFlow({ * authorizationUri: 'https://example.com/oauth/authorize', * clientId: 'foo_client', * redirectUri: 'http://localhost:8080/redirect', * scope: 'openid profile', * // this can be a function or static object * additionalAuthorizationParameters: () => { * // in prod, consider something more cryptographic * const nonce = Math.floor(Math.random() * 1000).toString(); * localStorage.setItem('nonce', nonce); * return { nonce }; * // `nonce` will now be encoded in the URL like so: * // https://example.com/oauth/authorize?client_id=foo_client...nonce=1234 * }, * // the token returned by Auth0, has the `nonce` in the payload * // you can add this additional check now * tokenValidator: ({ payload }) => { * const storageNonce = parseInt(localStorage.getItem('nonce'), 10); * const payloadNonce = parseInt(payload.nonce, 10); * return storageNonce === payloadNonce; * }, * }); * ``` * * [0]: https://auth0.com/docs/api-auth/tutorials/nonce#generate-a-cryptographically-random-nonce */ tokenValidator?: (options: { payload: TokenPayload; token: string; }) => boolean; /** * OPTIONAL * A hook that runs in `tryLoginPopup` before any popup is opened. This function can return a * `Promise` and the popup will not open until it resolves. * * A typical use case would be to wait a certain amount of time before opening the popup to let * the user see why the popup is happening. */ beforePopup?: () => any | Promise<any>; /** * OPTIONAL * A hook that runs in `handleRedirect` that takes in the result of the hash payload from the * authorization server. Use this hook to grab more from the response or to debug the response * from the authorization URL. */ afterResponse?: (authorizationResponse: { [key: string]: string | undefined; }) => void; } export declare class OAuth2PopupFlow<TokenPayload extends { exp: number; }> implements EventTarget { authorizationUri: string; clientId: string; redirectUri: string; scope: string; responseType: string; accessTokenStorageKey: string; accessTokenResponseKey: string; storage: Storage; pollingTime: number; additionalAuthorizationParameters?: (() => { [key: string]: string; }) | { [key: string]: string; }; tokenValidator?: (options: { payload: TokenPayload; token: string; }) => boolean; beforePopup?: () => any | Promise<any>; afterResponse?: (authorizationResponse: { [key: string]: string | undefined; }) => void; private _eventListeners; constructor(options: OAuth2PopupFlowOptions<TokenPayload>); private get _rawToken(); private set _rawToken(value); private get _rawTokenPayload(); /** * A simple synchronous method that returns whether or not the user is logged in by checking * whether or not their token is present and not expired. */ loggedIn(): boolean; /** * Returns true only if there is a token in storage and that token is expired. Use this to method * in conjunction with `loggedIn` to display a message like "you need to *re*login" vs "you need * to login". */ tokenExpired(): boolean; /** * Deletes the token from the given storage causing `loggedIn` to return false on its next call. * Also dispatches `logout` event */ logout(): void; /** * Call this method in a route of the `redirectUri`. This method takes the value of the hash at * `window.location.hash` and attempts to grab the token from the URL. * * If the method was able to grab the token, it will return `'SUCCESS'` else it will return a * different string. */ handleRedirect(): "REDIRECT_URI_MISMATCH" | "FALSY_HASH" | "NO_HASH_MATCH" | "FALSY_TOKEN" | "SUCCESS"; /** * supported events are: * * 1. `logout`–fired when the `logout()` method is called and * 2. `login`–fired during the `tryLoginPopup()` method is called and succeeds */ addEventListener(type: string, listener: EventListenerOrEventListenerObject): void; /** * Use this to dispatch an event to the internal `EventTarget` */ dispatchEvent(event: Event): boolean; /** * Removes the event listener in target's event listener list with the same type, callback, and options. */ removeEventListener(type: string, listener: EventListenerOrEventListenerObject): void; /** * Tries to open a popup to login the user in. If the user is already `loggedIn()` it will * immediately return `'ALREADY_LOGGED_IN'`. If the popup fails to open, it will immediately * return `'POPUP_FAILED'` else it will wait for `loggedIn()` to be `true` and eventually * return `'SUCCESS'`. * * Also dispatches `login` event */ tryLoginPopup(): Promise<"SUCCESS" | "ALREADY_LOGGED_IN" | "POPUP_FAILED">; /** * A promise that does not resolve until `loggedIn()` is true. This uses the `pollingTime` * to wait until checking if `loggedIn()` is `true`. */ authenticated(): Promise<void>; /** * If the user is `loggedIn()`, the token will be returned immediate, else it will open a popup * and wait until the user is `loggedIn()` (i.e. a new token has been added). */ token(): Promise<string>; /** * If the user is `loggedIn()`, the token payload will be returned immediate, else it will open a * popup and wait until the user is `loggedIn()` (i.e. a new token has been added). */ tokenPayload(): Promise<TokenPayload>; /** * wraps `JSON.parse` and return `undefined` if the parsing failed */ static jsonParseOrUndefined<T = {}>(json: string): T | undefined; /** * wraps `setTimeout` in a `Promise` that resolves to `'TIMER'` */ static time(milliseconds: number): Promise<"TIMER">; /** * wraps `decodeURIComponent` and returns the original string if it cannot be decoded */ static decodeUri(str: string): string; /** * Encodes an object of strings to a URL * * `{one: 'two', buckle: 'shoes or something'}` ==> `one=two&buckle=shoes%20or%20something` */ static encodeObjectToUri(obj: { [key: string]: string; }): string; /** * Decodes a URL string to an object of string * * `one=two&buckle=shoes%20or%20something` ==> `{one: 'two', buckle: 'shoes or something'}` */ static decodeUriToObject(str: string): { [key: string]: string | undefined; }; } export default OAuth2PopupFlow;