Skip to content
This repository has been archived by the owner on Jun 13, 2022. It is now read-only.

feat: Added generic support to SmartAuthProvider #173

Merged
merged 1 commit into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export {
SmartAuthRedirectQuerystring,
SmartAuthUrlQuerystring,
getAccessTokenFromClientCredentialFlow,
ClientCredentialsConfig,
AuthCodeConfig,
GrantFlow
} from "./smart-auth/index.js";

Expand Down
3 changes: 3 additions & 0 deletions src/smart-auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
27 changes: 8 additions & 19 deletions src/smart-auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export type SmartAuthScope = LaunchContext | Profile | Refresh | Resources

export type GrantFlow = "authorization_code" | "client_credentials"

export interface SmartAuthProvider {
export interface SmartAuthProvider<T = AuthCodeConfig | ClientCredentialsConfig> {
/** A name to label the provider */
name: string;
/** Client registration */
Expand All @@ -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. */
Expand All @@ -45,11 +45,11 @@ interface SmartAuthConfig {
tokenParams?: Record<string, any>;
}

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 */
Expand Down Expand Up @@ -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 {
Expand All @@ -135,10 +131,9 @@ function routeCase(value: string): string {
return value.toLowerCase().replace(/\s/g,'-');
}

const oauthPlugin: FastifyPluginCallback<SmartAuthProvider> = function (http, options, next) {
const oauthPlugin: FastifyPluginCallback<SmartAuthProvider<AuthCodeConfig>> = 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";
Expand Down Expand Up @@ -215,8 +210,6 @@ const oauthPlugin: FastifyPluginCallback<SmartAuthProvider> = function (http, op
getNewAccessTokenUsingRefreshToken,
generateAuthorizationUri
})


} catch (e) {
next(e as Error)
return
Expand All @@ -226,13 +219,9 @@ const oauthPlugin: FastifyPluginCallback<SmartAuthProvider> = function (http, op
}

export const getAccessTokenFromClientCredentialFlow = async (
smartAuthProvider: SmartAuthProvider,
smartAuthProvider: SmartAuthProvider<ClientCredentialsConfig>,
scope?: string[]
): Promise<AccessToken | undefined> => {
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: {
Expand Down
8 changes: 4 additions & 4 deletions test/smart-auth/idp.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SmartAuthProvider } from "../../src";
import { AuthCodeConfig, ClientCredentialsConfig, SmartAuthProvider } from "../../src";

export const AuthorizationCodeExample: SmartAuthProvider = {
export const AuthorizationCodeExample: SmartAuthProvider<AuthCodeConfig> = {
name: "idp",
client: {
id: "123",
Expand All @@ -17,7 +17,7 @@ export const AuthorizationCodeExample: SmartAuthProvider = {
},
};

export const badNameExample: SmartAuthProvider = {
export const badNameExample: SmartAuthProvider<AuthCodeConfig> = {
name: "Bad Name",
client: {
id: "123",
Expand All @@ -34,7 +34,7 @@ export const badNameExample: SmartAuthProvider = {
}
};

export const ClientCredentialsExample: SmartAuthProvider = {
export const ClientCredentialsExample: SmartAuthProvider<ClientCredentialsConfig> = {
name: 'smart-stub',
client: {
id: 'foo',
Expand Down