backend-common: extract NoopTokenManager from ServerTokenManager

Signed-off-by: MT Lewis <mtlewis@users.noreply.github.com>
This commit is contained in:
MT Lewis
2021-11-26 12:01:11 +00:00
parent 5510cee039
commit 10d0109d2e
2 changed files with 56 additions and 15 deletions
@@ -14,6 +14,7 @@
* limitations under the License.
*/
import { ConfigReader } from '@backstage/config';
import { TokenManager } from './types';
import { ServerTokenManager } from './ServerTokenManager';
const emptyConfig = new ConfigReader({});
@@ -122,8 +123,40 @@ describe('ServerTokenManager', () => {
});
});
describe('ServerTokenManager.fromConfig', () => {
it('should throw if backend auth configuration is missing', () => {
expect(() =>
ServerTokenManager.fromConfig(new ConfigReader({})),
).toThrow();
});
it('should throw if no keys are included in the configuration', () => {
expect(() =>
ServerTokenManager.fromConfig(
new ConfigReader({
backend: { auth: { keys: [] } },
}),
),
).toThrow();
});
it('should throw if any key is missing a secret property', () => {
expect(() =>
ServerTokenManager.fromConfig(
new ConfigReader({
backend: {
auth: {
keys: [{ secret: '123' }, {}, { secret: '789' }],
},
},
}),
),
).toThrow();
});
});
describe('ServerTokenManager.noop', () => {
let noopTokenManager: ServerTokenManager;
let noopTokenManager: TokenManager;
beforeEach(() => {
noopTokenManager = ServerTokenManager.noop();
@@ -146,11 +179,11 @@ describe('ServerTokenManager', () => {
).resolves.not.toThrow();
});
it('should not accept signed tokens', async () => {
it('should accept signed tokens', async () => {
const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
await expect(
noopTokenManager.authenticate((await tokenManager.getToken()).token),
).rejects.toThrowError(/invalid server token/i);
).resolves.not.toThrow();
});
});
});
@@ -19,6 +19,14 @@ import { Config } from '@backstage/config';
import { AuthenticationError } from '@backstage/errors';
import { TokenManager } from './types';
class NoopTokenManager implements TokenManager {
async getToken() {
return { token: '' };
}
async authenticate() {}
}
/**
* Creates and validates tokens for use during backend-to-backend
* authentication.
@@ -26,12 +34,11 @@ import { TokenManager } from './types';
* @public
*/
export class ServerTokenManager implements TokenManager {
private readonly verificationKeys: JWKS.KeyStore | JWK.NoneKey;
private readonly signingKey: JWK.Key | JWK.NoneKey;
private readonly signingAlgorithm: string | undefined;
private readonly verificationKeys: JWKS.KeyStore;
private readonly signingKey: JWK.Key;
static noop() {
return new ServerTokenManager();
return new NoopTokenManager();
}
static fromConfig(config: Config) {
@@ -43,20 +50,21 @@ export class ServerTokenManager implements TokenManager {
}
private constructor(secrets?: string[]) {
if (secrets?.length) {
this.verificationKeys = new JWKS.KeyStore(
secrets.map(k => JWK.asKey({ kty: 'oct', k })),
if (!secrets?.length) {
throw new Error(
'No secrets provided when constructing ServerTokenManager',
);
this.signingKey = this.verificationKeys.all()[0];
this.signingAlgorithm = 'HS256';
} else {
this.verificationKeys = this.signingKey = JWK.None;
}
this.verificationKeys = new JWKS.KeyStore(
secrets.map(k => JWK.asKey({ kty: 'oct', k })),
);
this.signingKey = this.verificationKeys.all()[0];
}
async getToken(): Promise<{ token: string }> {
const jwt = JWT.sign({ sub: 'backstage-server' }, this.signingKey, {
algorithm: this.signingAlgorithm,
algorithm: 'HS256',
});
return { token: jwt };