auth-backend: expire signing keys

This commit is contained in:
Patrik Oldsberg
2020-06-19 14:08:54 +02:00
parent 0a305d7f70
commit 90cc2ca615
@@ -18,6 +18,8 @@ import { TokenIssuer, TokenParams, KeyStore, PublicKey } from './types';
import { JSONWebKey, JWK, JWS } from 'jose';
import { Logger } from 'winston';
const KEY_DURATION_MS = 3600 * 1000;
type Options = {
issuer: string;
logger: Logger;
@@ -29,7 +31,8 @@ export class TokenFactory implements TokenIssuer {
private readonly logger: Logger;
private readonly keyStore: KeyStore;
private privateKey?: Promise<JSONWebKey>;
private keyExpiry?: number;
private privateKeyPromise?: Promise<JSONWebKey>;
constructor(options: Options) {
const { issuer, logger, keyStore } = options;
@@ -56,11 +59,16 @@ export class TokenFactory implements TokenIssuer {
}
private async getKey(): Promise<JSONWebKey> {
if (this.privateKey) {
return this.privateKey;
if (this.privateKeyPromise) {
if (this.keyExpiry && Date.now() < this.keyExpiry) {
return this.privateKeyPromise;
}
this.logger.info(`Signing key has expired, generating new key`);
delete this.privateKeyPromise;
}
this.privateKey = (async () => {
this.keyExpiry = Date.now() + KEY_DURATION_MS;
const promise = (async () => {
const dateStr = new Date().toISOString();
const randStr = Math.random().toString(36).slice(2, 6);
const kid = `key-${dateStr}-${randStr}`;
@@ -74,12 +82,18 @@ export class TokenFactory implements TokenIssuer {
return key as JSONWebKey;
})();
this.privateKeyPromise = promise;
try {
await this.privateKey;
// If we fail to generate a new key, we need to clear the state so that
// the next caller will try to generate another key.
await promise;
} catch (error) {
this.logger.error(`Failed to generate signing key, ${error}`);
this.logger.error(`Failed to generate new signing key, ${error}`);
delete this.keyExpiry;
delete this.privateKeyPromise;
}
return this.privateKey;
return promise;
}
}