Merge pull request #8396 from SDA-SE/feat/DOCKM-1134

feat: Add new authentication provider: OAuth2Proxy
This commit is contained in:
Fredrik Adelöw
2022-01-17 15:50:13 +01:00
committed by GitHub
26 changed files with 692 additions and 96 deletions
+29 -6
View File
@@ -62,6 +62,7 @@ export type Auth0ProviderOptions = {
// @public
export type AuthHandler<TAuthResult> = (
input: TAuthResult,
context: AuthResolverContext,
) => Promise<AuthHandlerResult>;
// @public
@@ -99,6 +100,13 @@ export interface AuthProviderRouteHandlers {
start(req: express.Request, res: express.Response): Promise<void>;
}
// @public
export type AuthResolverContext = {
tokenIssuer: TokenIssuer;
catalogIdentityClient: CatalogIdentityClient;
logger: Logger_2;
};
// Warning: (ae-missing-release-tag) "AuthResponse" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -270,6 +278,11 @@ export const createOAuth2Provider: (
options?: OAuth2ProviderOptions | undefined,
) => AuthProviderFactory;
// @public
export const createOauth2ProxyProvider: <JWTPayload>(
options: Oauth2ProxyProviderOptions<JWTPayload>,
) => AuthProviderFactory;
// 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)
@@ -436,6 +449,20 @@ export type OAuth2ProviderOptions = {
};
};
// @public
export type Oauth2ProxyProviderOptions<JWTPayload> = {
authHandler: AuthHandler<OAuth2ProxyResult<JWTPayload>>;
signIn: {
resolver: SignInResolver<OAuth2ProxyResult<JWTPayload>>;
};
};
// @public
export type OAuth2ProxyResult<JWTPayload> = {
fullProfile: JWTPayload;
accessToken: string;
};
// Warning: (ae-missing-release-tag) "OAuthAdapter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -664,11 +691,7 @@ export type SignInInfo<TAuthResult> = {
// @public
export type SignInResolver<TAuthResult> = (
info: SignInInfo<TAuthResult>,
context: {
tokenIssuer: TokenIssuer;
catalogIdentityClient: CatalogIdentityClient;
logger: Logger_2;
},
context: AuthResolverContext,
) => Promise<BackstageSignInResult>;
// Warning: (ae-missing-release-tag) "TokenIssuer" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -704,5 +727,5 @@ export type WebMessageResponse =
// 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/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:81:5 - (ae-forgotten-export) The symbol "StateEncoder" needs to be exported by the entry point index.d.ts
// src/providers/types.d.ts:88:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts
// src/providers/types.d.ts:98:5 - (ae-forgotten-export) The symbol "AuthProviderConfig" needs to be exported by the entry point index.d.ts
```
@@ -120,7 +120,12 @@ export class AtlassianAuthProvider implements OAuthHandlers {
}
private async handleResult(result: OAuthResult): Promise<OAuthResponse> {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -138,11 +143,7 @@ export class AtlassianAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -149,7 +149,12 @@ export class Auth0AuthProvider implements OAuthHandlers {
}
private async handleResult(result: OAuthResult) {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -167,11 +172,7 @@ export class Auth0AuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -182,17 +182,18 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers {
}
private async handleResult(result: AwsAlbResult): Promise<AwsAlbResponse> {
const { profile } = await this.authHandler(result);
const context = {
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
};
const { profile } = await this.authHandler(result, context);
const backstageIdentity = await this.signInResolver(
{
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
return {
@@ -174,7 +174,12 @@ export class BitbucketAuthProvider implements OAuthHandlers {
private async handleResult(result: BitbucketOAuthResult) {
result.fullProfile.avatarUrl =
result.fullProfile._json!.links!.avatar!.href;
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -192,11 +197,7 @@ export class BitbucketAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -71,16 +71,17 @@ export class GcpIapProvider implements AuthProviderRouteHandlers {
req.header(IAP_JWT_HEADER),
this.tokenValidator,
);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result);
const { profile } = await this.authHandler(result, context);
const backstageIdentity = await this.signInResolver(
{ profile, result },
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
const response: GcpIapResponse = {
@@ -152,7 +152,12 @@ export class GithubAuthProvider implements OAuthHandlers {
}
private async handleResult(result: GithubOAuthResult) {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const expiresInStr = result.params.expires_in;
const response: OAuthResponse = {
@@ -171,11 +176,7 @@ export class GithubAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -167,7 +167,12 @@ export class GitlabAuthProvider implements OAuthHandlers {
}
private async handleResult(result: OAuthResult): Promise<OAuthResponse> {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -185,11 +190,7 @@ export class GitlabAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -148,7 +148,12 @@ export class GoogleAuthProvider implements OAuthHandlers {
}
private async handleResult(result: OAuthResult) {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -166,11 +171,7 @@ export class GoogleAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -23,6 +23,7 @@ export * from './gitlab';
export * from './google';
export * from './microsoft';
export * from './oauth2';
export * from './oauth2-proxy';
export * from './oidc';
export * from './okta';
export * from './onelogin';
@@ -38,6 +39,7 @@ export type {
AuthProviderFactoryOptions,
AuthProviderFactory,
AuthHandler,
AuthResolverContext,
AuthHandlerResult,
SignInResolver,
SignInInfo,
@@ -143,7 +143,12 @@ export class MicrosoftAuthProvider implements OAuthHandlers {
const photo = await this.getUserPhoto(result.accessToken);
result.fullProfile.photos = photo ? [{ value: photo }] : undefined;
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -161,11 +166,7 @@ export class MicrosoftAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -0,0 +1,18 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { createOauth2ProxyProvider } from './provider';
export type { Oauth2ProxyProviderOptions, OAuth2ProxyResult } from './provider';
@@ -0,0 +1,214 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
jest.mock('jose', () => ({
JWT: {
decode: jest.fn(),
},
}));
jest.mock('@backstage/catalog-client');
import express from 'express';
import { JWT } from 'jose';
import { Logger } from 'winston';
import {
AuthHandler,
SignInResolver,
AuthProviderFactoryOptions,
} from '../types';
import { CatalogIdentityClient } from '../../lib/catalog';
import { TokenIssuer } from '../../identity/types';
import {
createOauth2ProxyProvider,
Oauth2ProxyAuthProvider,
Oauth2ProxyProviderOptions,
OAuth2ProxyResult,
OAUTH2_PROXY_JWT_HEADER,
} from './provider';
describe('Oauth2ProxyAuthProvider', () => {
const mockToken =
'eyblob.eyJzdWIiOiJqaW1teW1hcmt1bSIsImVudCI6WyJ1c2VyOmRlZmF1bHQvamltbXltYXJrdW0iXX0=.eyblob';
let provider: Oauth2ProxyAuthProvider<any>;
let logger: jest.Mocked<Logger>;
let signInResolver: jest.MockedFunction<
SignInResolver<OAuth2ProxyResult<any>>
>;
let authHandler: jest.MockedFunction<AuthHandler<OAuth2ProxyResult<any>>>;
let mockResponse: jest.Mocked<express.Response>;
let mockRequest: jest.Mocked<express.Request>;
let JWTMock: jest.Mocked<typeof JWT>;
beforeEach(() => {
jest.resetAllMocks();
JWTMock = JWT as jest.Mocked<typeof JWT>;
authHandler = jest.fn();
signInResolver = jest.fn();
logger = { error: jest.fn() } as unknown as jest.Mocked<Logger>;
mockResponse = {
status: jest.fn(),
end: jest.fn(),
json: jest.fn(),
} as unknown as jest.Mocked<express.Response>;
mockRequest = {
body: {},
header: jest.fn(),
} as unknown as jest.Mocked<express.Request>;
provider = new Oauth2ProxyAuthProvider<any>({
authHandler,
logger,
signInResolver,
catalogIdentityClient: {} as CatalogIdentityClient,
tokenIssuer: {} as TokenIssuer,
});
});
describe('frameHandler()', () => {
it('should do nothing and return undefined', async () => {
const result = await provider.frameHandler();
expect(result).toBeUndefined();
});
});
describe('start()', () => {
it('should do nothing and return undefined', async () => {
const result = await provider.start();
expect(result).toBeUndefined();
});
});
describe('refresh()', () => {
it('should throw an error when auth header is missing', async () => {
mockRequest.header.mockReturnValue(undefined);
await provider.refresh(mockRequest, mockResponse);
expect(mockResponse.status).toHaveBeenCalledWith(401);
});
it('should throw an error if the bearer token is invalid', async () => {
mockRequest.header.mockReturnValue('Basic asdf=');
await provider.refresh(mockRequest, mockResponse);
expect(mockResponse.status).toHaveBeenCalledWith(401);
});
it('should return if auth header is set and valid', async () => {
mockRequest.header.mockReturnValue(`Bearer token`);
authHandler.mockResolvedValue({
profile: {},
});
signInResolver.mockResolvedValue({
id: 'some-id',
token: mockToken,
});
await provider.refresh(mockRequest, mockResponse);
expect(mockRequest.header).toBeCalledWith(OAUTH2_PROXY_JWT_HEADER);
expect(JWTMock.decode).toHaveBeenCalledWith('token');
expect(mockResponse.json).toHaveBeenCalled();
});
it('should load profile from authHandler and backstage identity from signInResolver', async () => {
const decodedToken = {
oid: 'oid',
name: 'name',
upn: 'john.doe@example.com',
};
const profile = { displayName: 'some value' };
mockRequest.header.mockReturnValue(`Bearer token`);
signInResolver.mockResolvedValue({
id: 'some-id',
token: mockToken,
});
authHandler.mockResolvedValue({ profile: profile });
JWTMock.decode.mockReturnValue(decodedToken as any);
await provider.refresh(mockRequest, mockResponse);
expect(signInResolver).toHaveBeenCalledWith(
{
profile: profile,
result: {
accessToken: 'token',
fullProfile: decodedToken,
},
},
{ catalogIdentityClient: {}, logger, tokenIssuer: {} },
);
expect(mockResponse.json).toHaveBeenCalledWith({
backstageIdentity: {
id: 'some-id',
idToken: mockToken,
identity: {
ownershipEntityRefs: ['user:default/jimmymarkum'],
type: 'user',
userEntityRef: 'jimmymarkum',
},
token: mockToken,
},
profile: { displayName: 'some value' },
providerInfo: {
accessToken: 'token',
},
});
});
});
describe('createOauth2ProxyProvider()', () => {
beforeEach(() => {
mockRequest.header.mockReturnValue(`Bearer token`);
authHandler.mockResolvedValue({
profile: {},
});
signInResolver.mockResolvedValue({
id: 'some-id',
token: mockToken,
});
});
it('should create a valid provider', async () => {
const providerOptions = {
authHandler,
signIn: { resolver: signInResolver },
} as Oauth2ProxyProviderOptions<any>;
const factoryOptions = {
logger,
catalogApi: {},
tokenIssuer: {},
} as unknown as AuthProviderFactoryOptions;
const factory = createOauth2ProxyProvider(providerOptions);
const handler = factory(factoryOptions);
await handler.refresh!(mockRequest, mockResponse);
expect(mockRequest.header).toBeCalledWith(OAUTH2_PROXY_JWT_HEADER);
expect(JWTMock.decode).toHaveBeenCalledWith('token');
expect(mockResponse.json).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,199 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import express from 'express';
import { Logger } from 'winston';
import { AuthenticationError } from '@backstage/errors';
import {
AuthHandler,
SignInResolver,
AuthProviderFactory,
AuthProviderRouteHandlers,
AuthResponse,
} from '../types';
import { CatalogIdentityClient } from '../../lib/catalog';
import { JWT } from 'jose';
import { IdentityClient } from '../../identity';
import { TokenIssuer } from '../../identity/types';
import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse';
export const OAUTH2_PROXY_JWT_HEADER = 'X-OAUTH2-PROXY-ID-TOKEN';
/**
* JWT header extraction result, containing the raw value and the parsed JWT
* payload.
*
* @public
*/
export type OAuth2ProxyResult<JWTPayload> = {
/**
* Parsed and decoded JWT payload.
*/
fullProfile: JWTPayload;
/**
* Raw JWT token
*/
accessToken: string;
};
/**
* Options for the oauth2-proxy provider factory
*
* @public
*/
export type Oauth2ProxyProviderOptions<JWTPayload> = {
/**
* Configure an auth handler to generate a profile for the user.
*/
authHandler: AuthHandler<OAuth2ProxyResult<JWTPayload>>;
/**
* Configure sign-in for this provider, without it the provider can not be used to sign users in.
*/
signIn: {
/**
* Maps an auth result to a Backstage identity for the user.
*/
resolver: SignInResolver<OAuth2ProxyResult<JWTPayload>>;
};
};
interface Options<JWTPayload> {
logger: Logger;
signInResolver: SignInResolver<OAuth2ProxyResult<JWTPayload>>;
authHandler: AuthHandler<OAuth2ProxyResult<JWTPayload>>;
tokenIssuer: TokenIssuer;
catalogIdentityClient: CatalogIdentityClient;
}
export class Oauth2ProxyAuthProvider<JWTPayload>
implements AuthProviderRouteHandlers
{
private readonly logger: Logger;
private readonly catalogIdentityClient: CatalogIdentityClient;
private readonly signInResolver: SignInResolver<
OAuth2ProxyResult<JWTPayload>
>;
private readonly authHandler: AuthHandler<OAuth2ProxyResult<JWTPayload>>;
private readonly tokenIssuer: TokenIssuer;
constructor(options: Options<JWTPayload>) {
this.catalogIdentityClient = options.catalogIdentityClient;
this.logger = options.logger;
this.tokenIssuer = options.tokenIssuer;
this.signInResolver = options.signInResolver;
this.authHandler = options.authHandler;
}
frameHandler(): Promise<void> {
return Promise.resolve(undefined);
}
async refresh(req: express.Request, res: express.Response): Promise<void> {
try {
const result = this.getResult(req);
const response = await this.handleResult(result);
res.json(response);
} catch (e) {
this.logger.error(
`Exception occurred during ${OAUTH2_PROXY_JWT_HEADER} refresh`,
e,
);
res.status(401);
res.end();
}
}
start(): Promise<void> {
return Promise.resolve(undefined);
}
private async handleResult(
result: OAuth2ProxyResult<JWTPayload>,
): Promise<AuthResponse<{ accessToken: string }>> {
const ctx = {
logger: this.logger,
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
};
const { profile } = await this.authHandler(result, ctx);
const backstageSignInResult = await this.signInResolver(
{
result,
profile,
},
ctx,
);
return {
providerInfo: {
accessToken: result.accessToken,
},
backstageIdentity: prepareBackstageIdentityResponse(
backstageSignInResult,
),
profile,
};
}
private getResult(req: express.Request): OAuth2ProxyResult<JWTPayload> {
const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER);
const jwt = IdentityClient.getBearerToken(authHeader);
if (!jwt) {
throw new AuthenticationError(
`Missing or in incorrect format - Oauth2Proxy OIDC header: ${OAUTH2_PROXY_JWT_HEADER}`,
);
}
const decodedJWT = JWT.decode(jwt) as unknown as JWTPayload;
return {
fullProfile: decodedJWT,
accessToken: jwt,
};
}
}
/**
* Factory function for oauth2-proxy auth provider
*
* @public
*/
export const createOauth2ProxyProvider =
<JWTPayload>(
options: Oauth2ProxyProviderOptions<JWTPayload>,
): AuthProviderFactory =>
({ catalogApi, logger, tokenIssuer }) => {
const signInResolver = options.signIn.resolver;
const authHandler = options.authHandler;
const catalogIdentityClient = new CatalogIdentityClient({
catalogApi,
tokenIssuer,
});
return new Oauth2ProxyAuthProvider<JWTPayload>({
logger,
signInResolver,
authHandler,
tokenIssuer,
catalogIdentityClient,
});
};
@@ -15,4 +15,5 @@
*/
export { createOAuth2Provider } from './provider';
export type { OAuth2ProviderOptions } from './provider';
@@ -163,7 +163,12 @@ export class OAuth2AuthProvider implements OAuthHandlers {
}
private async handleResult(result: OAuthResult) {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -181,11 +186,7 @@ export class OAuth2AuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -182,7 +182,12 @@ 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: OidcAuthResult): Promise<OAuthResponse> {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
idToken: result.tokenset.id_token,
@@ -198,11 +203,7 @@ export class OidcAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -169,7 +169,12 @@ export class OktaAuthProvider implements OAuthHandlers {
}
private async handleResult(result: OAuthResult) {
const { profile } = await this._authHandler(result);
const context = {
logger: this._logger,
catalogIdentityClient: this._catalogIdentityClient,
tokenIssuer: this._tokenIssuer,
};
const { profile } = await this._authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -187,11 +192,7 @@ export class OktaAuthProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this._tokenIssuer,
catalogIdentityClient: this._catalogIdentityClient,
logger: this._logger,
},
context,
);
}
@@ -148,7 +148,12 @@ export class OneLoginProvider implements OAuthHandlers {
}
private async handleResult(result: OAuthResult) {
const { profile } = await this.authHandler(result);
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const response: OAuthResponse = {
providerInfo: {
@@ -166,11 +171,7 @@ export class OneLoginProvider implements OAuthHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
}
@@ -93,12 +93,18 @@ export class SamlAuthProvider implements AuthProviderRouteHandlers {
res: express.Response,
): Promise<void> {
try {
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { result } = await executeFrameHandlerStrategy<SamlAuthResult>(
req,
this.strategy,
);
const { profile } = await this.authHandler(result);
const { profile } = await this.authHandler(result, context);
const response: AuthResponse<{}> = {
profile,
@@ -111,11 +117,7 @@ export class SamlAuthProvider implements AuthProviderRouteHandlers {
result,
profile,
},
{
tokenIssuer: this.tokenIssuer,
catalogIdentityClient: this.catalogIdentityClient,
logger: this.logger,
},
context,
);
response.backstageIdentity =
+13 -5
View File
@@ -24,6 +24,17 @@ import { TokenIssuer } from '../identity/types';
import { OAuthStartRequest } from '../lib/oauth/types';
import { CatalogIdentityClient } from '../lib/catalog';
/**
* The context that is used for auth processing.
*
* @public
*/
export type AuthResolverContext = {
tokenIssuer: TokenIssuer;
catalogIdentityClient: CatalogIdentityClient;
logger: Logger;
};
export type AuthProviderConfig = {
/**
* The protocol://domain[:port] where the app is hosted. This is used to construct the
@@ -259,11 +270,7 @@ export type SignInInfo<TAuthResult> = {
*/
export type SignInResolver<TAuthResult> = (
info: SignInInfo<TAuthResult>,
context: {
tokenIssuer: TokenIssuer;
catalogIdentityClient: CatalogIdentityClient;
logger: Logger;
},
context: AuthResolverContext,
) => Promise<BackstageSignInResult>;
/**
@@ -289,6 +296,7 @@ export type AuthHandlerResult = { profile: ProfileInfo };
*/
export type AuthHandler<TAuthResult> = (
input: TAuthResult,
context: AuthResolverContext,
) => Promise<AuthHandlerResult>;
export type StateEncoder = (