auth-backend: migrate github provider to use resolver context

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-04-08 13:07:25 +02:00
parent 29e6a4af63
commit 9c8a2e2116
2 changed files with 22 additions and 93 deletions
@@ -15,9 +15,6 @@
*/
import { Profile as PassportProfile } from 'passport';
import { getVoidLogger } from '@backstage/backend-common';
import { TokenIssuer } from '../../identity/types';
import { CatalogIdentityClient } from '../../lib/catalog';
import {
GithubAuthProvider,
GithubOAuthResult,
@@ -26,6 +23,7 @@ import {
import * as helpers from '../../lib/passport/PassportStrategyHelper';
import { makeProfileInfo } from '../../lib/passport/PassportStrategyHelper';
import { OAuthStartRequest, encodeState } from '../../lib/oauth';
import { AuthResolverContext } from '../types';
const mockFrameHandler = jest.spyOn(
helpers,
@@ -38,25 +36,12 @@ const mockFrameHandler = jest.spyOn(
>;
describe('GithubAuthProvider', () => {
const tokenIssuer: TokenIssuer = {
listPublicKeys: jest.fn(),
async issueToken(params) {
return `token-for-${params.claims.sub}`;
},
};
const catalogIdentityClient = {
findUser: jest.fn(),
resolveCatalogMembership: async ({
entityRefs,
}: {
entityRefs: string[];
}) => entityRefs,
} as unknown as CatalogIdentityClient;
const provider = new GithubAuthProvider({
logger: getVoidLogger(),
catalogIdentityClient: catalogIdentityClient,
tokenIssuer: tokenIssuer as unknown as TokenIssuer,
resolverContext: {
signInWithCatalogUser: jest.fn(({ entityRef }) => ({
token: `token-for-user:${entityRef.name}`,
})),
} as unknown as AuthResolverContext,
signInResolver: githubUsernameEntityNameSignInResolver,
authHandler: async ({ fullProfile }) => ({
profile: makeProfileInfo(fullProfile),
@@ -96,8 +81,7 @@ describe('GithubAuthProvider', () => {
const expected = {
backstageIdentity: {
id: 'jimmymarkum',
token: 'token-for-user:default/jimmymarkum',
token: 'token-for-user:jimmymarkum',
},
providerInfo: {
accessToken: '19xasczxcm9n7gacn9jdgm19me',
@@ -142,8 +126,7 @@ describe('GithubAuthProvider', () => {
const expected = {
backstageIdentity: {
id: 'jimmymarkum',
token: 'token-for-user:default/jimmymarkum',
token: 'token-for-user:jimmymarkum',
},
providerInfo: {
accessToken: '19xasczxcm9n7gacn9jdgm19me',
@@ -186,8 +169,7 @@ describe('GithubAuthProvider', () => {
};
const expected = {
backstageIdentity: {
id: 'jimmymarkum',
token: 'token-for-user:default/jimmymarkum',
token: 'token-for-user:jimmymarkum',
},
providerInfo: {
accessToken: '19xasczxcm9n7gacn9jdgm19me',
@@ -230,8 +212,7 @@ describe('GithubAuthProvider', () => {
const expected = {
backstageIdentity: {
id: 'daveboyle',
token: 'token-for-user:default/daveboyle',
token: 'token-for-user:daveboyle',
},
providerInfo: {
accessToken:
@@ -276,8 +257,7 @@ describe('GithubAuthProvider', () => {
expect(response).toEqual({
response: {
backstageIdentity: {
id: 'daveboyle',
token: 'token-for-user:default/daveboyle',
token: 'token-for-user:daveboyle',
},
providerInfo: {
accessToken: 'a.b.c',
@@ -352,8 +332,7 @@ describe('GithubAuthProvider', () => {
expect(result).toEqual({
response: {
backstageIdentity: {
id: 'mockuser',
token: 'token-for-user:default/mockuser',
token: 'token-for-user:mockuser',
},
profile: {
displayName: 'Mocked User',
@@ -404,8 +383,7 @@ describe('GithubAuthProvider', () => {
expect(result).toEqual({
response: {
backstageIdentity: {
id: 'mockuser',
token: 'token-for-user:default/mockuser',
token: 'token-for-user:mockuser',
},
profile: {
displayName: 'Mocked User',
@@ -14,12 +14,7 @@
* limitations under the License.
*/
import {
DEFAULT_NAMESPACE,
stringifyEntityRef,
} from '@backstage/catalog-model';
import express from 'express';
import { Logger } from 'winston';
import { Profile as PassportProfile } from 'passport';
import { Strategy as GithubStrategy } from 'passport-github2';
import {
@@ -36,6 +31,7 @@ import {
AuthHandler,
SignInResolver,
StateEncoder,
AuthResolverContext,
} from '../types';
import {
OAuthAdapter,
@@ -46,8 +42,6 @@ import {
encodeState,
OAuthRefreshRequest,
} from '../../lib/oauth';
import { CatalogIdentityClient } from '../../lib/catalog';
import { TokenIssuer } from '../../identity';
const ACCESS_TOKEN_PREFIX = 'access-token.';
@@ -76,27 +70,21 @@ export type GithubAuthProviderOptions = OAuthProviderOptions & {
signInResolver?: SignInResolver<GithubOAuthResult>;
authHandler: AuthHandler<GithubOAuthResult>;
stateEncoder: StateEncoder;
tokenIssuer: TokenIssuer;
catalogIdentityClient: CatalogIdentityClient;
logger: Logger;
resolverContext: AuthResolverContext;
};
export class GithubAuthProvider implements OAuthHandlers {
private readonly _strategy: GithubStrategy;
private readonly signInResolver?: SignInResolver<GithubOAuthResult>;
private readonly authHandler: AuthHandler<GithubOAuthResult>;
private readonly tokenIssuer: TokenIssuer;
private readonly catalogIdentityClient: CatalogIdentityClient;
private readonly logger: Logger;
private readonly resolverContext: AuthResolverContext;
private readonly stateEncoder: StateEncoder;
constructor(options: GithubAuthProviderOptions) {
this.signInResolver = options.signInResolver;
this.authHandler = options.authHandler;
this.stateEncoder = options.stateEncoder;
this.tokenIssuer = options.tokenIssuer;
this.catalogIdentityClient = options.catalogIdentityClient;
this.logger = options.logger;
this.resolverContext = options.resolverContext;
this._strategy = new GithubStrategy(
{
clientID: options.clientId,
@@ -198,12 +186,7 @@ export class GithubAuthProvider implements OAuthHandlers {
}
private async handleResult(result: GithubOAuthResult) {
const context = {
logger: this.logger,
catalogIdentityClient: this.catalogIdentityClient,
tokenIssuer: this.tokenIssuer,
};
const { profile } = await this.authHandler(result, context);
const { profile } = await this.authHandler(result, this.resolverContext);
const expiresInStr = result.params.expires_in;
let expiresInSeconds =
@@ -217,7 +200,7 @@ export class GithubAuthProvider implements OAuthHandlers {
result,
profile,
},
context,
this.resolverContext,
);
// GitHub sessions last longer than Backstage sessions, so if we're using
@@ -254,23 +237,7 @@ export const githubUsernameEntityNameSignInResolver: SignInResolver<
throw new Error(`GitHub user profile does not contain a username`);
}
const entityRef = stringifyEntityRef({
kind: 'User',
namespace: DEFAULT_NAMESPACE,
name: userId,
});
const ownershipEntityRefs =
await ctx.catalogIdentityClient.resolveCatalogMembership({
entityRefs: [entityRef],
});
const token = await ctx.tokenIssuer.issueToken({
claims: {
sub: entityRef,
ent: ownershipEntityRefs,
},
});
return { id: userId, token };
return ctx.signInWithCatalogUser({ entityRef: { name: userId } });
};
/**
@@ -347,15 +314,7 @@ export const createGithubProvider = (options?: {
*/
stateEncoder?: StateEncoder;
}): AuthProviderFactory => {
return ({
providerId,
globalConfig,
config,
tokenIssuer,
tokenManager,
catalogApi,
logger,
}) =>
return ({ providerId, globalConfig, config, resolverContext }) =>
OAuthEnvironmentHandler.mapConfig(config, envConfig => {
const clientId = envConfig.getString('clientId');
const clientSecret = envConfig.getString('clientSecret');
@@ -376,11 +335,6 @@ export const createGithubProvider = (options?: {
customCallbackUrl ||
`${globalConfig.baseUrl}/${providerId}/handler/frame`;
const catalogIdentityClient = new CatalogIdentityClient({
catalogApi,
tokenManager,
});
const authHandler: AuthHandler<GithubOAuthResult> = options?.authHandler
? options.authHandler
: async ({ fullProfile }) => ({
@@ -402,16 +356,13 @@ export const createGithubProvider = (options?: {
authorizationUrl,
signInResolver: options?.signIn?.resolver,
authHandler,
tokenIssuer,
catalogIdentityClient,
stateEncoder,
logger,
resolverContext,
});
return OAuthAdapter.fromConfig(globalConfig, provider, {
persistScopes: true,
providerId,
tokenIssuer,
callbackUrl,
});
});