move IdentityClient.getBearerToken

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2022-02-08 12:25:27 +01:00
parent 9058bb1b5e
commit b3f3e42036
16 changed files with 119 additions and 65 deletions
+7 -1
View File
@@ -2,4 +2,10 @@
'@backstage/plugin-auth-backend': minor
---
Made `IdentityClient.listPublicKeys` private. It was only used in tests, and should not be part of the API surface of that class. The interface is marked as experimental, and therefore this is a breaking change without a deprecation period.
- Made `IdentityClient.listPublicKeys` private. It was only used in tests, and
should not be part of the API surface of that class.
- Removed the static `IdentityClient.getBearerToken`. It is now replaced by
`getBearerTokenFromAuthorizationHeader` from `@backstage/plugin-auth-node`.
Since the `IdentityClient` interface is marked as experimental, this is a
breaking change without a deprecation period.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-permission-backend': patch
'@backstage/plugin-search-backend': patch
---
Use `getBearerTokenFromAuthorizationHeader` from `@backstage/plugin-auth-node` instead of the deprecated `IdentityClient` method.
-3
View File
@@ -428,9 +428,6 @@ export type GoogleProviderOptions = {
export class IdentityClient {
constructor(options: { discovery: PluginEndpointDiscovery; issuer: string });
authenticate(token: string | undefined): Promise<BackstageIdentityResponse>;
static getBearerToken(
authorizationHeader: string | undefined,
): string | undefined;
}
// Warning: (ae-missing-release-tag) "microsoftEmailSignInResolver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+1
View File
@@ -30,6 +30,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/plugin-auth-node": "^0.0.0",
"@backstage/backend-common": "^0.10.7-next.0",
"@backstage/catalog-client": "^0.5.5",
"@backstage/catalog-model": "^0.9.10",
@@ -199,38 +199,6 @@ describe('IdentityClient', () => {
});
});
describe('getBearerToken', () => {
it('should return undefined on undefined input', async () => {
const token = IdentityClient.getBearerToken(undefined);
expect(token).toBeUndefined();
});
it('should return undefined on malformed input', async () => {
const token = IdentityClient.getBearerToken('malformed');
expect(token).toBeUndefined();
});
it('should return undefined on unexpected scheme', async () => {
const token = IdentityClient.getBearerToken('Basic token');
expect(token).toBeUndefined();
});
it('should return Bearer token', async () => {
const token = IdentityClient.getBearerToken('Bearer token');
expect(token).toEqual('token');
});
it('should return Bearer token despite extra space', async () => {
const token = IdentityClient.getBearerToken('Bearer \n token ');
expect(token).toEqual('token');
});
it('should return Bearer token despite unconventionial case', async () => {
const token = IdentityClient.getBearerToken('bEARER token');
expect(token).toEqual('token');
});
});
describe('listPublicKeys', () => {
const defaultServiceResponse: {
keys: JSONWebKey[];
@@ -84,20 +84,6 @@ export class IdentityClient {
return user;
}
/**
* Parses the given authorization header and returns
* the bearer token, or null if no bearer token is given
*/
static getBearerToken(
authorizationHeader: string | undefined,
): string | undefined {
if (typeof authorizationHeader !== 'string') {
return undefined;
}
const matches = authorizationHeader.match(/Bearer\s+(\S+)/i);
return matches?.[1];
}
/**
* Returns the public signing key matching the given jwt token,
* or null if no matching key was found
@@ -17,6 +17,7 @@
import express from 'express';
import { Logger } from 'winston';
import { AuthenticationError } from '@backstage/errors';
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
import {
AuthHandler,
SignInResolver,
@@ -26,7 +27,6 @@ import {
} from '../types';
import { CatalogIdentityClient } from '../../lib/catalog';
import { JWT } from 'jose';
import { IdentityClient } from '../../identity';
import { TokenIssuer } from '../../identity/types';
import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse';
@@ -156,7 +156,7 @@ export class Oauth2ProxyAuthProvider<JWTPayload>
private getResult(req: express.Request): OAuth2ProxyResult<JWTPayload> {
const authHeader = req.header(OAUTH2_PROXY_JWT_HEADER);
const jwt = IdentityClient.getBearerToken(authHeader);
const jwt = getBearerTokenFromAuthorizationHeader(authHeader);
if (!jwt) {
throw new AuthenticationError(
+3 -1
View File
@@ -4,5 +4,7 @@
```ts
// @public
export const COMMON_CONSTANT = 1;
export function getBearerTokenFromAuthorizationHeader(
authorizationHeader: unknown,
): string | undefined;
```
+2 -2
View File
@@ -19,12 +19,12 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.10.6",
"@backstage/backend-common": "^0.10.7-next.0",
"@backstage/config": "^0.1.13",
"winston": "^3.2.1"
},
"devDependencies": {
"@backstage/cli": "^0.13.1"
"@backstage/cli": "^0.13.2-next.0"
},
"files": [
"dist"
@@ -0,0 +1,50 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { getBearerTokenFromAuthorizationHeader } from './getBearerTokenFromAuthorizationHeader';
describe('getBearerToken', () => {
it('should return undefined on bad input', async () => {
expect(getBearerTokenFromAuthorizationHeader(undefined)).toBeUndefined();
expect(getBearerTokenFromAuthorizationHeader(7)).toBeUndefined();
expect(
getBearerTokenFromAuthorizationHeader('Bearer \n token'),
).toBeUndefined();
expect(
getBearerTokenFromAuthorizationHeader('Bearer token '),
).toBeUndefined();
});
it('should return undefined on malformed input', async () => {
const token = getBearerTokenFromAuthorizationHeader('malformed');
expect(token).toBeUndefined();
});
it('should return undefined on unexpected scheme', async () => {
const token = getBearerTokenFromAuthorizationHeader('Basic token');
expect(token).toBeUndefined();
});
it('should return Bearer token', async () => {
const token = getBearerTokenFromAuthorizationHeader('Bearer token');
expect(token).toEqual('token');
});
it('should return Bearer token despite unconventional case', async () => {
const token = getBearerTokenFromAuthorizationHeader('bEARER token');
expect(token).toEqual('token');
});
});
@@ -0,0 +1,37 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Parses the given authorization header and returns the bearer token, or
* undefined if no bearer token is given.
*
* @remarks
*
* This function is explicitly built to tolerate bad inputs safely, so you may
* call it directly with e.g. the output of `req.header('authorization')`
* without first checking that it exists.
*
* @public
*/
export function getBearerTokenFromAuthorizationHeader(
authorizationHeader: unknown,
): string | undefined {
if (typeof authorizationHeader !== 'string') {
return undefined;
}
const matches = authorizationHeader.match(/^Bearer[ ]+(\S+)$/i);
return matches?.[1];
}
+1 -6
View File
@@ -20,9 +20,4 @@
* @packageDocumentation
*/
/**
* Dummy.
*
* @public
*/
export const COMMON_CONSTANT = 1;
export { getBearerTokenFromAuthorizationHeader } from './getBearerTokenFromAuthorizationHeader';
+1
View File
@@ -23,6 +23,7 @@
"@backstage/config": "^0.1.13",
"@backstage/errors": "^0.2.0",
"@backstage/plugin-auth-backend": "^0.10.0-next.0",
"@backstage/plugin-auth-node": "^0.0.0",
"@backstage/plugin-permission-common": "^0.4.0",
"@backstage/plugin-permission-node": "^0.4.3-next.0",
"@types/express": "*",
@@ -27,6 +27,7 @@ import {
BackstageIdentityResponse,
IdentityClient,
} from '@backstage/plugin-auth-backend';
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
import {
AuthorizeResult,
AuthorizeDecision,
@@ -157,7 +158,9 @@ export async function createRouter(
req: Request<AuthorizeRequest>,
res: Response<AuthorizeResponse>,
) => {
const token = IdentityClient.getBearerToken(req.header('authorization'));
const token = getBearerTokenFromAuthorizationHeader(
req.header('authorization'),
);
const user = token ? await identity.authenticate(token) : undefined;
const parseResult = requestSchema.safeParse(req.body);
+1 -1
View File
@@ -24,7 +24,7 @@
"@backstage/config": "^0.1.13",
"@backstage/errors": "^0.2.0",
"@backstage/search-common": "^0.2.2",
"@backstage/plugin-auth-backend": "^0.10.0-next.0",
"@backstage/plugin-auth-node": "^0.0.0",
"@backstage/plugin-permission-common": "^0.4.0-next.0",
"@backstage/plugin-permission-node": "^0.4.3-next.0",
"@backstage/plugin-search-backend-node": "^0.4.5",
+4 -2
View File
@@ -22,7 +22,7 @@ import { errorHandler } from '@backstage/backend-common';
import { InputError } from '@backstage/errors';
import { Config } from '@backstage/config';
import { JsonObject, JsonValue } from '@backstage/types';
import { IdentityClient } from '@backstage/plugin-auth-backend';
import { getBearerTokenFromAuthorizationHeader } from '@backstage/plugin-auth-node';
import { PermissionAuthorizer } from '@backstage/plugin-permission-common';
import { DocumentTypeInfo, SearchResultSet } from '@backstage/search-common';
import { SearchEngine } from '@backstage/plugin-search-backend-node';
@@ -106,7 +106,9 @@ export async function createRouter(
}, pageCursor=${query.pageCursor ?? ''}`,
);
const token = IdentityClient.getBearerToken(req.header('authorization'));
const token = getBearerTokenFromAuthorizationHeader(
req.header('authorization'),
);
try {
const resultSet = await engine?.query(query, { token });