diff --git a/.changeset/strong-paws-laugh.md b/.changeset/strong-paws-laugh.md new file mode 100644 index 0000000000..de39440e34 --- /dev/null +++ b/.changeset/strong-paws-laugh.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +export minimal typescript types for OIDC provider diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index a637118130..9ed523c678 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -47,6 +47,16 @@ export type AtlassianProviderOptions = { }; }; +// @public +export type AuthHandler = ( + input: AuthResult, +) => Promise; + +// @public +export type AuthHandlerResult = { + profile: ProfileInfo; +}; + // Warning: (ae-missing-release-tag) "AuthProviderFactory" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -258,7 +268,6 @@ export const createOAuth2Provider: ( options?: OAuth2ProviderOptions | undefined, ) => AuthProviderFactory; -// Warning: (ae-forgotten-export) The symbol "OidcProviderOptions" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "createOidcProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -534,6 +543,20 @@ export type OAuthState = { origin?: string; }; +// @public +export type OidcAuthResult = { + tokenset: TokenSet; + userinfo: UserinfoResponse; +}; + +// @public +export type OidcProviderOptions = { + authHandler?: AuthHandler; + signIn?: { + resolver?: SignInResolver; + }; +}; + // Warning: (ae-missing-release-tag) "oktaEmailSignInResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -606,6 +629,22 @@ export type SamlProviderOptions = { }; }; +// @public +export type SignInInfo = { + profile: ProfileInfo; + result: AuthResult; +}; + +// @public +export type SignInResolver = ( + info: SignInInfo, + context: { + tokenIssuer: TokenIssuer; + catalogIdentityClient: CatalogIdentityClient; + logger: Logger_2; + }, +) => Promise; + // Warning: (ae-missing-release-tag) "TokenIssuer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public @@ -637,8 +676,6 @@ export type WebMessageResponse = // Warnings were encountered during analysis: // // src/identity/types.d.ts:31:9 - (ae-forgotten-export) The symbol "AnyJWK" needs to be exported by the entry point index.d.ts -// src/providers/atlassian/provider.d.ts:37:5 - (ae-forgotten-export) The symbol "AuthHandler" needs to be exported by the entry point index.d.ts -// src/providers/atlassian/provider.d.ts:42:9 - (ae-forgotten-export) The symbol "SignInResolver" needs to be exported by the entry point index.d.ts // src/providers/aws-alb/provider.d.ts:77:5 - (ae-forgotten-export) The symbol "AwsAlbResult" needs to be exported by the entry point index.d.ts // src/providers/github/provider.d.ts:71:58 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag // src/providers/github/provider.d.ts:71:90 - (tsdoc-escape-greater-than) The ">" character should be escaped using a backslash to avoid confusion with an HTML tag diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 4ecbb98fd2..9589e97265 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -34,6 +34,10 @@ export type { AuthProviderRouteHandlers, AuthProviderFactoryOptions, AuthProviderFactory, + AuthHandler, + AuthHandlerResult, + SignInResolver, + SignInInfo, } from './types'; // These types are needed for a postMessage from the login pop-up diff --git a/plugins/auth-backend/src/providers/oidc/index.ts b/plugins/auth-backend/src/providers/oidc/index.ts index 20d014a783..39bd02928d 100644 --- a/plugins/auth-backend/src/providers/oidc/index.ts +++ b/plugins/auth-backend/src/providers/oidc/index.ts @@ -13,5 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +export type { OidcAuthResult, OidcProviderOptions } from './provider'; export { createOidcProvider } from './provider'; diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 158af6f830..fe4c042500 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -56,7 +56,11 @@ type OidcImpl = { client: Client; }; -type AuthResult = { +/** + * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) + * @public + */ +export type OidcAuthResult = { tokenset: TokenSet; userinfo: UserinfoResponse; }; @@ -66,8 +70,8 @@ export type Options = OAuthProviderOptions & { scope?: string; prompt?: string; tokenSignedResponseAlg?: string; - signInResolver?: SignInResolver; - authHandler: AuthHandler; + signInResolver?: SignInResolver; + authHandler: AuthHandler; tokenIssuer: TokenIssuer; catalogIdentityClient: CatalogIdentityClient; logger: Logger; @@ -78,8 +82,8 @@ export class OidcAuthProvider implements OAuthHandlers { private readonly scope?: string; private readonly prompt?: string; - private readonly signInResolver?: SignInResolver; - private readonly authHandler: AuthHandler; + private readonly signInResolver?: SignInResolver; + private readonly authHandler: AuthHandler; private readonly tokenIssuer: TokenIssuer; private readonly catalogIdentityClient: CatalogIdentityClient; private readonly logger: Logger; @@ -113,7 +117,7 @@ export class OidcAuthProvider implements OAuthHandlers { ): Promise<{ response: OAuthResponse; refreshToken?: string }> { const { strategy } = await this.implementation; const strategyResponse = await executeFrameHandlerStrategy< - AuthResult, + OidcAuthResult, PrivateInfo >(req, strategy); const { @@ -158,7 +162,7 @@ export class OidcAuthProvider implements OAuthHandlers { ( tokenset: TokenSet, userinfo: UserinfoResponse, - done: PassportDoneCallback, + done: PassportDoneCallback, ) => { if (typeof done !== 'function') { throw new Error( @@ -180,7 +184,7 @@ export class OidcAuthProvider implements OAuthHandlers { // Use this function to grab the user profile info from the token // Then populate the profile with it - private async handleResult(result: AuthResult): Promise { + private async handleResult(result: OidcAuthResult): Promise { const { profile } = await this.authHandler(result); const response: OAuthResponse = { providerInfo: { @@ -210,27 +214,37 @@ export class OidcAuthProvider implements OAuthHandlers { } } -export const oAuth2DefaultSignInResolver: SignInResolver = async ( - info, - ctx, -) => { - const { profile } = info; +export const oAuth2DefaultSignInResolver: SignInResolver = + async (info, ctx) => { + const { profile } = info; - if (!profile.email) { - throw new Error('Profile contained no email'); - } - const userId = profile.email.split('@')[0]; - const token = await ctx.tokenIssuer.issueToken({ - claims: { sub: userId, ent: [`user:default/${userId}`] }, - }); - return { id: userId, token }; -}; + if (!profile.email) { + throw new Error('Profile contained no email'); + } + const userId = profile.email.split('@')[0]; + const token = await ctx.tokenIssuer.issueToken({ + claims: { sub: userId, ent: [`user:default/${userId}`] }, + }); + return { id: userId, token }; + }; +/** + * OIDC provider callback options. An auth handler and a sign in resolver + * can be passed while creating a OIDC provider. + * + * authHandler : called after sign in was successful, a new object must be returned which includes a profile + * signInResolver: called after sign in was successful, expects to return a new {@link BackstageSignInResult} + * + * Both options are optional. There is fallback for authHandler where the default handler expect an e-mail explicitly + * otherwise it throws an error + * + * @public + */ export type OidcProviderOptions = { - authHandler?: AuthHandler; + authHandler?: AuthHandler; signIn?: { - resolver?: SignInResolver; + resolver?: SignInResolver; }; }; @@ -260,7 +274,7 @@ export const createOidcProvider = ( tokenIssuer, }); - const authHandler: AuthHandler = options?.authHandler + const authHandler: AuthHandler = options?.authHandler ? options.authHandler : async ({ userinfo }) => ({ profile: { @@ -271,7 +285,7 @@ export const createOidcProvider = ( }); const signInResolverFn = options?.signIn?.resolver ?? oAuth2DefaultSignInResolver; - const signInResolver: SignInResolver = info => + const signInResolver: SignInResolver = info => signInResolverFn(info, { catalogIdentityClient, tokenIssuer, diff --git a/plugins/auth-backend/src/providers/types.ts b/plugins/auth-backend/src/providers/types.ts index dafa52163c..ecb466c768 100644 --- a/plugins/auth-backend/src/providers/types.ts +++ b/plugins/auth-backend/src/providers/types.ts @@ -240,6 +240,10 @@ export type ProfileInfo = { picture?: string; }; +/** + * type of sign in information context, includes the profile information and authentication result which contains auth. related information + * @public + */ export type SignInInfo = { /** * The simple profile passed down for use in the frontend. @@ -252,6 +256,11 @@ export type SignInInfo = { result: AuthResult; }; +/** + * Sign in resolver type describes the function which handles the result of a successful authentication + * and it must return a valid {@link BackstageSignInResult} + * @public + */ export type SignInResolver = ( info: SignInInfo, context: { @@ -261,6 +270,10 @@ export type SignInResolver = ( }, ) => Promise; +/** + * The return type of authentication handler which must contain a valid profile information + * @public + */ export type AuthHandlerResult = { profile: ProfileInfo }; /** @@ -270,6 +283,8 @@ export type AuthHandlerResult = { profile: ProfileInfo }; * * Throwing an error in the function will cause the authentication to fail, making it * possible to use this function as a way to limit access to a certain group of users. + * + * @public */ export type AuthHandler = ( input: AuthResult,