auth-backend: refactor identity APIs for forwards compatibility

This commit is contained in:
Patrik Oldsberg
2020-06-20 14:33:34 +02:00
parent 625f50725b
commit 48b6f9533c
6 changed files with 25 additions and 24 deletions
@@ -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');
});
};
@@ -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<DatabaseKeyStore> {
const { database } = options;
@@ -65,7 +65,7 @@ export class DatabaseKeyStore {
this.logger = logger.child({ service: 'key-store' });
}
async addPublicKey(key: PublicKey): Promise<void> {
async storeKey({ key }: { key: AnyJWK }): Promise<void> {
this.logger.info(`Storing public key ${key.kid}`);
await this.database<Row>(TABLE).insert({
@@ -74,7 +74,7 @@ export class DatabaseKeyStore {
});
}
async listPublicKeys(): Promise<PublicKey[]> {
async listKeys(): Promise<{ keys: AnyJWK[] }> {
const rows = await this.database<Row>(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[]) {
@@ -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<string> {
async issueToken(params: TokenParams): Promise<string> {
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;
})();
+1 -1
View File
@@ -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 });
});
+8 -6
View File
@@ -14,23 +14,25 @@
* limitations under the License.
*/
export interface PublicKey extends Record<string, string> {
use: 'sig' | 'enc';
export interface AnyJWK extends Record<string, string> {
use: 'sig';
alg: string;
kid: string;
kty: string;
}
export type KeyStore = {
addPublicKey(key: PublicKey): Promise<void>;
storeKey(params: { key: AnyJWK }): Promise<void>;
listPublicKeys(): Promise<PublicKey[]>;
listKeys(): Promise<{ keys: AnyJWK[] }>;
};
export type TokenParams = {
sub: string;
claims: {
sub: string;
};
};
export type TokenIssuer = {
issueToken(claims: TokenParams): Promise<string>;
issueToken(params: TokenParams): Promise<string>;
};
@@ -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);