fix some review comments

Signed-off-by: Brian Fletcher <brian@roadie.io>
This commit is contained in:
Brian Fletcher
2022-08-23 15:49:24 +01:00
parent 8f44eb1724
commit d102f14f9e
17 changed files with 1293 additions and 507 deletions
+11 -2
View File
@@ -29,7 +29,11 @@ export class DefaultIdentityClient implements IdentityApi {
authenticate(token: string | undefined): Promise<BackstageIdentityResponse>;
static create(options: IdentityClientOptions): DefaultIdentityClient;
// (undocumented)
getIdentity(req: Request_2): Promise<BackstageIdentityResponse | undefined>;
getIdentity({
request,
}: IdentityApiGetIdentityRequest): Promise<
BackstageIdentityResponse | undefined
>;
}
// @public
@@ -40,10 +44,15 @@ export function getBearerTokenFromAuthorizationHeader(
// @public
export interface IdentityApi {
getIdentity(
req: Request_2<any>,
options: IdentityApiGetIdentityRequest,
): Promise<BackstageIdentityResponse | undefined>;
}
// @public
export type IdentityApiGetIdentityRequest = {
request: Request_2<unknown>;
};
// @public @deprecated
export class IdentityClient {
// @deprecated
@@ -26,6 +26,7 @@ import { setupServer } from 'msw/node';
import { v4 as uuid } from 'uuid';
import { DefaultIdentityClient } from './DefaultIdentityClient';
import { IdentityApiGetIdentityRequest } from './types';
interface AnyJWK extends Record<string, string> {
use: 'sig';
@@ -366,4 +367,51 @@ describe('DefaultIdentityClient', () => {
dateSpy.mockClear();
});
});
describe('getIdentity', () => {
beforeEach(() => {
server.use(
rest.get(
`${mockBaseUrl}/.well-known/jwks.json`,
async (_, res, ctx) => {
const keys = await factory.listPublicKeys();
return res(ctx.json(keys));
},
),
);
});
it('returns the identity', async () => {
const token = await factory.issueToken({
claims: { sub: 'foo', ent: ['entity1', 'entity2'] },
});
const response = await client.getIdentity({
request: { headers: { authorization: `Bearer ${token}` } },
} as IdentityApiGetIdentityRequest);
expect(response).toEqual({
token: token,
identity: {
type: 'user',
userEntityRef: 'foo',
ownershipEntityRefs: ['entity1', 'entity2'],
},
});
});
it('given a corrupt the identity', async () => {
await expect(
client.getIdentity({
request: { headers: { authorization: `Bearer bad-token` } },
} as IdentityApiGetIdentityRequest),
).rejects.toThrow('Invalid JWT');
});
it('given no authorization header', async () => {
expect(
await client.getIdentity({
request: { headers: {} },
} as IdentityApiGetIdentityRequest),
).toEqual(undefined);
});
});
});
@@ -24,9 +24,11 @@ import {
jwtVerify,
} from 'jose';
import { GetKeyFunction } from 'jose/dist/types/types';
import { Request } from 'express';
import { BackstageIdentityResponse } from './types';
import {
BackstageIdentityResponse,
IdentityApiGetIdentityRequest,
} from './types';
import { getBearerTokenFromAuthorizationHeader, IdentityApi } from '.';
const CLOCK_MARGIN_S = 10;
@@ -75,14 +77,13 @@ export class DefaultIdentityClient implements IdentityApi {
: ['ES256'];
}
async getIdentity(req: Request) {
try {
return await this.authenticate(
getBearerTokenFromAuthorizationHeader(req.headers.authorization),
);
} catch (e) {
async getIdentity({ request }: IdentityApiGetIdentityRequest) {
if (!request.headers.authorization) {
return undefined;
}
return await this.authenticate(
getBearerTokenFromAuthorizationHeader(request.headers.authorization),
);
}
/**
+5 -3
View File
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BackstageIdentityResponse } from './types';
import { Request } from 'express';
import {
BackstageIdentityResponse,
IdentityApiGetIdentityRequest,
} from './types';
/**
* An identity client api to authenticate Backstage
@@ -30,6 +32,6 @@ export interface IdentityApi {
* The method throws an error if verification fails.
*/
getIdentity(
req: Request<any>,
options: IdentityApiGetIdentityRequest,
): Promise<BackstageIdentityResponse | undefined>;
}
+1
View File
@@ -29,4 +29,5 @@ export type {
BackstageIdentityResponse,
BackstageSignInResult,
BackstageUserIdentity,
IdentityApiGetIdentityRequest,
} from './types';
+11
View File
@@ -14,6 +14,8 @@
* limitations under the License.
*/
import { Request } from 'express';
/**
* A representation of a successful Backstage sign-in.
*
@@ -29,6 +31,15 @@ export interface BackstageSignInResult {
token: string;
}
/**
* Options to request the identity from a Backstage backend request
*
* @public
*/
export type IdentityApiGetIdentityRequest = {
request: Request<unknown>;
};
/**
* Response object containing the {@link BackstageUserIdentity} and the token
* from the authentication provider.