diff --git a/plugins/auth-backend/migrations/20200619125845_init.js b/plugins/auth-backend/migrations/20200619125845_init.js index 6d06fd1125..e31731b037 100644 --- a/plugins/auth-backend/migrations/20200619125845_init.js +++ b/plugins/auth-backend/migrations/20200619125845_init.js @@ -34,10 +34,7 @@ exports.up = async function up(knex) { .notNullable() .defaultTo(knex.fn.now()) .comment('The creation time of the key'); - table - .string('key') - .notNullable() - .comment('The serialized public part of the signing key'); + table.string('key').notNullable().comment('The serialized signing key'); }); }; diff --git a/plugins/auth-backend/src/identity/DatabaseKeyStore.ts b/plugins/auth-backend/src/identity/DatabaseKeyStore.ts index b8247f066f..9d288b3624 100644 --- a/plugins/auth-backend/src/identity/DatabaseKeyStore.ts +++ b/plugins/auth-backend/src/identity/DatabaseKeyStore.ts @@ -18,7 +18,7 @@ import Knex from 'knex'; import path from 'path'; import { utc } from 'moment'; import { Logger } from 'winston'; -import { PublicKey } from './types'; +import { AnyJWK, KeyStore } from './types'; const migrationsDir = path.resolve( require.resolve('@backstage/plugin-auth-backend/package.json'), @@ -40,7 +40,7 @@ type Options = { keyDuration: number; }; -export class DatabaseKeyStore { +export class DatabaseKeyStore implements KeyStore { static async create(options: Options): Promise { const { database } = options; @@ -65,7 +65,7 @@ export class DatabaseKeyStore { this.logger = logger.child({ service: 'key-store' }); } - async addPublicKey(key: PublicKey): Promise { + async storeKey({ key }: { key: AnyJWK }): Promise { this.logger.info(`Storing public key ${key.kid}`); await this.database(TABLE).insert({ @@ -74,7 +74,7 @@ export class DatabaseKeyStore { }); } - async listPublicKeys(): Promise { + async listKeys(): Promise<{ keys: AnyJWK[] }> { const rows = await this.database(TABLE).select(); const [validRows, expiredRows] = this.splitExpiredRows(rows); @@ -83,7 +83,9 @@ export class DatabaseKeyStore { this.removeExpiredRows(expiredRows); } - return validRows.map(row => JSON.parse(row.key)); + return { + keys: validRows.map(row => JSON.parse(row.key)), + }; } private splitExpiredRows(rows: Row[]) { diff --git a/plugins/auth-backend/src/identity/TokenFactory.ts b/plugins/auth-backend/src/identity/TokenFactory.ts index f121f2522d..4d7ba0c7e6 100644 --- a/plugins/auth-backend/src/identity/TokenFactory.ts +++ b/plugins/auth-backend/src/identity/TokenFactory.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { TokenIssuer, TokenParams, KeyStore, PublicKey } from './types'; +import { TokenIssuer, TokenParams, KeyStore, AnyJWK } from './types'; import { JSONWebKey, JWK, JWS } from 'jose'; import { Logger } from 'winston'; import { v4 as uuid } from 'uuid'; @@ -45,11 +45,11 @@ export class TokenFactory implements TokenIssuer { this.keyDuration = options.keyDuration; } - async issueToken(claims: TokenParams): Promise { + async issueToken(params: TokenParams): Promise { const key = await this.getKey(); const iss = this.issuer; - const sub = claims.sub; + const sub = params.claims.sub; const aud = 'backstage'; const iat = (Date.now() / 1000) | 0; const exp = iat + 3600; @@ -79,9 +79,9 @@ export class TokenFactory implements TokenIssuer { alg: 'ES256', }); - await this.keyStore.addPublicKey( - (key.toJWK(false) as unknown) as PublicKey, - ); + await this.keyStore.storeKey({ + key: (key.toJWK(false) as unknown) as AnyJWK, + }); return key as JSONWebKey; })(); diff --git a/plugins/auth-backend/src/identity/router.ts b/plugins/auth-backend/src/identity/router.ts index 7ba0835ecd..c18fdb7bc8 100644 --- a/plugins/auth-backend/src/identity/router.ts +++ b/plugins/auth-backend/src/identity/router.ts @@ -50,7 +50,7 @@ export function createOidcRouter(options: Options) { router.get('/.well-known/jwks.json', async (_req, res) => { logger.info('request certs'); - const keys = await keyStore.listPublicKeys(); + const { keys } = await keyStore.listKeys(); res.json({ keys }); }); diff --git a/plugins/auth-backend/src/identity/types.ts b/plugins/auth-backend/src/identity/types.ts index 59221e8d81..c19f8e1ddf 100644 --- a/plugins/auth-backend/src/identity/types.ts +++ b/plugins/auth-backend/src/identity/types.ts @@ -14,23 +14,25 @@ * limitations under the License. */ -export interface PublicKey extends Record { - use: 'sig' | 'enc'; +export interface AnyJWK extends Record { + use: 'sig'; alg: string; kid: string; kty: string; } export type KeyStore = { - addPublicKey(key: PublicKey): Promise; + storeKey(params: { key: AnyJWK }): Promise; - listPublicKeys(): Promise; + listKeys(): Promise<{ keys: AnyJWK[] }>; }; export type TokenParams = { - sub: string; + claims: { + sub: string; + }; }; export type TokenIssuer = { - issueToken(claims: TokenParams): Promise; + issueToken(params: TokenParams): Promise; }; diff --git a/plugins/auth-backend/src/lib/OAuthProvider.ts b/plugins/auth-backend/src/lib/OAuthProvider.ts index d8d11d52a3..38065c3380 100644 --- a/plugins/auth-backend/src/lib/OAuthProvider.ts +++ b/plugins/auth-backend/src/lib/OAuthProvider.ts @@ -145,7 +145,7 @@ export class OAuthProvider implements AuthProviderRouteHandlers { } user.userIdToken = await this.options.tokenIssuer.issueToken({ - sub: user.profile.email, + claims: { sub: user.profile.email }, }); // post message back to popup if successful @@ -206,7 +206,7 @@ export class OAuthProvider implements AuthProviderRouteHandlers { ); refreshInfo.userIdToken = await this.options.tokenIssuer.issueToken({ - sub: refreshInfo.profile?.email, + claims: { sub: refreshInfo.profile?.email }, }); return res.send(refreshInfo);