Added SSO based auth
This commit is contained in:
parent
2e4af394f9
commit
edb2544e60
|
@ -129,3 +129,6 @@ dist
|
||||||
.yarn/build-state.yml
|
.yarn/build-state.yml
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
**/jwtRS256.key
|
||||||
|
**/jwtRS256.key.pub
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { authClient } from "../helpers/auth_client";
|
||||||
|
import { generators } from 'openid-client';
|
||||||
|
import { configKeys } from "..";
|
||||||
|
|
||||||
|
const code_verifier = generators.codeVerifier();
|
||||||
|
const code_challenge = generators.codeChallenge(code_verifier);
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const authurl = authClient.authorizationUrl({
|
||||||
|
scope: 'email profile openid',
|
||||||
|
code_challenge,
|
||||||
|
code_challenge_method: 'S256',
|
||||||
|
client_id: configKeys.KEYCLOAK_CLIENT_ID,
|
||||||
|
redirect_uri: configKeys.KEYCLOAK_REDIRECT_URI,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
authurl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
code_challenge,
|
||||||
|
code_verifier,
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { NextFunction, Response } from "express";
|
||||||
|
import {authClient} from "../helpers/auth_client";
|
||||||
|
import { ModRequest } from "../types";
|
||||||
|
import { CustomError } from "../libs/error";
|
||||||
|
|
||||||
|
const middleware = async (req: ModRequest | any, res: Response, next: NextFunction) => {
|
||||||
|
try {
|
||||||
|
const authHeader = req.headers?.authorization?.split(' ');
|
||||||
|
if (!authHeader) {
|
||||||
|
throw new Error("No authorization header");
|
||||||
|
}
|
||||||
|
const token : string = authHeader[1];
|
||||||
|
const user = await authClient.userinfo(token, {
|
||||||
|
method: 'GET',
|
||||||
|
tokenType: 'Bearer',
|
||||||
|
params: {
|
||||||
|
access_token: token
|
||||||
|
},
|
||||||
|
via: 'header'
|
||||||
|
});
|
||||||
|
req.user = user;
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
next(new CustomError({
|
||||||
|
message: "Invalid token",
|
||||||
|
statusCode: 401
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default middleware
|
|
@ -0,0 +1,10 @@
|
||||||
|
import {authClient} from "../helpers/auth_client";
|
||||||
|
import { code_verifier } from './check'
|
||||||
|
|
||||||
|
export default async (session_state: string, code: string) => {
|
||||||
|
return authClient.callback(
|
||||||
|
'http://localhost:4038/auth/signin/callback',
|
||||||
|
{ code_verifier, code, session_state, expires_in: "1d" },
|
||||||
|
{ code_verifier }
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,9 +1,13 @@
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
import { parse as parseFile } from 'envfile'
|
import { parse as parseFile } from 'envfile'
|
||||||
|
import { Issuer } from 'openid-client';
|
||||||
|
|
||||||
|
const keyCloakIssuer : Issuer = await Issuer.discover(process.env.KEYCLOAK_AUTH_SERVER_URL!);
|
||||||
|
console.log('🔐 Connected to Keycloak');
|
||||||
|
|
||||||
type IconfigStore = 'development' | 'production'
|
type IconfigStore = 'development' | 'production'
|
||||||
|
|
||||||
interface IConfigKeys {
|
export interface IConfigKeys {
|
||||||
PORT: string | number
|
PORT: string | number
|
||||||
NODE_ENV: string
|
NODE_ENV: string
|
||||||
HASURA_GRAPHQL_ADMIN_SECRET: string
|
HASURA_GRAPHQL_ADMIN_SECRET: string
|
||||||
|
@ -32,6 +36,13 @@ interface IConfigKeys {
|
||||||
AWS_ACCESS_KEY_ID: string
|
AWS_ACCESS_KEY_ID: string
|
||||||
AWS_SECRET_ACCESS_KEY: string
|
AWS_SECRET_ACCESS_KEY: string
|
||||||
AWS_REGION: string
|
AWS_REGION: string
|
||||||
|
PUBLIC_KEY: string
|
||||||
|
PRIVATE_KEY: string
|
||||||
|
KEYCLOAK_ISSUER: Issuer
|
||||||
|
KEYCLOAK_CLIENT_ID: string
|
||||||
|
KEYCLOAK_CLIENT_SECRET: string
|
||||||
|
KEYCLOAK_REDIRECT_URI: string
|
||||||
|
KEYCLOAK_AUTH_SERVER_URL: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ConfigStoreFactory {
|
export default class ConfigStoreFactory {
|
||||||
|
@ -46,9 +57,14 @@ export default class ConfigStoreFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getConfigStore() {
|
public async getConfigStore() {
|
||||||
|
const publicKEY = fs.readFileSync('./jwtRS256.key', 'utf8');
|
||||||
|
const privateKEY = fs.readFileSync('./jwtRS256.key.pub', 'utf8');
|
||||||
if (this.configStoreType === 'development') {
|
if (this.configStoreType === 'development') {
|
||||||
const envContent = await fs.readFileSync(`./.env`, 'utf8')
|
const envContent = await fs.readFileSync(`./.env`, 'utf8')
|
||||||
const env: Partial<IConfigKeys> = await parseFile(envContent)
|
const env: Partial<IConfigKeys> = await parseFile(envContent)
|
||||||
|
env.PUBLIC_KEY = publicKEY
|
||||||
|
env.PRIVATE_KEY = privateKEY
|
||||||
|
env.KEYCLOAK_ISSUER = keyCloakIssuer
|
||||||
return env
|
return env
|
||||||
} else {
|
} else {
|
||||||
let reqEnvContent: any = await fs.readFileSync(
|
let reqEnvContent: any = await fs.readFileSync(
|
||||||
|
@ -59,6 +75,9 @@ export default class ConfigStoreFactory {
|
||||||
reqEnvContent = reqEnvContent.split('\n')
|
reqEnvContent = reqEnvContent.split('\n')
|
||||||
let missingKeys: string[] = []
|
let missingKeys: string[] = []
|
||||||
let env: Partial<IConfigKeys> = {}
|
let env: Partial<IConfigKeys> = {}
|
||||||
|
env.PUBLIC_KEY = publicKEY
|
||||||
|
env.PRIVATE_KEY = privateKEY
|
||||||
|
env.KEYCLOAK_ISSUER = keyCloakIssuer
|
||||||
for (const line of reqEnvContent) {
|
for (const line of reqEnvContent) {
|
||||||
if (!process.env[line]) {
|
if (!process.env[line]) {
|
||||||
missingKeys.push(line)
|
missingKeys.push(line)
|
||||||
|
@ -67,6 +86,7 @@ export default class ConfigStoreFactory {
|
||||||
if (missingKeys.length > 0) {
|
if (missingKeys.length > 0) {
|
||||||
throw new Error(`Missing keys: ${missingKeys}`)
|
throw new Error(`Missing keys: ${missingKeys}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return env
|
return env
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import { Request, Response } from 'express'
|
||||||
|
import { makeResponse } from '../libs'
|
||||||
|
import check from '../auth/check'
|
||||||
|
import verify from '../auth/verify'
|
||||||
|
import { ModRequest } from '../types'
|
||||||
|
|
||||||
|
export default class AuthController {
|
||||||
|
public signin = (req: Request, res: Response) => {
|
||||||
|
const { authurl } = check()
|
||||||
|
res.redirect(authurl)
|
||||||
|
}
|
||||||
|
|
||||||
|
public callback = async (req: Request, res: Response) => {
|
||||||
|
const { session_state, code } = req.query as { session_state: string, code: string}
|
||||||
|
res.send(makeResponse(await verify(session_state, code)))
|
||||||
|
}
|
||||||
|
|
||||||
|
public me = (req: ModRequest, res: Response) => {
|
||||||
|
res.send(makeResponse(req.user))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
actions: []
|
actions: []
|
||||||
custom_types:
|
custom_types:
|
||||||
enums: []
|
enums: []
|
||||||
input_objects: []
|
input_objects: []
|
||||||
objects: []
|
objects: []
|
||||||
scalars: []
|
scalars: []
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -1 +1,9 @@
|
||||||
[]
|
- name: default
|
||||||
|
kind: postgres
|
||||||
|
configuration:
|
||||||
|
connection_info:
|
||||||
|
database_url:
|
||||||
|
from_env: PG_DATABASE_URL
|
||||||
|
isolation_level: read-committed
|
||||||
|
use_prepared_statements: false
|
||||||
|
tables: "!include default/tables/tables.yaml"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
|
@ -0,0 +1 @@
|
||||||
|
disabled_for_roles: []
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1 @@
|
||||||
|
[]
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { configKeys } from "..";
|
||||||
|
|
||||||
|
const { KEYCLOAK_ISSUER } = configKeys
|
||||||
|
|
||||||
|
const authConfig = {
|
||||||
|
client_id: configKeys.KEYCLOAK_CLIENT_ID,
|
||||||
|
'auth-server-url': configKeys.KEYCLOAK_AUTH_SERVER_URL,
|
||||||
|
'ssl-required': 'all',
|
||||||
|
resource: configKeys.KEYCLOAK_CLIENT_ID,
|
||||||
|
credentials: { 'secret-jwt': { secret: configKeys.KEYCLOAK_CLIENT_SECRET } },
|
||||||
|
'confidential-port': 0,
|
||||||
|
redirect_uri: configKeys.KEYCLOAK_REDIRECT_URI,
|
||||||
|
client_secret: configKeys.KEYCLOAK_CLIENT_SECRET,
|
||||||
|
default_max_age: 3600000,
|
||||||
|
}
|
||||||
|
|
||||||
|
const authClient = new KEYCLOAK_ISSUER.Client(authConfig)
|
||||||
|
|
||||||
|
export { authClient }
|
|
@ -43,7 +43,7 @@ export default class CacheClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._nodeClient = new NodeCache()
|
this._nodeClient = new NodeCache()
|
||||||
console.log(`Caching Client initialized in '${env}' environment`)
|
console.log(`🍞 Caching Client initialized in '${env}' environment`)
|
||||||
}
|
}
|
||||||
|
|
||||||
static async set(key: string, value: any) {
|
static async set(key: string, value: any) {
|
||||||
|
|
|
@ -5,45 +5,40 @@ import morgan from 'morgan'
|
||||||
import helmet from 'helmet'
|
import helmet from 'helmet'
|
||||||
|
|
||||||
import { hgqlInit } from './helpers'
|
import { hgqlInit } from './helpers'
|
||||||
|
import cacheClient from './helpers/cache.factory';
|
||||||
import routes from './routes'
|
import routes from './routes'
|
||||||
import { errorHandler, notFoundHandler } from './libs'
|
import { errorHandler, notFoundHandler } from './libs'
|
||||||
import pkg from './package.json' assert { type: 'json' }
|
import pkg from './package.json' assert { type: 'json' }
|
||||||
import configStore from './configs'
|
import configStore, { IConfigKeys } from './configs';
|
||||||
|
|
||||||
export const app: express.Application = express()
|
export const app: express.Application = express()
|
||||||
|
|
||||||
hgqlInit()
|
|
||||||
|
|
||||||
console.log('🚀', '@b68/api', 'v' + pkg.version)
|
console.log('🚀', '@b68/api', 'v' + pkg.version)
|
||||||
|
|
||||||
|
hgqlInit()
|
||||||
|
cacheClient.init();
|
||||||
|
|
||||||
const isDev: boolean = process.env.NODE_ENV == 'production'
|
const isDev: boolean = process.env.NODE_ENV == 'production'
|
||||||
console.log(isDev ? '🚀 Production Mode' : '🚀 Development Mode')
|
console.log(isDev ? '🚀 Production Mode' : '👷 Development Mode')
|
||||||
const configs = new configStore(isDev)
|
const configs = new configStore(isDev)
|
||||||
const configKeys: any = await configs.getConfigStore()
|
const configKeys: IConfigKeys = await configs.getConfigStore() as IConfigKeys
|
||||||
|
|
||||||
app.use(cors())
|
app.use(cors())
|
||||||
app.use(helmet())
|
app.use(helmet())
|
||||||
app.use(morgan('dev'))
|
app.use(morgan('dev'))
|
||||||
app.use(express.json())
|
app.use(express.json())
|
||||||
app.use(express.urlencoded({ extended: true, limit: '50mb' }))
|
app.use(express.urlencoded({ extended: true, limit: '50mb' }))
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
app.use('/health', (req, res) => {
|
console.log('☄ ', 'Base Route', '/')
|
||||||
return res.status(200).json({
|
|
||||||
app: pkg.name,
|
|
||||||
request_ip: req.ip,
|
|
||||||
uptime: process.uptime(),
|
|
||||||
hrtime: process.hrtime(),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
console.log('☄', 'Base Route', '/')
|
|
||||||
app.use('/', routes)
|
app.use('/', routes)
|
||||||
|
|
||||||
app.use(notFoundHandler)
|
app.use(notFoundHandler)
|
||||||
app.use(errorHandler)
|
app.use(errorHandler)
|
||||||
|
|
||||||
app.listen(process.env.PORT, async () => {
|
app.listen(configKeys.PORT, async () => {
|
||||||
console.log(`\nServer running on port ${process.env.PORT}`)
|
console.log(`\n🌈 Server running on port ${configKeys.PORT}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
export { configKeys }
|
export { configKeys }
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"axios": "^1.2.1",
|
"axios": "^1.2.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
|
"ejs": "^3.1.9",
|
||||||
"envfile": "^6.18.0",
|
"envfile": "^6.18.0",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
"napi-nanoid": "^0.0.4",
|
"napi-nanoid": "^0.0.4",
|
||||||
"node-cache": "^5.1.2",
|
"node-cache": "^5.1.2",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
|
"openid-client": "^5.4.0",
|
||||||
"osu-api-extended": "^2.5.12",
|
"osu-api-extended": "^2.5.12",
|
||||||
"redis": "^4.5.1",
|
"redis": "^4.5.1",
|
||||||
"typescript": "^4.9.3"
|
"typescript": "^4.9.3"
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,18 +1,18 @@
|
||||||
import { Router } from 'express'
|
import { Router } from 'express'
|
||||||
import { makeResponse } from '../libs'
|
import AuthController from '../controllers/auth.controller'
|
||||||
|
import middleware from '../auth/middleware'
|
||||||
|
|
||||||
const router = Router()
|
const router = Router()
|
||||||
|
const authController = new AuthController()
|
||||||
|
|
||||||
router.get('/', (req, res) => {
|
router.get('/signin', authController.signin)
|
||||||
res.send(makeResponse({ message: 'Hello World auth!' }))
|
|
||||||
})
|
|
||||||
|
|
||||||
router.all('/err', async (req, res, next) => {
|
router.get('/signin/callback', authController.callback)
|
||||||
try {
|
|
||||||
throw new Error('This is an error')
|
router.get('/me', middleware, authController.me as any)
|
||||||
} catch (err) {
|
|
||||||
next(err)
|
router.get('/', function(req, res) {
|
||||||
}
|
res.render('pages/auth');
|
||||||
})
|
});
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Router } from 'express'
|
||||||
|
|
||||||
|
const router = Router()
|
||||||
|
|
||||||
|
router.use('/health',
|
||||||
|
(req, res) => {
|
||||||
|
return res.status(200).json({
|
||||||
|
status: "OK",
|
||||||
|
app: "B68 API",
|
||||||
|
request_ip: req.ip,
|
||||||
|
uptime: process.uptime(),
|
||||||
|
hrtime: process.hrtime(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
|
@ -30,7 +30,7 @@ const loadRoutes = async (dirPath: string, prefix = '/') => {
|
||||||
''
|
''
|
||||||
)
|
)
|
||||||
const modRoute = path.join(prefix, route)
|
const modRoute = path.join(prefix, route)
|
||||||
console.log('🛰️', 'Loaded', modRoute)
|
console.log('🛰️ ', 'Loaded', modRoute)
|
||||||
|
|
||||||
const mod = await import(path.join(baseDir, prefix + f.name))
|
const mod = await import(path.join(baseDir, prefix + f.name))
|
||||||
router.use(modRoute, mod.default)
|
router.use(modRoute, mod.default)
|
||||||
|
@ -48,4 +48,13 @@ let baseDir = path.dirname(__filename)
|
||||||
baseDir = path.resolve(baseDir)
|
baseDir = path.resolve(baseDir)
|
||||||
|
|
||||||
loadRoutes(baseDir)
|
loadRoutes(baseDir)
|
||||||
|
|
||||||
|
router.get('/', function(req, res) {
|
||||||
|
res.render('pages/index');
|
||||||
|
});
|
||||||
|
|
||||||
|
router.get('/favicon.ico', function(req, res) {
|
||||||
|
res.sendFile(path.join(__dirname, '../public', 'favicon.ico'));
|
||||||
|
});
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Request } from 'express'
|
||||||
|
|
||||||
export interface PaginationType {
|
export interface PaginationType {
|
||||||
page: number
|
page: number
|
||||||
limit: number
|
limit: number
|
||||||
|
@ -5,3 +7,7 @@ export interface PaginationType {
|
||||||
sort_by?: string
|
sort_by?: string
|
||||||
filters?: { [k: string]: any }
|
filters?: { [k: string]: any }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ModRequest extends Request {
|
||||||
|
user: any
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<%- include('../partials/head'); %>
|
||||||
|
</head>
|
||||||
|
<body class="container">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<%- include('../partials/header'); %>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="jumbotron">
|
||||||
|
<a href="/auth/signin">
|
||||||
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
|
||||||
|
Sign In
|
||||||
|
</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<%- include('../partials/footer'); %>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,24 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<%- include('../partials/head'); %>
|
||||||
|
</head>
|
||||||
|
<body class="container">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<%- include('../partials/header'); %>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<div class="jumbotron">
|
||||||
|
<h1>API Landing</h1>
|
||||||
|
<p>Welcome to B68 API Home</p>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<%- include('../partials/footer'); %>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1 @@
|
||||||
|
<p class="text-center text-muted">© Copyright 2023 <a href="https://itsmebravo.dev">Jyotirmoy Bandyopadhayaya</a> | <a href="https://github.com/bravo68web">Github</a></p>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>B68 API</title>
|
||||||
|
|
||||||
|
<!-- CSS (load bootstrap from a CDN) -->
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.2/css/bootstrap.min.css">
|
||||||
|
<link rel="icon" href="/favicon.ico">
|
||||||
|
<style>
|
||||||
|
body { padding-top:50px; }
|
||||||
|
.jumbotron {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
|
<a class="navbar-brand" href="/">B68 API</a>
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/auth">Auth</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
32
yarn.lock
32
yarn.lock
|
@ -3754,6 +3754,13 @@ ejs@^3.1.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
jake "^10.8.5"
|
jake "^10.8.5"
|
||||||
|
|
||||||
|
ejs@^3.1.9:
|
||||||
|
version "3.1.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
|
||||||
|
integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
|
||||||
|
dependencies:
|
||||||
|
jake "^10.8.5"
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"
|
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"
|
||||||
|
@ -5332,6 +5339,11 @@ joi@^17.7.0:
|
||||||
"@sideway/formula" "^3.0.0"
|
"@sideway/formula" "^3.0.0"
|
||||||
"@sideway/pinpoint" "^2.0.0"
|
"@sideway/pinpoint" "^2.0.0"
|
||||||
|
|
||||||
|
jose@^4.10.0:
|
||||||
|
version "4.14.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.0.tgz#c8c03579a0ba3598194c92ccca777d96adca8f48"
|
||||||
|
integrity sha512-LSA/XenLPwqk6e2L+PSUNuuY9G4NGsvjRWz6sJcUBmzTLEPJqQh46FHSUxnAQ64AWOkRO6bSXpy3yXuEKZkbIA==
|
||||||
|
|
||||||
js-sdsl@^4.1.4:
|
js-sdsl@^4.1.4:
|
||||||
version "4.2.0"
|
version "4.2.0"
|
||||||
resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz"
|
resolved "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.2.0.tgz"
|
||||||
|
@ -6464,6 +6476,11 @@ object-assign@^4, object-assign@^4.1.1:
|
||||||
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
|
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
|
||||||
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||||
|
|
||||||
|
object-hash@^2.0.1:
|
||||||
|
version "2.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5"
|
||||||
|
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
|
||||||
|
|
||||||
object-inspect@^1.12.2, object-inspect@^1.9.0:
|
object-inspect@^1.12.2, object-inspect@^1.9.0:
|
||||||
version "1.12.2"
|
version "1.12.2"
|
||||||
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz"
|
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz"
|
||||||
|
@ -6519,6 +6536,11 @@ object.values@^1.1.5, object.values@^1.1.6:
|
||||||
define-properties "^1.1.4"
|
define-properties "^1.1.4"
|
||||||
es-abstract "^1.20.4"
|
es-abstract "^1.20.4"
|
||||||
|
|
||||||
|
oidc-token-hash@^5.0.1:
|
||||||
|
version "5.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/oidc-token-hash/-/oidc-token-hash-5.0.2.tgz#f9ca7f7f1f92d721a2973e66b7430cb52a486648"
|
||||||
|
integrity sha512-U91Ba78GtVBxcExLI7U+hC2AwJQqXQEW/D3fjmJC4hhSVIgdl954KO4Gu95WqAlgDKJdLATxkmuxraWLT0fVRQ==
|
||||||
|
|
||||||
on-finished@2.4.1:
|
on-finished@2.4.1:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
|
resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
|
||||||
|
@ -6568,6 +6590,16 @@ open@^8.4.0:
|
||||||
is-docker "^2.1.1"
|
is-docker "^2.1.1"
|
||||||
is-wsl "^2.2.0"
|
is-wsl "^2.2.0"
|
||||||
|
|
||||||
|
openid-client@^5.4.0:
|
||||||
|
version "5.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/openid-client/-/openid-client-5.4.0.tgz#77f1cda14e2911446f16ea3f455fc7c405103eac"
|
||||||
|
integrity sha512-hgJa2aQKcM2hn3eyVtN12tEA45ECjTJPXCgUh5YzTzy9qwapCvmDTVPWOcWVL0d34zeQoQ/hbG9lJhl3AYxJlQ==
|
||||||
|
dependencies:
|
||||||
|
jose "^4.10.0"
|
||||||
|
lru-cache "^6.0.0"
|
||||||
|
object-hash "^2.0.1"
|
||||||
|
oidc-token-hash "^5.0.1"
|
||||||
|
|
||||||
optionator@^0.9.1:
|
optionator@^0.9.1:
|
||||||
version "0.9.1"
|
version "0.9.1"
|
||||||
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz"
|
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz"
|
||||||
|
|
Loading…
Reference in New Issue