removed logacy, oauth, and some more provider code
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 { AuthResolverContext } from '@backstage/plugin-auth-node';
|
||||
import { AuthHandler } from '../../providers';
|
||||
import { OAuthResult } from '../oauth';
|
||||
import { PassportProfile } from '../passport/types';
|
||||
import { adaptLegacyOAuthHandler } from './adaptLegacyOAuthHandler';
|
||||
|
||||
describe('adaptLegacyOAuthHandler', () => {
|
||||
it('should pass through undefined', () => {
|
||||
expect(adaptLegacyOAuthHandler(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should convert an old auth handler to a new profile transform', () => {
|
||||
const authHandler: AuthHandler<OAuthResult> = jest.fn();
|
||||
const profileTransform = adaptLegacyOAuthHandler(authHandler);
|
||||
|
||||
profileTransform?.(
|
||||
{
|
||||
fullProfile: { id: 'id' } as PassportProfile,
|
||||
session: {
|
||||
accessToken: 'token',
|
||||
expiresInSeconds: 3,
|
||||
scope: 'sco pe',
|
||||
tokenType: 'bear',
|
||||
idToken: 'id-token',
|
||||
refreshToken: 'refresh-token',
|
||||
},
|
||||
},
|
||||
{ ctx: 'ctx' } as unknown as AuthResolverContext,
|
||||
);
|
||||
|
||||
expect(authHandler).toHaveBeenCalledWith(
|
||||
{
|
||||
fullProfile: { id: 'id' },
|
||||
accessToken: 'token',
|
||||
params: {
|
||||
scope: 'sco pe',
|
||||
id_token: 'id-token',
|
||||
expires_in: 3,
|
||||
token_type: 'bear',
|
||||
},
|
||||
},
|
||||
{ ctx: 'ctx' },
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
OAuthAuthenticatorResult,
|
||||
ProfileTransform,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { AuthHandler } from '../../providers';
|
||||
import { OAuthResult } from '../oauth';
|
||||
import { PassportProfile } from '../passport/types';
|
||||
|
||||
/** @internal */
|
||||
export function adaptLegacyOAuthHandler(
|
||||
authHandler?: AuthHandler<OAuthResult>,
|
||||
): ProfileTransform<OAuthAuthenticatorResult<PassportProfile>> | undefined {
|
||||
return (
|
||||
authHandler &&
|
||||
(async (result, ctx) =>
|
||||
authHandler(
|
||||
{
|
||||
fullProfile: result.fullProfile,
|
||||
accessToken: result.session.accessToken,
|
||||
params: {
|
||||
scope: result.session.scope,
|
||||
id_token: result.session.idToken,
|
||||
token_type: result.session.tokenType,
|
||||
expires_in: result.session.expiresInSeconds!,
|
||||
},
|
||||
},
|
||||
ctx,
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
AuthResolverContext,
|
||||
PassportProfile,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { adaptLegacyOAuthSignInResolver } from './adaptLegacyOAuthSignInResolver';
|
||||
|
||||
describe('adaptLegacyOAuthSignInResolver', () => {
|
||||
it('should pass through undefined', () => {
|
||||
expect(adaptLegacyOAuthSignInResolver(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should convert a legacy resolver to a new one', () => {
|
||||
const legacyResolver = jest.fn();
|
||||
|
||||
const newResolver = adaptLegacyOAuthSignInResolver(legacyResolver);
|
||||
|
||||
newResolver?.(
|
||||
{
|
||||
profile: { email: 'em@i.l' },
|
||||
result: {
|
||||
fullProfile: { id: 'id' } as PassportProfile,
|
||||
session: {
|
||||
accessToken: 'token',
|
||||
expiresInSeconds: 3,
|
||||
scope: 'sco pe',
|
||||
tokenType: 'bear',
|
||||
idToken: 'id-token',
|
||||
refreshToken: 'refresh-token',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ ctx: 'ctx' } as unknown as AuthResolverContext,
|
||||
);
|
||||
|
||||
expect(legacyResolver).toHaveBeenCalledWith(
|
||||
{
|
||||
profile: { email: 'em@i.l' },
|
||||
result: {
|
||||
fullProfile: { id: 'id' },
|
||||
accessToken: 'token',
|
||||
refreshToken: 'refresh-token',
|
||||
params: {
|
||||
scope: 'sco pe',
|
||||
id_token: 'id-token',
|
||||
expires_in: 3,
|
||||
token_type: 'bear',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ ctx: 'ctx' },
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
OAuthAuthenticatorResult,
|
||||
PassportProfile,
|
||||
SignInResolver,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { OAuthResult } from '../oauth';
|
||||
|
||||
/** @internal */
|
||||
export function adaptLegacyOAuthSignInResolver(
|
||||
signInResolver?: SignInResolver<OAuthResult>,
|
||||
): SignInResolver<OAuthAuthenticatorResult<PassportProfile>> | undefined {
|
||||
return (
|
||||
signInResolver &&
|
||||
(async (input, ctx) =>
|
||||
signInResolver(
|
||||
{
|
||||
profile: input.profile,
|
||||
result: {
|
||||
fullProfile: input.result.fullProfile,
|
||||
accessToken: input.result.session.accessToken,
|
||||
refreshToken: input.result.session.refreshToken,
|
||||
params: {
|
||||
scope: input.result.session.scope,
|
||||
id_token: input.result.session.idToken,
|
||||
token_type: input.result.session.tokenType,
|
||||
expires_in: input.result.session.expiresInSeconds!,
|
||||
},
|
||||
},
|
||||
},
|
||||
ctx,
|
||||
))
|
||||
);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
AuthResolverContext,
|
||||
PassportProfile,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { adaptOAuthSignInResolverToLegacy } from './adaptOAuthSignInResolverToLegacy';
|
||||
|
||||
describe('adaptOAuthSignInResolverToLegacy', () => {
|
||||
it('should pass through an empty object', () => {
|
||||
const legacyResolvers = adaptOAuthSignInResolverToLegacy({});
|
||||
expect(legacyResolvers).toEqual({});
|
||||
|
||||
// @ts-expect-error
|
||||
legacyResolvers.missing?.();
|
||||
});
|
||||
|
||||
it('should adapt a collection of sign-in resolvers', () => {
|
||||
const resolverA = jest.fn();
|
||||
const resolverB = jest.fn();
|
||||
|
||||
const legacyResolvers = adaptOAuthSignInResolverToLegacy({
|
||||
resolverA,
|
||||
resolverB,
|
||||
});
|
||||
|
||||
const legacyResolverA = legacyResolvers.resolverA();
|
||||
legacyResolverA(
|
||||
{
|
||||
profile: { email: 'em@i.l' },
|
||||
result: {
|
||||
fullProfile: { id: 'id' } as PassportProfile,
|
||||
accessToken: 'token',
|
||||
refreshToken: 'refresh-token',
|
||||
params: {
|
||||
scope: 'sco pe',
|
||||
id_token: 'id-token',
|
||||
expires_in: 3,
|
||||
token_type: 'bear',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ ctx: 'ctx' } as unknown as AuthResolverContext,
|
||||
);
|
||||
|
||||
expect(resolverA).toHaveBeenCalledWith(
|
||||
{
|
||||
profile: { email: 'em@i.l' },
|
||||
result: {
|
||||
fullProfile: { id: 'id' } as PassportProfile,
|
||||
session: {
|
||||
accessToken: 'token',
|
||||
expiresInSeconds: 3,
|
||||
scope: 'sco pe',
|
||||
tokenType: 'bear',
|
||||
idToken: 'id-token',
|
||||
refreshToken: 'refresh-token',
|
||||
},
|
||||
},
|
||||
},
|
||||
{ ctx: 'ctx' },
|
||||
);
|
||||
|
||||
expect(resolverB).not.toHaveBeenCalled();
|
||||
legacyResolvers.resolverB()(
|
||||
{ profile: {}, result: { params: {} } } as any,
|
||||
{} as any,
|
||||
);
|
||||
expect(resolverB).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 {
|
||||
OAuthAuthenticatorResult,
|
||||
PassportProfile,
|
||||
SignInResolver,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { OAuthResult } from '../oauth';
|
||||
|
||||
/** @internal */
|
||||
export function adaptOAuthSignInResolverToLegacy<
|
||||
TKeys extends string,
|
||||
>(resolvers: {
|
||||
[key in TKeys]: SignInResolver<OAuthAuthenticatorResult<PassportProfile>>;
|
||||
}): { [key in TKeys]: () => SignInResolver<OAuthResult> } {
|
||||
const legacyResolvers = {} as {
|
||||
[key in TKeys]: () => SignInResolver<OAuthResult>;
|
||||
};
|
||||
for (const name of Object.keys(resolvers) as TKeys[]) {
|
||||
const resolver = resolvers[name];
|
||||
legacyResolvers[name] = () => async (input, ctx) =>
|
||||
resolver(
|
||||
{
|
||||
profile: input.profile,
|
||||
result: {
|
||||
fullProfile: input.result.fullProfile,
|
||||
session: {
|
||||
accessToken: input.result.accessToken,
|
||||
expiresInSeconds: input.result.params.expires_in,
|
||||
scope: input.result.params.scope,
|
||||
idToken: input.result.params.id_token,
|
||||
tokenType: input.result.params.token_type ?? 'bearer',
|
||||
refreshToken: input.result.refreshToken,
|
||||
},
|
||||
},
|
||||
},
|
||||
ctx,
|
||||
);
|
||||
}
|
||||
return legacyResolvers;
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
export { adaptLegacyOAuthHandler } from './adaptLegacyOAuthHandler';
|
||||
export { adaptLegacyOAuthSignInResolver } from './adaptLegacyOAuthSignInResolver';
|
||||
export { adaptOAuthSignInResolverToLegacy } from './adaptOAuthSignInResolverToLegacy';
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 { OAuthEnvironmentHandler as _OAuthEnvironmentHandler } from '@backstage/plugin-auth-node';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export const OAuthEnvironmentHandler = _OAuthEnvironmentHandler;
|
||||
@@ -1,213 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 express from 'express';
|
||||
import {
|
||||
verifyNonce,
|
||||
encodeState,
|
||||
readState,
|
||||
defaultCookieConfigurer,
|
||||
} from './helpers';
|
||||
|
||||
describe('OAuthProvider Utils', () => {
|
||||
describe('encodeState', () => {
|
||||
it('should serialized values', () => {
|
||||
const state = {
|
||||
nonce: '123',
|
||||
env: 'development',
|
||||
origin: 'https://example.com',
|
||||
};
|
||||
|
||||
const encoded = encodeState(state);
|
||||
expect(encoded).toBe(
|
||||
Buffer.from(
|
||||
'nonce=123&env=development&origin=https%3A%2F%2Fexample.com',
|
||||
).toString('hex'),
|
||||
);
|
||||
|
||||
expect(readState(encoded)).toEqual(state);
|
||||
});
|
||||
|
||||
it('should not include undefined values', () => {
|
||||
const state = { nonce: '123', env: 'development', origin: undefined };
|
||||
|
||||
const encoded = encodeState(state);
|
||||
expect(encoded).toBe(
|
||||
Buffer.from('nonce=123&env=development').toString('hex'),
|
||||
);
|
||||
|
||||
expect(readState(encoded)).toEqual(state);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyNonce', () => {
|
||||
it('should throw error if cookie nonce missing', () => {
|
||||
const state = { nonce: 'NONCE', env: 'development' };
|
||||
const mockRequest = {
|
||||
cookies: {},
|
||||
query: {
|
||||
state: encodeState(state),
|
||||
},
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrow('Auth response is missing cookie nonce');
|
||||
});
|
||||
|
||||
it('should throw error if state nonce missing', () => {
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'providera-nonce': 'NONCE',
|
||||
},
|
||||
query: {},
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrow('OAuth state is invalid, missing env');
|
||||
});
|
||||
|
||||
it('should throw error if nonce mismatch', () => {
|
||||
const state = { nonce: 'NONCEB', env: 'development' };
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'providera-nonce': 'NONCEA',
|
||||
},
|
||||
query: {
|
||||
state: encodeState(state),
|
||||
},
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).toThrow('Invalid nonce');
|
||||
});
|
||||
|
||||
it('should not throw any error if nonce matches', () => {
|
||||
const state = { nonce: 'NONCE', env: 'development' };
|
||||
const mockRequest = {
|
||||
cookies: {
|
||||
'providera-nonce': 'NONCE',
|
||||
},
|
||||
query: {
|
||||
state: encodeState(state),
|
||||
},
|
||||
} as unknown as express.Request;
|
||||
expect(() => {
|
||||
verifyNonce(mockRequest, 'providera');
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultCookieConfigurer', () => {
|
||||
it('should set the correct domain and path for a base url', () => {
|
||||
expect(
|
||||
defaultCookieConfigurer({
|
||||
baseUrl: '',
|
||||
providerId: 'test-provider',
|
||||
callbackUrl: 'http://domain.org/auth',
|
||||
appOrigin: 'http://domain.org',
|
||||
}),
|
||||
).toMatchObject({
|
||||
domain: 'domain.org',
|
||||
path: '/auth/test-provider',
|
||||
secure: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the correct domain and path for a url containing a frame handler', () => {
|
||||
expect(
|
||||
defaultCookieConfigurer({
|
||||
baseUrl: '',
|
||||
providerId: 'test-provider',
|
||||
callbackUrl: 'http://domain.org/auth/test-provider/handler/frame',
|
||||
appOrigin: 'http://domain.org',
|
||||
}),
|
||||
).toMatchObject({
|
||||
domain: 'domain.org',
|
||||
path: '/auth/test-provider',
|
||||
secure: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set the secure flag if url is using https', () => {
|
||||
expect(
|
||||
defaultCookieConfigurer({
|
||||
baseUrl: '',
|
||||
providerId: 'test-provider',
|
||||
callbackUrl: 'https://domain.org/auth',
|
||||
appOrigin: 'http://domain.org',
|
||||
}),
|
||||
).toMatchObject({
|
||||
secure: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set sameSite to lax for https on the same domain', () => {
|
||||
expect(
|
||||
defaultCookieConfigurer({
|
||||
baseUrl: '',
|
||||
providerId: 'test-provider',
|
||||
callbackUrl: 'https://domain.org/auth',
|
||||
appOrigin: 'http://domain.org',
|
||||
}),
|
||||
).toMatchObject({
|
||||
sameSite: 'lax',
|
||||
secure: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set sameSite to lax for http on the same domain', () => {
|
||||
expect(
|
||||
defaultCookieConfigurer({
|
||||
baseUrl: '',
|
||||
providerId: 'test-provider',
|
||||
callbackUrl: 'http://domain.org/auth',
|
||||
appOrigin: 'http://domain.org',
|
||||
}),
|
||||
).toMatchObject({
|
||||
sameSite: 'lax',
|
||||
secure: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set sameSite to lax if not secure and on different domains', () => {
|
||||
expect(
|
||||
defaultCookieConfigurer({
|
||||
baseUrl: '',
|
||||
providerId: 'test-provider',
|
||||
callbackUrl: 'http://authdomain.org/auth',
|
||||
appOrigin: 'http://domain.org',
|
||||
}),
|
||||
).toMatchObject({
|
||||
sameSite: 'lax',
|
||||
secure: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set sameSite to none if secure and on different domains', () => {
|
||||
expect(
|
||||
defaultCookieConfigurer({
|
||||
baseUrl: '',
|
||||
providerId: 'test-provider',
|
||||
callbackUrl: 'https://authdomain.org/auth',
|
||||
appOrigin: 'http://domain.org',
|
||||
}),
|
||||
).toMatchObject({
|
||||
sameSite: 'none',
|
||||
secure: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 express from 'express';
|
||||
import {
|
||||
CookieConfigurer,
|
||||
OAuthState,
|
||||
decodeOAuthState,
|
||||
encodeOAuthState,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `decodeOAuthState` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export const readState = decodeOAuthState;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `encodeOAuthState` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export const encodeState = encodeOAuthState;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use inline logic to make sure the session and state nonce matches instead.
|
||||
*/
|
||||
export const verifyNonce = (req: express.Request, providerId: string) => {
|
||||
const cookieNonce = req.cookies[`${providerId}-nonce`];
|
||||
const state: OAuthState = readState(req.query.state?.toString() ?? '');
|
||||
const stateNonce = state.nonce;
|
||||
|
||||
if (!cookieNonce) {
|
||||
throw new Error('Auth response is missing cookie nonce');
|
||||
}
|
||||
if (stateNonce.length === 0) {
|
||||
throw new Error('Auth response is missing state nonce');
|
||||
}
|
||||
if (cookieNonce !== stateNonce) {
|
||||
throw new Error('Invalid nonce');
|
||||
}
|
||||
};
|
||||
|
||||
export const defaultCookieConfigurer: CookieConfigurer = ({
|
||||
callbackUrl,
|
||||
providerId,
|
||||
appOrigin,
|
||||
}) => {
|
||||
const { hostname: domain, pathname, protocol } = new URL(callbackUrl);
|
||||
const secure = protocol === 'https:';
|
||||
|
||||
// For situations where the auth-backend is running on a
|
||||
// different domain than the app, we set the SameSite attribute
|
||||
// to 'none' to allow third-party access to the cookie, but
|
||||
// only if it's in a secure context (https).
|
||||
let sameSite: ReturnType<CookieConfigurer>['sameSite'] = 'lax';
|
||||
if (new URL(appOrigin).hostname !== domain && secure) {
|
||||
sameSite = 'none';
|
||||
}
|
||||
|
||||
// If the provider supports callbackUrls, the pathname will
|
||||
// contain the complete path to the frame handler so we need
|
||||
// to slice off the trailing part of the path.
|
||||
const path = pathname.endsWith(`${providerId}/handler/frame`)
|
||||
? pathname.slice(0, -'/handler/frame'.length)
|
||||
: `${pathname}/${providerId}`;
|
||||
|
||||
return { domain, path, secure, sameSite };
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
export { OAuthEnvironmentHandler } from './OAuthEnvironmentHandler';
|
||||
export { encodeState, verifyNonce, readState } from './helpers';
|
||||
export type {
|
||||
OAuthHandlers,
|
||||
OAuthProviderInfo,
|
||||
OAuthProviderOptions,
|
||||
OAuthResponse,
|
||||
OAuthState,
|
||||
OAuthStartRequest,
|
||||
OAuthRefreshRequest,
|
||||
OAuthLogoutRequest,
|
||||
OAuthResult,
|
||||
} from './types';
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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 express from 'express';
|
||||
import { Profile as PassportProfile } from 'passport';
|
||||
import {
|
||||
BackstageSignInResult,
|
||||
ProfileInfo,
|
||||
OAuthState as _OAuthState,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { OAuthStartResponse } from '../../providers/types';
|
||||
|
||||
/**
|
||||
* Common options for passport.js-based OAuth providers
|
||||
*
|
||||
* @public
|
||||
* @deprecated No longer in use
|
||||
*/
|
||||
export type OAuthProviderOptions = {
|
||||
/**
|
||||
* Client ID of the auth provider.
|
||||
*/
|
||||
clientId: string;
|
||||
/**
|
||||
* Client Secret of the auth provider.
|
||||
*/
|
||||
clientSecret: string;
|
||||
/**
|
||||
* Callback URL to be passed to the auth provider to redirect to after the user signs in.
|
||||
*/
|
||||
callbackUrl: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `OAuthAuthenticatorResult<PassportProfile>` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type OAuthResult = {
|
||||
fullProfile: PassportProfile;
|
||||
params: {
|
||||
id_token?: string;
|
||||
scope: string;
|
||||
token_type?: string;
|
||||
expires_in: number;
|
||||
};
|
||||
accessToken: string;
|
||||
refreshToken?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `ClientAuthResponse` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type OAuthResponse = {
|
||||
profile: ProfileInfo;
|
||||
providerInfo: OAuthProviderInfo;
|
||||
backstageIdentity?: BackstageSignInResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type OAuthProviderInfo = {
|
||||
/**
|
||||
* An access token issued for the signed in user.
|
||||
*/
|
||||
accessToken: string;
|
||||
/**
|
||||
* (Optional) Id token issued for the signed in user.
|
||||
*/
|
||||
idToken?: string;
|
||||
/**
|
||||
* Expiry of the access token in seconds.
|
||||
*/
|
||||
expiresInSeconds?: number;
|
||||
/**
|
||||
* Scopes granted for the access token.
|
||||
*/
|
||||
scope: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type OAuthState = _OAuthState;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type OAuthStartRequest = express.Request<{}> & {
|
||||
scope: string;
|
||||
state: OAuthState;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type OAuthRefreshRequest = express.Request<{}> & {
|
||||
scope: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type OAuthLogoutRequest = express.Request<{}> & {
|
||||
refreshToken: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export interface OAuthHandlers {
|
||||
/**
|
||||
* Initiate a sign in request with an auth provider.
|
||||
*/
|
||||
start(req: OAuthStartRequest): Promise<OAuthStartResponse>;
|
||||
|
||||
/**
|
||||
* Handle the redirect from the auth provider when the user has signed in.
|
||||
*/
|
||||
handler(req: express.Request): Promise<{
|
||||
response: OAuthResponse;
|
||||
refreshToken?: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* (Optional) Given a refresh token and scope fetches a new access token from the auth provider.
|
||||
*/
|
||||
refresh?(req: OAuthRefreshRequest): Promise<{
|
||||
response: OAuthResponse;
|
||||
refreshToken?: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* (Optional) Sign out of the auth provider.
|
||||
*/
|
||||
logout?(req: OAuthLogoutRequest): Promise<void>;
|
||||
}
|
||||
@@ -17,18 +17,7 @@
|
||||
export { createOriginFilter, type ProviderFactories } from './router';
|
||||
|
||||
export type {
|
||||
AuthProviderConfig,
|
||||
AuthProviderRouteHandlers,
|
||||
AuthProviderFactory,
|
||||
AuthHandler,
|
||||
AuthResolverCatalogUserQuery,
|
||||
AuthResolverContext,
|
||||
AuthHandlerResult,
|
||||
SignInResolver,
|
||||
SignInInfo,
|
||||
CookieConfigurer,
|
||||
StateEncoder,
|
||||
AuthResponse,
|
||||
ProfileInfo,
|
||||
OAuthStartResponse,
|
||||
} from './types';
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* 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 { SignInResolver } from '@backstage/plugin-auth-node';
|
||||
|
||||
/**
|
||||
* A common sign-in resolver that looks up the user using the local part of
|
||||
* their email address as the entity name.
|
||||
*/
|
||||
export const commonByEmailLocalPartResolver: SignInResolver<unknown> = async (
|
||||
info,
|
||||
ctx,
|
||||
) => {
|
||||
const { profile } = info;
|
||||
|
||||
if (!profile.email) {
|
||||
throw new Error('Login failed, user profile does not contain an email');
|
||||
}
|
||||
const [localPart] = profile.email.split('@');
|
||||
|
||||
return ctx.signInWithCatalogUser({
|
||||
entityRef: { name: localPart },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* A common sign-in resolver that looks up the user using their email address
|
||||
* as email of the entity.
|
||||
*/
|
||||
export const commonByEmailResolver: SignInResolver<unknown> = async (
|
||||
info,
|
||||
ctx,
|
||||
) => {
|
||||
const { profile } = info;
|
||||
|
||||
if (!profile.email) {
|
||||
throw new Error('Login failed, user profile does not contain an email');
|
||||
}
|
||||
|
||||
return ctx.signInWithCatalogUser({
|
||||
filter: {
|
||||
'spec.profile.email': profile.email,
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -15,36 +15,9 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
AuthProviderConfig as _AuthProviderConfig,
|
||||
AuthProviderRouteHandlers as _AuthProviderRouteHandlers,
|
||||
AuthProviderFactory as _AuthProviderFactory,
|
||||
AuthResolverCatalogUserQuery as _AuthResolverCatalogUserQuery,
|
||||
AuthResolverContext as _AuthResolverContext,
|
||||
ClientAuthResponse as _ClientAuthResponse,
|
||||
CookieConfigurer as _CookieConfigurer,
|
||||
ProfileInfo as _ProfileInfo,
|
||||
SignInInfo as _SignInInfo,
|
||||
SignInResolver as _SignInResolver,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { OAuthStartRequest } from '../lib/oauth/types';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type AuthResolverCatalogUserQuery = _AuthResolverCatalogUserQuery;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type AuthResolverContext = _AuthResolverContext;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type CookieConfigurer = _CookieConfigurer;
|
||||
|
||||
/**
|
||||
* @public
|
||||
@@ -61,48 +34,6 @@ export type OAuthStartResponse = {
|
||||
status?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type AuthProviderConfig = _AuthProviderConfig;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type AuthProviderRouteHandlers = _AuthProviderRouteHandlers;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type AuthProviderFactory = _AuthProviderFactory;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import `ClientAuthResponse` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type AuthResponse<TProviderInfo> = _ClientAuthResponse<TProviderInfo>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type ProfileInfo = _ProfileInfo;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type SignInInfo<TAuthResult> = _SignInInfo<TAuthResult>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated import from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type SignInResolver<TAuthResult> = _SignInResolver<TAuthResult>;
|
||||
|
||||
/**
|
||||
* The return type of an authentication handler. Must contain valid profile
|
||||
* information.
|
||||
@@ -130,11 +61,3 @@ export type AuthHandler<TAuthResult> = (
|
||||
input: TAuthResult,
|
||||
context: _AuthResolverContext,
|
||||
) => Promise<AuthHandlerResult>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @deprecated Use `createOAuthRouteHandlers` from `@backstage/plugin-auth-node` instead
|
||||
*/
|
||||
export type StateEncoder = (
|
||||
req: OAuthStartRequest,
|
||||
) => Promise<{ encodedState: string }>;
|
||||
|
||||
Reference in New Issue
Block a user