diff --git a/plugins/auth-backend-module-oidc-provider/api-report.md b/plugins/auth-backend-module-oidc-provider/api-report.md index cf3335b13b..d96d9952a0 100644 --- a/plugins/auth-backend-module-oidc-provider/api-report.md +++ b/plugins/auth-backend-module-oidc-provider/api-report.md @@ -7,10 +7,10 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { BaseClient } from 'openid-client'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; -import { PassportOAuthResult } from '@backstage/plugin-auth-node'; -import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; import { Strategy } from 'openid-client'; +import { TokenSet } from 'openid-client'; +import { UserinfoResponse } from 'openid-client'; // @public (undocumented) const authModuleOidcProvider: () => BackendFeature; @@ -18,16 +18,24 @@ export default authModuleOidcProvider; // @public (undocumented) export const oidcAuthenticator: OAuthAuthenticator< - Promise<{ - helper: PassportOAuthAuthenticatorHelper; - client: BaseClient; + { initializedScope: string | undefined; initializedPrompt: string | undefined; - strategy: Strategy; - }>, - PassportProfile + promise: Promise<{ + helper: PassportOAuthAuthenticatorHelper; + client: BaseClient; + strategy: Strategy; + }>; + }, + OidcAuthResult >; +// @public +export type OidcAuthResult = { + tokenset: TokenSet; + userinfo: UserinfoResponse; +}; + // @public export namespace oidcSignInResolvers { const emailLocalPartMatchingUserEntityName: SignInResolverFactory< diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index b44f89e7c7..717083562c 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -281,81 +281,75 @@ describe('oidcAuthenticator', () => { }); it('exchanges authorization code for access token', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const accessToken = handlerResponse.session.accessToken; + const accessToken = authenticatorResult.session.accessToken; expect(accessToken).toEqual('accessToken'); }); it('exchanges authorization code for refresh token', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const refreshToken = handlerResponse.session.refreshToken; + const refreshToken = authenticatorResult.session.refreshToken; expect(refreshToken).toEqual('refreshToken'); }); it('returns granted scope', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const responseScope = handlerResponse.session.scope; + const responseScope = authenticatorResult.session.scope; expect(responseScope).toEqual('testScope'); }); it('returns a default session.tokentype field', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const tokenType = handlerResponse.session.tokenType; + const tokenType = authenticatorResult.session.tokenType; expect(tokenType).toEqual('bearer'); }); - it('returns defined fullProfile with picture and email', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + it('returns picture and email', async () => { + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const displayName = handlerResponse.fullProfile.displayName; - const email = handlerResponse.fullProfile.emails![0].value; - const picture = handlerResponse.fullProfile.photos![0].value; - expect(displayName).toEqual('Alice Adams'); - expect(email).toEqual('alice@test.com'); - expect(picture).toEqual('http://testPictureUrl/photo.jpg'); + expect(authenticatorResult).toMatchObject({ + fullProfile: { + userinfo: { + email: 'alice@test.com', + picture: 'http://testPictureUrl/photo.jpg', + name: 'Alice Adams', + }, + }, + }); }); - it('returns defined response with an idToken', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + it('returns idToken', async () => { + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - expect(handlerResponse).toMatchObject({ - fullProfile: { - displayName: 'Alice Adams', - id: 'test', - provider: 'oidc', - }, + expect(authenticatorResult).toMatchObject({ session: { - accessToken: 'accessToken', idToken, - refreshToken: 'refreshToken', - scope: 'testScope', - tokenType: 'bearer', }, }); expect( - Math.abs(handlerResponse.session.expiresInSeconds! - 3600), + Math.abs(authenticatorResult.session.expiresInSeconds! - 3600), ).toBeLessThan(5); }); diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index 97b0af8d7f..ba6bc9d142 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -23,15 +23,35 @@ import { } from 'openid-client'; import { createOAuthAuthenticator, + OAuthAuthenticatorResult, + PassportDoneCallback, + PassportHelpers, PassportOAuthAuthenticatorHelper, - PassportOAuthDoneCallback, + PassportOAuthPrivateInfo, } from '@backstage/plugin-auth-node'; +/** + * authentication result for the OIDC which includes the token set and user + * profile response + * @public + */ +export type OidcAuthResult = { + tokenset: TokenSet; + userinfo: UserinfoResponse; +}; + /** @public */ export const oidcAuthenticator = createOAuthAuthenticator({ - defaultProfileTransform: - PassportOAuthAuthenticatorHelper.defaultProfileTransform, - async initialize({ callbackUrl, config }) { + defaultProfileTransform: async ( + input: OAuthAuthenticatorResult, + ) => ({ + profile: { + email: input.fullProfile.userinfo.email, + picture: input.fullProfile.userinfo.picture, + displayName: input.fullProfile.userinfo.name, + }, + }), + initialize({ callbackUrl, config }) { const clientId = config.getString('clientId'); const clientSecret = config.getString('clientSecret'); const metadataUrl = config.getString('metadataUrl'); @@ -45,81 +65,53 @@ export const oidcAuthenticator = createOAuthAuthenticator({ const initializedScope = config.getOptionalString('scope'); const initializedPrompt = config.getOptionalString('prompt'); - const issuer = await Issuer.discover(metadataUrl); - const client = new issuer.Client({ - access_type: 'offline', // this option must be passed to provider to receive a refresh token - client_id: clientId, - client_secret: clientSecret, - redirect_uris: [customCallbackUrl || callbackUrl], - response_types: ['code'], - token_endpoint_auth_method: - tokenEndpointAuthMethod || 'client_secret_basic', - id_token_signed_response_alg: tokenSignedResponseAlg || 'RS256', - scope: initializedScope || '', + const promise = Issuer.discover(metadataUrl).then(issuer => { + const client = new issuer.Client({ + access_type: 'offline', // this option must be passed to provider to receive a refresh token + client_id: clientId, + client_secret: clientSecret, + redirect_uris: [customCallbackUrl || callbackUrl], + response_types: ['code'], + token_endpoint_auth_method: + tokenEndpointAuthMethod || 'client_secret_basic', + id_token_signed_response_alg: tokenSignedResponseAlg || 'RS256', + scope: initializedScope || '', + }); + + const strategy = new OidcStrategy( + { + client, + passReqToCallback: false, + }, + ( + tokenset: TokenSet, + userinfo: UserinfoResponse, + done: PassportDoneCallback, + ) => { + if (typeof done !== 'function') { + throw new Error( + 'OIDC IdP must provide a userinfo_endpoint in the metadata response', + ); + } + + done( + undefined, + { tokenset, userinfo }, + { refreshToken: tokenset.refresh_token }, + ); + }, + ); + + const helper = PassportOAuthAuthenticatorHelper.from(strategy); + return { helper, client, strategy }; }); - const strategy = new OidcStrategy( - { - client, - passReqToCallback: false, - }, - ( - tokenset: TokenSet, - userinfo: UserinfoResponse, - done: PassportOAuthDoneCallback, - ) => { - if (typeof done !== 'function') { - throw new Error( - 'OIDC IdP must provide a userinfo_endpoint in the metadata response', - ); - } - - const emails = userinfo.email ? [{ value: userinfo.email }] : undefined; - const photos = userinfo.picture - ? [{ value: userinfo.picture }] - : undefined; - const name = - userinfo.family_name && userinfo.given_name - ? { - familyName: userinfo.family_name, - givenName: userinfo.given_name, - } - : undefined; - - done( - undefined, - { - fullProfile: { - provider: 'oidc', - id: userinfo.sub, - displayName: userinfo.name!, - username: userinfo.preferred_username, - name, - emails, - photos, - }, - accessToken: tokenset.access_token!, - params: { - id_token: tokenset.id_token, - scope: tokenset.scope!, - expires_in: tokenset.expires_in!, - token_type: tokenset.token_type, - }, - }, - { - refreshToken: tokenset.refresh_token, - }, - ); - }, - ); - - const helper = PassportOAuthAuthenticatorHelper.from(strategy); - - return { helper, client, initializedScope, initializedPrompt, strategy }; + return { initializedScope, initializedPrompt, promise }; }, async start(input, ctx) { - const { initializedScope, initializedPrompt, helper, strategy } = await ctx; + const { initializedScope, initializedPrompt, promise } = ctx; + const { helper, strategy } = await promise; const options: Record = { scope: input.scope || initializedScope || 'openid profile email', state: input.state, @@ -140,12 +132,32 @@ export const oidcAuthenticator = createOAuthAuthenticator({ }); }, - async authenticate(input, ctx) { - return (await ctx).helper.authenticate(input); + async authenticate( + input, + ctx, + ): Promise> { + const { strategy } = await ctx.promise; + const { result, privateInfo } = + await PassportHelpers.executeFrameHandlerStrategy< + OidcAuthResult, + PassportOAuthPrivateInfo + >(input.req, strategy); + + return { + fullProfile: result, + session: { + accessToken: result.tokenset.access_token!, + tokenType: result.tokenset.token_type ?? 'bearer', + scope: result.tokenset.scope!, + expiresInSeconds: result.tokenset.expires_in, + idToken: result.tokenset.id_token, + refreshToken: privateInfo.refreshToken, + }, + }; }, async refresh(input, ctx) { - const { client } = await ctx; + const { client } = await ctx.promise; const tokenset = await client.refresh(input.refreshToken); if (!tokenset.access_token) { throw new Error('Refresh failed'); @@ -160,11 +172,7 @@ export const oidcAuthenticator = createOAuthAuthenticator({ reject(new Error('Refresh Failed')); } resolve({ - fullProfile: { - provider: 'oidc', - id: userinfo.sub, - displayName: userinfo.name!, - }, + fullProfile: { userinfo, tokenset }, session: { accessToken: tokenset.access_token!, tokenType: tokenset.token_type ?? 'bearer', diff --git a/plugins/auth-backend-module-oidc-provider/src/index.ts b/plugins/auth-backend-module-oidc-provider/src/index.ts index b14d350d4d..4e951382bb 100644 --- a/plugins/auth-backend-module-oidc-provider/src/index.ts +++ b/plugins/auth-backend-module-oidc-provider/src/index.ts @@ -21,5 +21,6 @@ */ export { oidcAuthenticator } from './authenticator'; +export type { OidcAuthResult } from './authenticator'; export { authModuleOidcProvider as default } from './module'; export { oidcSignInResolvers } from './resolvers'; diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index d097ae2881..16f257d17d 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -25,6 +25,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { OAuth2ProxyResult as OAuth2ProxyResult_2 } from '@backstage/plugin-auth-backend-module-oauth2-proxy-provider'; import { OAuthEnvironmentHandler as OAuthEnvironmentHandler_2 } from '@backstage/plugin-auth-node'; import { OAuthState as OAuthState_2 } from '@backstage/plugin-auth-node'; +import { OidcAuthResult } from '@backstage/plugin-auth-backend-module-oidc-provider'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { prepareBackstageIdentityResponse as prepareBackstageIdentityResponse_2 } from '@backstage/plugin-auth-node'; @@ -34,9 +35,7 @@ import { SignInInfo as SignInInfo_2 } from '@backstage/plugin-auth-node'; import { SignInResolver as SignInResolver_2 } from '@backstage/plugin-auth-node'; import { TokenManager } from '@backstage/backend-common'; import { TokenParams as TokenParams_2 } from '@backstage/plugin-auth-node'; -import { TokenSet } from 'openid-client'; import { UserEntity } from '@backstage/catalog-model'; -import { UserinfoResponse } from 'openid-client'; import { WebMessageResponse as WebMessageResponse_2 } from '@backstage/plugin-auth-node'; // @public @deprecated @@ -340,12 +339,6 @@ export type OAuthStartResponse = { // @public @deprecated (undocumented) export type OAuthState = OAuthState_2; -// @public @deprecated -export type OidcAuthResult = { - tokenset: TokenSet; - userinfo: UserinfoResponse; -}; - // @public @deprecated (undocumented) export const postMessageResponse: ( res: express.Response, @@ -564,10 +557,10 @@ export const providers: Readonly<{ create: ( options?: | { - authHandler?: AuthHandler | undefined; + authHandler?: AuthHandler | undefined; signIn?: | { - resolver: SignInResolver; + resolver: SignInResolver; } | undefined; } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index ca92d4c9dd..918e359610 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -75,7 +75,6 @@ "passport-auth0": "^1.4.3", "passport-bitbucket-oauth2": "^0.1.2", "passport-github2": "^0.1.12", - "passport-gitlab2": "^5.0.0", "passport-google-oauth20": "^2.0.0", "passport-microsoft": "^1.0.0", "passport-oauth2": "^1.6.1", diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 8173a7fbc3..c8140f864e 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -29,7 +29,6 @@ export type { } from './cloudflare-access'; export type { GithubOAuthResult } from './github'; export type { OAuth2ProxyResult } from './oauth2-proxy'; -export type { OidcAuthResult } from './oidc'; export type { SamlAuthResult } from './saml'; export type { GcpIapResult, GcpIapTokenInfo } from './gcp-iap'; diff --git a/plugins/auth-backend/src/providers/oidc/index.ts b/plugins/auth-backend/src/providers/oidc/index.ts index 6b2282411c..c4343b1547 100644 --- a/plugins/auth-backend/src/providers/oidc/index.ts +++ b/plugins/auth-backend/src/providers/oidc/index.ts @@ -15,4 +15,3 @@ */ export { oidc } from './provider'; -export type { OidcAuthResult } from './provider'; diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts new file mode 100644 index 0000000000..f2172a796d --- /dev/null +++ b/plugins/auth-backend/src/providers/oidc/provider.test.ts @@ -0,0 +1,169 @@ +/* + * Copyright 2024 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 { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { getVoidLogger } from '@backstage/backend-common'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { Config, ConfigReader } from '@backstage/config'; +import { + AuthProviderConfig, + AuthResolverContext, + CookieConfigurer, +} from '@backstage/plugin-auth-node'; +import express from 'express'; +import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { oidc } from './provider'; + +describe('oidc.create', () => { + const userinfo = { + sub: 'test', + iss: 'https://oidc.test', + aud: 'clientId', + nonce: 'foo', + }; + const server = setupServer(); + setupRequestMockHandlers(server); + + let publicKey: JWK; + let tokenset: object; + let providerFactoryOptions: { + providerId: string; + globalConfig: AuthProviderConfig; + config: Config; + logger: LoggerService; + resolverContext: AuthResolverContext; + baseUrl: string; + appUrl: string; + isOriginAllowed: (origin: string) => boolean; + cookieConfigurer?: CookieConfigurer; + }; + + beforeAll(async () => { + const keyPair = await generateKeyPair('RS256'); + const privateKey = await exportJWK(keyPair.privateKey); + publicKey = await exportJWK(keyPair.publicKey); + publicKey.alg = privateKey.alg = 'RS256'; + + tokenset = { + id_token: await new SignJWT({ + iat: Date.now(), + exp: Date.now() + 10000, + ...userinfo, + }) + .setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid }) + .sign(keyPair.privateKey), + access_token: 'accessToken', + }; + }); + + beforeEach(() => { + server.use( + rest.get( + 'https://oidc.test/.well-known/openid-configuration', + (_req, res, ctx) => + res( + ctx.json({ + issuer: 'https://oidc.test', + token_endpoint: 'https://oidc.test/oauth2/token', + userinfo_endpoint: 'https://oidc.test/idp/userinfo.openid', + jwks_uri: 'https://oidc.test/jwks.json', + }), + ), + ), + rest.post('https://oidc.test/oauth2/token', (_req, res, ctx) => + res(ctx.json(tokenset)), + ), + rest.get('https://oidc.test/jwks.json', async (_req, res, ctx) => + res(ctx.json({ keys: [{ ...publicKey }] })), + ), + rest.get( + 'https://oidc.test/idp/userinfo.openid', + async (_req, res, ctx) => res(ctx.json(userinfo)), + ), + ); + providerFactoryOptions = { + providerId: 'myoidc', + baseUrl: 'http://backstage.test/api/auth', + appUrl: 'http://backstage.test', + isOriginAllowed: _ => true, + globalConfig: { + baseUrl: 'http://backstage.test/api/auth', + appUrl: 'http://backstage.test', + isOriginAllowed: _ => true, + }, + config: new ConfigReader({ + development: { + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + }), + logger: getVoidLogger(), + resolverContext: { + issueToken: jest.fn(), + findCatalogUser: jest.fn(), + signInWithCatalogUser: jest.fn(), + }, + }; + }); + + it('invokes authHandler with tokenset and userinfo response', async () => { + const authHandler = jest.fn(); + const provider = oidc.create({ authHandler })(providerFactoryOptions); + const state = Buffer.from('nonce=foo&env=development').toString('hex'); + + await provider.frameHandler( + { + method: 'GET', + url: `http://backstage.test/api/auth/myoidc/handler/frame?code=blahblah&state=${state}`, + query: { state }, + cookies: { 'myoidc-nonce': 'foo' }, + session: { 'oidc:oidc.test': { state, nonce: 'foo' } }, + } as unknown as express.Request, + { setHeader: jest.fn(), end: jest.fn() } as unknown as express.Response, + ); + + expect(authHandler).toHaveBeenCalledWith( + { tokenset, userinfo }, + providerFactoryOptions.resolverContext, + ); + }); + + it('invokes sign-in resolver with tokenset and userinfo response', async () => { + const resolver = jest.fn(); + const provider = oidc.create({ signIn: { resolver } })( + providerFactoryOptions, + ); + const state = Buffer.from('nonce=foo&env=development').toString('hex'); + + await provider.frameHandler( + { + method: 'GET', + url: `http://backstage.test/api/auth/myoidc/handler/frame?code=blahblah&state=${state}`, + query: { state }, + cookies: { 'myoidc-nonce': 'foo' }, + session: { 'oidc:oidc.test': { state, nonce: 'foo' } }, + } as unknown as express.Request, + { setHeader: jest.fn(), end: jest.fn() } as unknown as express.Response, + ); + + expect(resolver).toHaveBeenCalledWith( + expect.objectContaining({ result: { tokenset, userinfo } }), + providerFactoryOptions.resolverContext, + ); + }); +}); diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index f6804265cd..9bc78c48d2 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -15,30 +15,23 @@ */ import { AuthHandler, SignInResolver } from '../types'; -import { OAuthResult } from '../../lib/oauth'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; -import { createOAuthProviderFactory } from '@backstage/plugin-auth-node'; import { - adaptLegacyOAuthHandler, - adaptLegacyOAuthSignInResolver, -} from '../../lib/legacy'; -import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider'; -import { TokenSet, UserinfoResponse } from 'openid-client'; + createOAuthProviderFactory, + AuthResolverContext, + BackstageSignInResult, + OAuthAuthenticatorResult, + SignInInfo, +} from '@backstage/plugin-auth-node'; +import { + oidcAuthenticator, + OidcAuthResult, +} from '@backstage/plugin-auth-backend-module-oidc-provider'; import { commonByEmailLocalPartResolver, commonByEmailResolver, } from '../resolvers'; -/** - * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) - * @public - * @deprecated No longer used - */ -export type OidcAuthResult = { - tokenset: TokenSet; - userinfo: UserinfoResponse; -}; - /** * Auth provider integration for generic OpenID Connect auth * @@ -50,19 +43,39 @@ export const oidc = createAuthProviderIntegration({ * The profile transformation function used to verify and convert the auth response * into the profile that will be presented to the user. */ - authHandler?: AuthHandler; + authHandler?: AuthHandler; /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. + * Configure sign-in for this provider; convert user profile respones into + * Backstage identities. */ signIn?: { - resolver: SignInResolver; + resolver: SignInResolver; }; }) { + const authHandler = options?.authHandler; + const signInResolver = options?.signIn?.resolver; return createOAuthProviderFactory({ authenticator: oidcAuthenticator, - profileTransform: adaptLegacyOAuthHandler(options?.authHandler), - signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), + profileTransform: + authHandler && + (( + result: OAuthAuthenticatorResult, + context: AuthResolverContext, + ) => authHandler(result.fullProfile, context)), + signInResolver: + signInResolver && + (( + info: SignInInfo>, + context: AuthResolverContext, + ): Promise => + signInResolver( + { + result: info.result.fullProfile, + profile: info.profile, + }, + context, + )), }); }, resolvers: { diff --git a/yarn.lock b/yarn.lock index 1e41fd83c7..eb6ade926f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4819,7 +4819,6 @@ __metadata: passport-auth0: ^1.4.3 passport-bitbucket-oauth2: ^0.1.2 passport-github2: ^0.1.12 - passport-gitlab2: ^5.0.0 passport-google-oauth20: ^2.0.0 passport-microsoft: ^1.0.0 passport-oauth2: ^1.6.1