From 6d8bb0a4a6b19415cfee8b581d23f49fa89d587a Mon Sep 17 00:00:00 2001 From: Joshua Kelly Date: Wed, 15 Dec 2021 19:33:03 -0500 Subject: [PATCH] feat: Added generic support to SmartAuthProvider --- src/index.ts | 2 ++ src/smart-auth/README.md | 3 +++ src/smart-auth/index.ts | 27 ++++++++------------------- test/smart-auth/idp.ts | 8 ++++---- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/index.ts b/src/index.ts index d22b07b..44ee38f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,6 +15,8 @@ export { SmartAuthRedirectQuerystring, SmartAuthUrlQuerystring, getAccessTokenFromClientCredentialFlow, + ClientCredentialsConfig, + AuthCodeConfig, GrantFlow } from "./smart-auth/index.js"; diff --git a/src/smart-auth/README.md b/src/smart-auth/README.md index 52e95a4..ba013c4 100644 --- a/src/smart-auth/README.md +++ b/src/smart-auth/README.md @@ -130,6 +130,9 @@ Sero will pass your scoped parameters in its generated authorization URL for you * `SmartAuthUrlQuerystring` is a TS interface that types the Fastify route contraints for the auto-generated authorization URL starting point described above. You can customize scopes on a per request basis. * `SmartAuthRedirectQuerystringSchema` is the AJV schema definition that corresponds to `SmartAuthRedirectQuerystring`, and can be used in the Fastify runtime - see example for use * `getAccessTokenFromClientCredentialFlow` is a function to fetch a `client_credential` access token for a given `SmartAuthProvider`. It will also prioritize the passed in `scope: string[]` over the `smartAuthProvider.scope`, in case you need special scope(s) for this flow. This function is not decorated on the fastify server, so it can be called directly on a `SmartAuthProvider`. +* `ClientCredentialsConfig` a TS interface that types the auth configuration for the Client Credentials grant flow inside of a `SmartAuthProvider` +* `AuthCodeConfig` is a TS interface that types the auth configuration for the Authorization Code grant flow inside of a `SmartAuthProvider` +* `GrantFlow` is a TS union type for `"authorization_code"` and `"client_credentials"` ### Decorators diff --git a/src/smart-auth/index.ts b/src/smart-auth/index.ts index 70260d1..0053a08 100644 --- a/src/smart-auth/index.ts +++ b/src/smart-auth/index.ts @@ -22,7 +22,7 @@ export type SmartAuthScope = LaunchContext | Profile | Refresh | Resources export type GrantFlow = "authorization_code" | "client_credentials" -export interface SmartAuthProvider { +export interface SmartAuthProvider { /** A name to label the provider */ name: string; /** Client registration */ @@ -31,10 +31,10 @@ export interface SmartAuthProvider { secret: string; }; /** Auth related config */ - auth: AuthCodeConfig | ClientCredentialsConfig; + auth: T; } -interface SmartAuthConfig { +export interface SmartAuthConfig { /** Supported grant flow */ grantFlow: GrantFlow; /** String used to set the host to request the tokens to. Required. */ @@ -45,11 +45,11 @@ interface SmartAuthConfig { tokenParams?: Record; } -interface ClientCredentialsConfig extends SmartAuthConfig { +export interface ClientCredentialsConfig extends SmartAuthConfig { grantFlow: "client_credentials" }; -interface AuthCodeConfig extends SmartAuthConfig { +export interface AuthCodeConfig extends SmartAuthConfig { grantFlow: "authorization_code" scope: SmartAuthScope[]; /** An optional prefix to add to every route path */ @@ -115,10 +115,6 @@ export interface SmartAuthUrlQuerystring { } } -function supports(provider: SmartAuthProvider, flow: GrantFlow): boolean { - return provider.auth.grantFlow === flow -} - const defaultState = randomBytes(10).toString('hex') function generateState(): string { @@ -135,10 +131,9 @@ function routeCase(value: string): string { return value.toLowerCase().replace(/\s/g,'-'); } -const oauthPlugin: FastifyPluginCallback = function (http, options, next) { +const oauthPlugin: FastifyPluginCallback> = function (http, options, next) { const { name, client } = options; - supports(options, "authorization_code"); - const auth = options.auth as AuthCodeConfig; + const auth = options.auth; const { scope: defaultScope, redirect } = auth; const prefix = auth?.pathPrefix || "/smart"; @@ -215,8 +210,6 @@ const oauthPlugin: FastifyPluginCallback = function (http, op getNewAccessTokenUsingRefreshToken, generateAuthorizationUri }) - - } catch (e) { next(e as Error) return @@ -226,13 +219,9 @@ const oauthPlugin: FastifyPluginCallback = function (http, op } export const getAccessTokenFromClientCredentialFlow = async ( - smartAuthProvider: SmartAuthProvider, + smartAuthProvider: SmartAuthProvider, scope?: string[] ): Promise => { - if (!supports(smartAuthProvider, "client_credentials")) { - throw new Error(`SmartAuthProvider ${smartAuthProvider.name} does not support client_credentials - client_credentials must be explicitly set as a suppoedGrantFlow`) - } - const clientCredentialsOptions = { client: smartAuthProvider.client, auth: { diff --git a/test/smart-auth/idp.ts b/test/smart-auth/idp.ts index 27698ae..c5d10a8 100644 --- a/test/smart-auth/idp.ts +++ b/test/smart-auth/idp.ts @@ -1,6 +1,6 @@ -import { SmartAuthProvider } from "../../src"; +import { AuthCodeConfig, ClientCredentialsConfig, SmartAuthProvider } from "../../src"; -export const AuthorizationCodeExample: SmartAuthProvider = { +export const AuthorizationCodeExample: SmartAuthProvider = { name: "idp", client: { id: "123", @@ -17,7 +17,7 @@ export const AuthorizationCodeExample: SmartAuthProvider = { }, }; -export const badNameExample: SmartAuthProvider = { +export const badNameExample: SmartAuthProvider = { name: "Bad Name", client: { id: "123", @@ -34,7 +34,7 @@ export const badNameExample: SmartAuthProvider = { } }; -export const ClientCredentialsExample: SmartAuthProvider = { +export const ClientCredentialsExample: SmartAuthProvider = { name: 'smart-stub', client: { id: 'foo',