WIP: oidc auth provider module migration
Signed-off-by: Ruben Vallejo <rvallejo@vmware.com>
This commit is contained in:
committed by
Jamie Klassen
parent
f89777d76f
commit
c541201b1b
@@ -1,6 +1,6 @@
|
||||
# Auth Module: GitLab Provider
|
||||
# Auth Module: Oidc Provider
|
||||
|
||||
This module provides an GitLab auth provider implementation for `@backstage/plugin-auth-backend`.
|
||||
This module provides an Oidc auth provider implementation for `@backstage/plugin-auth-backend`.
|
||||
|
||||
## Links
|
||||
|
||||
|
||||
@@ -25,16 +25,22 @@
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "workspace:^",
|
||||
"@backstage/backend-plugin-api": "workspace:^",
|
||||
"@backstage/plugin-auth-backend": "workspace:^",
|
||||
"@backstage/plugin-auth-node": "workspace:^",
|
||||
"express": "^4.18.2",
|
||||
"passport": "^0.6.0",
|
||||
"passport-oidc2": "^5.0.0"
|
||||
"openid-client": "^5.5.0",
|
||||
"passport": "^0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-defaults": "workspace:^",
|
||||
"@backstage/backend-test-utils": "workspace:^",
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/plugin-auth-backend": "workspace:^",
|
||||
"@backstage/config": "workspace:^",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express-promise-router": "^4.1.1",
|
||||
"express-session": "^1.17.3",
|
||||
"jose": "^4.14.6",
|
||||
"msw": "^1.3.1",
|
||||
"supertest": "^6.3.3"
|
||||
},
|
||||
"configSchema": "config.d.ts",
|
||||
|
||||
@@ -14,64 +14,110 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Strategy as GitlabStrategy } from 'passport-oidc2';
|
||||
import {
|
||||
Issuer,
|
||||
ClientAuthMethod,
|
||||
TokenSet,
|
||||
UserinfoResponse,
|
||||
Strategy as OidcStrategy,
|
||||
} from 'openid-client';
|
||||
import {
|
||||
createOAuthAuthenticator,
|
||||
decodeOAuthState,
|
||||
encodeOAuthState,
|
||||
PassportDoneCallback,
|
||||
PassportOAuthAuthenticatorHelper,
|
||||
PassportOAuthDoneCallback,
|
||||
PassportOAuthPrivateInfo,
|
||||
PassportProfile,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { OidcAuthResult } from '@backstage/plugin-auth-backend';
|
||||
|
||||
/** @public */
|
||||
export const oidcAuthenticator = createOAuthAuthenticator({
|
||||
defaultProfileTransform:
|
||||
PassportOAuthAuthenticatorHelper.defaultProfileTransform,
|
||||
initialize({ callbackUrl, config }) {
|
||||
async initialize({ callbackUrl, config }) {
|
||||
const clientId = config.getString('clientId');
|
||||
const clientSecret = config.getString('clientSecret');
|
||||
const baseUrl =
|
||||
config.getOptionalString('audience') || 'https://oidc.com';
|
||||
const customCallbackUrl = config.getOptionalString('callbackUrl');
|
||||
const callbackUrl2 = customCallbackUrl || callbackUrl;
|
||||
const metadataUrl = config.getString('metadataUrl');
|
||||
const tokenEndpointAuthMethod = config.getOptionalString(
|
||||
'tokenEndpointAuthMethod',
|
||||
) as ClientAuthMethod;
|
||||
const tokenSignedResponseAlg = config.getOptionalString(
|
||||
'tokenSignedResponseAlg',
|
||||
);
|
||||
const initializedScope = config.getOptionalString('scope');
|
||||
const initializedPrompt = config.getOptionalString('prompt');
|
||||
const issuer = await Issuer.discover(
|
||||
`${metadataUrl}/.well-known/openid-configuration`,
|
||||
);
|
||||
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: [callbackUrl2],
|
||||
response_types: ['code'],
|
||||
token_endpoint_auth_method:
|
||||
tokenEndpointAuthMethod || 'client_secret_basic',
|
||||
id_token_signed_response_alg: tokenSignedResponseAlg || 'RS256',
|
||||
scope: initializedScope || '',
|
||||
});
|
||||
|
||||
return PassportOAuthAuthenticatorHelper.from(
|
||||
new GitlabStrategy(
|
||||
const helper = PassportOAuthAuthenticatorHelper.from(
|
||||
new OidcStrategy(
|
||||
{
|
||||
clientID: clientId,
|
||||
clientSecret: clientSecret,
|
||||
callbackURL: callbackUrl,
|
||||
baseURL: baseUrl,
|
||||
authorizationURL: `${baseUrl}/oauth/authorize`,
|
||||
tokenURL: `${baseUrl}/oauth/token`,
|
||||
profileURL: `${baseUrl}/api/v4/user`,
|
||||
client,
|
||||
passReqToCallback: false,
|
||||
},
|
||||
(
|
||||
accessToken: string,
|
||||
refreshToken: string,
|
||||
params: any,
|
||||
fullProfile: PassportProfile,
|
||||
done: PassportOAuthDoneCallback,
|
||||
tokenset: TokenSet,
|
||||
userinfo: UserinfoResponse,
|
||||
done: PassportDoneCallback<OidcAuthResult, PassportOAuthPrivateInfo>,
|
||||
) => {
|
||||
if (typeof done !== 'function') {
|
||||
throw new Error(
|
||||
'OIDC IdP must provide a userinfo_endpoint in the metadata response',
|
||||
);
|
||||
}
|
||||
done(
|
||||
undefined,
|
||||
{ fullProfile, params, accessToken },
|
||||
{ refreshToken },
|
||||
{ tokenset, userinfo },
|
||||
{
|
||||
refreshToken: tokenset.refresh_token,
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
return { helper, client, initializedScope, initializedPrompt };
|
||||
},
|
||||
|
||||
async start(input, helper) {
|
||||
async start(input, implementation) {
|
||||
const { initializedScope, initializedPrompt, helper } =
|
||||
await implementation;
|
||||
const options: Record<string, string> = {
|
||||
scope: input.scope || initializedScope || 'openid profile email',
|
||||
state: input.state,
|
||||
};
|
||||
const prompt = initializedPrompt || 'none';
|
||||
if (prompt !== 'auto') {
|
||||
options.prompt = prompt;
|
||||
}
|
||||
|
||||
return helper.start(input, {
|
||||
accessType: 'offline',
|
||||
prompt: 'consent',
|
||||
...options,
|
||||
});
|
||||
},
|
||||
|
||||
async authenticate(input, helper) {
|
||||
return helper.authenticate(input);
|
||||
async authenticate(input, implementation) {
|
||||
return (await implementation).helper.authenticate(input);
|
||||
},
|
||||
|
||||
async refresh(input, helper) {
|
||||
return helper.refresh(input);
|
||||
async refresh(input, implementation) {
|
||||
return (await implementation).helper.refresh(input);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -18,39 +18,190 @@ import { mockServices, startTestBackend } from '@backstage/backend-test-utils';
|
||||
import { authPlugin } from '@backstage/plugin-auth-backend';
|
||||
import { authModuleOidcProvider } from './module';
|
||||
import request from 'supertest';
|
||||
import { decodeOAuthState } from '@backstage/plugin-auth-node';
|
||||
import {
|
||||
AuthProviderRouteHandlers,
|
||||
AuthResolverContext,
|
||||
createOAuthRouteHandlers,
|
||||
decodeOAuthState,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { setupServer } from 'msw';
|
||||
import { ClientMetadata, IssuerMetadata } from 'openid-client';
|
||||
import { rest } from 'msw';
|
||||
import { setupRequestMockHandlers } from '@backstage/backend-test-utils';
|
||||
import { Server } from 'http';
|
||||
import { AddressInfo } from 'net';
|
||||
import express from 'express';
|
||||
import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import passport from 'passport';
|
||||
import session from 'express-session';
|
||||
import { oidcAuthenticator } from './authenticator';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import Router from 'express-promise-router';
|
||||
|
||||
describe('authModuleOidcProvider', () => {
|
||||
it('should start', async () => {
|
||||
const { server } = await startTestBackend({
|
||||
features: [
|
||||
authPlugin,
|
||||
authModuleOidcProvider,
|
||||
mockServices.rootConfig.factory({
|
||||
data: {
|
||||
app: {
|
||||
baseUrl: 'http://localhost:3000',
|
||||
},
|
||||
auth: {
|
||||
providers: {
|
||||
oidc: {
|
||||
development: {
|
||||
clientId: 'my-client-id',
|
||||
clientSecret: 'my-client-secret',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
let app: express.Express;
|
||||
let backstageServer: Server;
|
||||
let appUrl: string;
|
||||
let providerRouteHandler: AuthProviderRouteHandlers;
|
||||
let idToken: string;
|
||||
let publicKey: JWK;
|
||||
|
||||
const mswServer = setupServer();
|
||||
setupRequestMockHandlers(mswServer);
|
||||
|
||||
const issuerMetadata = {
|
||||
issuer: 'https://oidc.test',
|
||||
authorization_endpoint: 'https://oidc.test/as/authorization.oauth2',
|
||||
token_endpoint: 'https://oidc.test/as/token.oauth2',
|
||||
revocation_endpoint: 'https://oidc.test/as/revoke_token.oauth2',
|
||||
userinfo_endpoint: 'https://oidc.test/idp/userinfo.openid',
|
||||
introspection_endpoint: 'https://oidc.test/as/introspect.oauth2',
|
||||
jwks_uri: 'https://oidc.test/pf/JWKS',
|
||||
scopes_supported: ['openid'],
|
||||
claims_supported: ['email'],
|
||||
response_types_supported: ['code'],
|
||||
id_token_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'],
|
||||
token_endpoint_auth_signing_alg_values_supported: [
|
||||
'RS256',
|
||||
'RS512',
|
||||
'HS256',
|
||||
],
|
||||
request_object_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'],
|
||||
};
|
||||
|
||||
const clientMetadata = {
|
||||
authHandler: async input => ({
|
||||
profile: {
|
||||
displayName: input.userinfo.email,
|
||||
},
|
||||
}),
|
||||
resolverContext: {} as AuthResolverContext,
|
||||
callbackUrl: 'https://oidc.test/callback',
|
||||
clientId: 'testclientid',
|
||||
clientSecret: 'testclientsecret',
|
||||
metadataUrl: 'https://oidc.test/.well-known/openid-configuration',
|
||||
tokenEndpointAuthMethod: 'none',
|
||||
tokenSignedResponseAlg: 'none',
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
const keyPair = await generateKeyPair('ES256');
|
||||
const privateKey = await exportJWK(keyPair.privateKey);
|
||||
publicKey = await exportJWK(keyPair.publicKey);
|
||||
publicKey.alg = privateKey.alg = 'ES256';
|
||||
|
||||
idToken = await new SignJWT({
|
||||
sub: 'test',
|
||||
iss: 'https://pinniped.test',
|
||||
iat: Date.now(),
|
||||
aud: 'clientId',
|
||||
exp: Date.now() + 10000,
|
||||
})
|
||||
.setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid })
|
||||
.sign(keyPair.privateKey);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
mswServer.use(
|
||||
rest.get(
|
||||
'https://oidc.test/.well-known/openid-configuration',
|
||||
(_req, res, ctx) =>
|
||||
res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/json'),
|
||||
ctx.json(issuerMetadata),
|
||||
),
|
||||
),
|
||||
rest.get('https://oidc.test/oauth2/authorize', async (req, res, ctx) => {
|
||||
const callbackUrl = new URL(req.url.searchParams.get('redirect_uri')!);
|
||||
callbackUrl.searchParams.set('code', 'authorization_code');
|
||||
callbackUrl.searchParams.set(
|
||||
'state',
|
||||
req.url.searchParams.get('state')!,
|
||||
);
|
||||
callbackUrl.searchParams.set('scope', 'test-scope');
|
||||
return res(
|
||||
ctx.status(302),
|
||||
ctx.set('Location', callbackUrl.toString()),
|
||||
);
|
||||
}),
|
||||
);
|
||||
|
||||
const secret = 'secret';
|
||||
app = express()
|
||||
.use(cookieParser(secret))
|
||||
.use(
|
||||
session({
|
||||
secret,
|
||||
saveUninitialized: false,
|
||||
resave: false,
|
||||
cookie: { secure: false },
|
||||
}),
|
||||
],
|
||||
)
|
||||
.use(passport.initialize())
|
||||
.use(passport.session());
|
||||
await new Promise(resolve => {
|
||||
backstageServer = app.listen(0, '0.0.0.0', () => {
|
||||
appUrl = `http://127.0.0.1:${
|
||||
(backstageServer.address() as AddressInfo).port
|
||||
}`;
|
||||
resolve(null);
|
||||
});
|
||||
});
|
||||
|
||||
const agent = request.agent(server);
|
||||
mswServer.use(rest.all(`${appUrl}/*`, req => req.passthrough()));
|
||||
|
||||
const res = await agent.get('/api/auth/oidc/start?env=development');
|
||||
providerRouteHandler = createOAuthRouteHandlers({
|
||||
authenticator: oidcAuthenticator,
|
||||
appUrl,
|
||||
baseUrl: `${appUrl}/api/auth`,
|
||||
isOriginAllowed: _ => true,
|
||||
providerId: 'oidc',
|
||||
config: new ConfigReader({
|
||||
metadataUrl: 'https://oidc.test',
|
||||
clientId: 'clientId',
|
||||
clientSecret: 'clientSecret',
|
||||
}),
|
||||
resolverContext: {
|
||||
issueToken: async _ => ({ token: '' }),
|
||||
findCatalogUser: async _ => ({
|
||||
entity: {
|
||||
apiVersion: '',
|
||||
kind: '',
|
||||
metadata: { name: '' },
|
||||
},
|
||||
}),
|
||||
signInWithCatalogUser: async _ => ({ token: '' }),
|
||||
},
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(302);
|
||||
const router = Router();
|
||||
router
|
||||
.use(
|
||||
'/api/auth/pinniped/start',
|
||||
providerRouteHandler.start.bind(providerRouteHandler),
|
||||
)
|
||||
.use(
|
||||
'/api/auth/pinniped/handler/frame',
|
||||
providerRouteHandler.frameHandler.bind(providerRouteHandler),
|
||||
);
|
||||
app.use(router);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
backstageServer.close();
|
||||
});
|
||||
|
||||
it('should start', async () => {
|
||||
const agent = request.agent('');
|
||||
|
||||
const startResponse = await agent.get(
|
||||
`${appUrl}/api/auth/oidc/start?env=development`,
|
||||
);
|
||||
expect(startResponse.status).toEqual(302);
|
||||
|
||||
const nonceCookie = agent.jar.getCookie('oidc-nonce', {
|
||||
domain: 'localhost',
|
||||
@@ -60,14 +211,16 @@ describe('authModuleOidcProvider', () => {
|
||||
});
|
||||
expect(nonceCookie).toBeDefined();
|
||||
|
||||
const startUrl = new URL(res.get('location'));
|
||||
const startUrl = new URL(startResponse.get('location'));
|
||||
expect(startUrl.origin).toBe('https://oidc.com');
|
||||
expect(startUrl.pathname).toBe('/oauth/authorize');
|
||||
expect(Object.fromEntries(startUrl.searchParams)).toEqual({
|
||||
response_type: 'code',
|
||||
scope: 'read_user',
|
||||
client_id: 'my-client-id',
|
||||
redirect_uri: `http://localhost:${server.port()}/api/auth/oidc/handler/frame`,
|
||||
redirect_uri: `http://localhost:${
|
||||
(backstageServer.address() as AddressInfo).port
|
||||
}/api/auth/oidc/handler/frame`,
|
||||
state: expect.any(String),
|
||||
});
|
||||
|
||||
@@ -75,5 +228,5 @@ describe('authModuleOidcProvider', () => {
|
||||
env: 'development',
|
||||
nonce: decodeURIComponent(nonceCookie.value),
|
||||
});
|
||||
});
|
||||
}, 70000);
|
||||
});
|
||||
|
||||
@@ -19,13 +19,13 @@ import {
|
||||
commonSignInResolvers,
|
||||
createOAuthProviderFactory,
|
||||
} from '@backstage/plugin-auth-node';
|
||||
import { gitlabAuthenticator } from './authenticator';
|
||||
import { gitlabSignInResolvers } from './resolvers';
|
||||
import { oidcAuthenticator } from './authenticator';
|
||||
import { oidcSignInResolvers } from './resolvers';
|
||||
|
||||
/** @public */
|
||||
export const authModuleGitlabProvider = createBackendModule({
|
||||
export const authModuleOidcProvider = createBackendModule({
|
||||
pluginId: 'auth',
|
||||
moduleId: 'gitlab-provider',
|
||||
moduleId: 'oidc-provider',
|
||||
register(reg) {
|
||||
reg.registerInit({
|
||||
deps: {
|
||||
@@ -33,11 +33,11 @@ export const authModuleGitlabProvider = createBackendModule({
|
||||
},
|
||||
async init({ providers }) {
|
||||
providers.registerProvider({
|
||||
providerId: 'gitlab',
|
||||
providerId: 'oidc',
|
||||
factory: createOAuthProviderFactory({
|
||||
authenticator: gitlabAuthenticator,
|
||||
authenticator: oidcAuthenticator,
|
||||
signInResolverFactories: {
|
||||
...gitlabSignInResolvers,
|
||||
...oidcSignInResolvers,
|
||||
...commonSignInResolvers,
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -22,13 +22,13 @@ import {
|
||||
} from '@backstage/plugin-auth-node';
|
||||
|
||||
/**
|
||||
* Available sign-in resolvers for the GitLab auth provider.
|
||||
* Available sign-in resolvers for the Oidc auth provider.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export namespace gitlabSignInResolvers {
|
||||
export namespace oidcSignInResolvers {
|
||||
/**
|
||||
* Looks up the user by matching their GitLab username to the entity name.
|
||||
* Looks up the user by matching their Oidc username to the entity name.
|
||||
*/
|
||||
export const usernameMatchingUserEntityName = createSignInResolverFactory({
|
||||
create() {
|
||||
@@ -40,11 +40,32 @@ export namespace gitlabSignInResolvers {
|
||||
|
||||
const id = result.fullProfile.username;
|
||||
if (!id) {
|
||||
throw new Error(`GitLab user profile does not contain a username`);
|
||||
throw new Error(`Oidc user profile does not contain a username`);
|
||||
}
|
||||
|
||||
return ctx.signInWithCatalogUser({ entityRef: { name: id } });
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Looks up the user by matching their email to the `google.com/email` annotation. Still working out this resolver.....
|
||||
*/
|
||||
export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({
|
||||
create() {
|
||||
return async (info: SignInInfo<GcpIapResult>, ctx) => {
|
||||
const email = info.result.iapToken.email;
|
||||
|
||||
if (!email) {
|
||||
throw new Error('Google IAP sign-in result is missing email');
|
||||
}
|
||||
|
||||
return ctx.signInWithCatalogUser({
|
||||
annotations: {
|
||||
'google.com/email': email,
|
||||
},
|
||||
});
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,25 +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.
|
||||
*/
|
||||
|
||||
declare module 'passport-gitlab2' {
|
||||
import { Request } from 'express';
|
||||
import { StrategyCreated } from 'passport';
|
||||
|
||||
export class Strategy {
|
||||
constructor(options: any, verify: any);
|
||||
authenticate(this: StrategyCreated<this>, req: Request, options?: any): any;
|
||||
}
|
||||
}
|
||||
@@ -4672,6 +4672,30 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-auth-backend-module-oidc-provider@workspace:plugins/auth-backend-module-oidc-provider":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-auth-backend-module-oidc-provider@workspace:plugins/auth-backend-module-oidc-provider"
|
||||
dependencies:
|
||||
"@backstage/backend-common": "workspace:^"
|
||||
"@backstage/backend-defaults": "workspace:^"
|
||||
"@backstage/backend-plugin-api": "workspace:^"
|
||||
"@backstage/backend-test-utils": "workspace:^"
|
||||
"@backstage/cli": "workspace:^"
|
||||
"@backstage/config": "workspace:^"
|
||||
"@backstage/plugin-auth-backend": "workspace:^"
|
||||
"@backstage/plugin-auth-node": "workspace:^"
|
||||
cookie-parser: ^1.4.6
|
||||
express: ^4.18.2
|
||||
express-promise-router: ^4.1.1
|
||||
express-session: ^1.17.3
|
||||
jose: ^4.14.6
|
||||
msw: ^1.3.1
|
||||
openid-client: ^5.5.0
|
||||
passport: ^0.6.0
|
||||
supertest: ^6.3.3
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@backstage/plugin-auth-backend-module-okta-provider@workspace:^, @backstage/plugin-auth-backend-module-okta-provider@workspace:plugins/auth-backend-module-okta-provider":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@backstage/plugin-auth-backend-module-okta-provider@workspace:plugins/auth-backend-module-okta-provider"
|
||||
@@ -31245,6 +31269,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jose@npm:^4.14.4":
|
||||
version: 4.14.6
|
||||
resolution: "jose@npm:4.14.6"
|
||||
checksum: eae81a234e7bf1446b1bd80722b3462b014e3835b155c3a7799c1c5043163a53a0dc28d347004151b031e6b7b863403aabf8814d9cc217ce21f8c2f3ebd4b335
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jose@npm:^4.14.6, jose@npm:^4.15.4, jose@npm:^4.6.0":
|
||||
version: 4.15.4
|
||||
resolution: "jose@npm:4.15.4"
|
||||
@@ -35636,6 +35667,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"openid-client@npm:^5.5.0":
|
||||
version: 5.5.0
|
||||
resolution: "openid-client@npm:5.5.0"
|
||||
dependencies:
|
||||
jose: ^4.14.4
|
||||
lru-cache: ^6.0.0
|
||||
object-hash: ^2.2.0
|
||||
oidc-token-hash: ^5.0.3
|
||||
checksum: d2617b5bb0d9a0da338aeb7489bcbe3a79df9681189c7b61c2a3284289eee7110dfee2b04b49a9fdd4f064b7e2057ddb0becfedd9c19388e7788ae15b24c8e4c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"oppa@npm:^0.4.0":
|
||||
version: 0.4.0
|
||||
resolution: "oppa@npm:0.4.0"
|
||||
@@ -36273,6 +36316,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"passport@npm:^0.6.0":
|
||||
version: 0.6.0
|
||||
resolution: "passport@npm:0.6.0"
|
||||
dependencies:
|
||||
passport-strategy: 1.x.x
|
||||
pause: 0.0.1
|
||||
utils-merge: ^1.0.1
|
||||
checksum: ef932ad671d50de34765c7a53cd1e058d8331a82a6df09265a9c6c1168911aee4a7b5215803d0101110ab7f317e096b4954ca7e18fb2c33b9929f0bd17dbe159
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"passport@npm:^0.7.0":
|
||||
version: 0.7.0
|
||||
resolution: "passport@npm:0.7.0"
|
||||
|
||||
Reference in New Issue
Block a user