@lando/platformsh
Version:
A Lando plugin that provides a tight integration with Platform.sh.
329 lines (328 loc) • 13.4 kB
TypeScript
/**
* 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;