UNPKG

ketting

Version:

Opinionated HATEOAS / Rest client.

263 lines (214 loc) 6.21 kB
import { FetchMiddleware } from './fetcher.js'; import { OAuth2Client, OAuth2Fetch } from '@badgateway/oauth2-client'; function oauth2mw(oauth2Options: OAuth2Options, token?: OAuth2Token): FetchMiddleware { console.warn('The OAuth2 middleware in Ketting is deprecated, and will be removed in the next major version of Ketting. You should upgrade to the OAuth2Fetch from the @badgateway/oauth2-client project'); // This code converts the old 'fetch-mw-oauth2' options format to the new // oauth2 client we use now, which is why it's a bit clunky. const newOptions: ConstructorParameters<typeof OAuth2Client>[0] = { clientId: oauth2Options.clientId, clientSecret: 'clientSecret' in oauth2Options ? oauth2Options.clientSecret : undefined, tokenEndpoint: oauth2Options.tokenEndpoint, }; const oauth2Client = new OAuth2Client(newOptions); const oauth2Fetch = new OAuth2Fetch({ client: oauth2Client, getNewToken: async() => { switch(oauth2Options.grantType) { case 'password' : return oauth2Client.password({ username: oauth2Options.userName, password: oauth2Options.password, scope: oauth2Options.scope, }); case 'client_credentials' : return oauth2Client.clientCredentials({ scope: oauth2Options.scope }); case 'authorization_code' : return oauth2Client.authorizationCode.getToken({ code: oauth2Options.code, codeVerifier: oauth2Options.codeVerifier, redirectUri: oauth2Options.redirectUri, }); case undefined: return null; } }, getStoredToken: (): OAuth2Token|null => { return token ?? null; }, storeToken: (token: OAuth2Token) => { if (oauth2Options.onTokenUpdate) { oauth2Options.onTokenUpdate(token); } }, onError: (err: Error) => { if (oauth2Options.onAuthError) { oauth2Options.onAuthError(err); } } }); return oauth2Fetch.mw(); } export default oauth2mw; /** * Token information */ export type OAuth2Token = { /** * OAuth2 Access Token */ accessToken: string; /** * When the Access Token expires. * * This is expressed as a unix timestamp in milliseconds. */ expiresAt: number | null; /** * OAuth2 refresh token */ refreshToken: string | null; }; /** * grant_type=password */ type PasswordGrantOptions = { grantType: 'password'; /** * OAuth2 client id */ clientId: string; /** * OAuth2 Client Secret */ clientSecret: string; /** * OAuth2 token endpoint */ tokenEndpoint: string; /** * List of OAuth2 scopes */ scope?: string[]; /** * Username to log in as */ userName: string; /** * Password */ password: string; /** * Callback to trigger when a new access/refresh token pair was obtained. */ onTokenUpdate?: (token: OAuth2Token) => void; /** * If authentication fails without a chance of recovery, this gets triggered. * * This is used for example when your resource server returns a 401, but only after * other attempts have been made to reauthenticate (such as a token refresh). */ onAuthError?: (error: Error) => void; }; /** * grant_type=client_credentials */ type ClientCredentialsGrantOptions = { grantType: 'client_credentials'; /** * OAuth2 client id */ clientId: string; /** * OAuth2 Client Secret */ clientSecret: string; /** * OAuth2 token endpoint */ tokenEndpoint: string; /** * List of OAuth2 scopes */ scope?: string[]; /** * Callback to trigger when a new access/refresh token pair was obtained. */ onTokenUpdate?: (token: OAuth2Token) => void; /** * If authentication fails without a chance of recovery, this gets triggered. * * This is used for example when your resource server returns a 401, but only after * other attempts have been made to reauthenticate (such as a token refresh). */ onAuthError?: (error: Error) => void; }; /** * grant_type=authorization_code */ type AuthorizationCodeGrantOptions = { grantType: 'authorization_code'; /** * OAuth2 client id */ clientId: string; /** * OAuth2 token endpoint */ tokenEndpoint: string; /** * The redirect_uri that was passed originally to the 'authorization' endpoint. * * This must be identical to the original string, as conforming OAuth2 servers * will validate this. */ redirectUri: string; /** * Code that was obtained from the authorization endpoint */ code: string; /** * Callback to trigger when a new access/refresh token pair was obtained. */ onTokenUpdate?: (token: OAuth2Token) => void; /** * If authentication fails without a chance of recovery, this gets triggered. * * This is used for example when your resource server returns a 401, but only after * other attempts have been made to reauthenticate (such as a token refresh). */ onAuthError?: (error: Error) => void; /** * When using PKCE, specify the previously generated code verifier here. */ codeVerifier?: string; }; /** * In case you obtained an access token and/or refresh token through different * means, you can not specify a grant_type and simply only specify an access * and refresh token. * * If a refresh or tokenEndpoint are not supplied, the token will never get refreshed. */ type RefreshOnlyGrantOptions = { grantType: undefined; /** * OAuth2 client id */ clientId: string; tokenEndpoint: string; /** * Callback to trigger when a new access/refresh token pair was obtained. */ onTokenUpdate?: (token: OAuth2Token) => void; /** * If authentication fails without a chance of recovery, this gets triggered. * * This is used for example when your resource server returns a 401, but only after * other attempts have been made to reauthenticate (such as a token refresh). */ onAuthError?: (error: Error) => void; }; export type OAuth2Options = PasswordGrantOptions | ClientCredentialsGrantOptions | AuthorizationCodeGrantOptions | RefreshOnlyGrantOptions;