Support for OIDC flow (#306)

* Support 'POST' at authorization endpoint

* handle additional scope params

* handle additional claims param

* Try with `legacy-peer-deps` true

* Fix logic

* Set legacy-peer-deps to `true`

* Remove `.npmrc` files and sync packages from main

* Resolve conflicts

* Load jwtSigningKeys into env

* Return id_token for OIDC flow

* Support `nonce`

* Add type for `nonce`

* Set `nonce` only if present in request

* Expose OpenId provider metadata

* Update metadata

* Tweak path remove dot, map jwks

* Add jwsAlg and source keys using base64

* Source jose from root package.json too

* JWS utils

* Serve jwks_uri

* Load private key for signing

* Fix authz endpoint

* Format example env

* Fix claims

* Format discovery and add missing metadata

* Include the basic profile in id_token

* Fix claims access

* Remove console log

* Sync package lock

* Cleanup

* Support for claims is optional

* cleanup type

* Set `Content-Type` header

* Remove default from env

* Handle jwt env

* oidc fixture

* Test for oidc flow, check id_token in response

* Add jwt envs

* Fix for undefined

* Remove keys check in controller init

* Runtime check for JWS keys

* check if id_token is absent

* Check for claims and verify signature

* Snapshot test oidc discovery page

* Add snapshot for linux to work in CI

* Test with a fontless screenshot

* test with this one

* add a debug step

* Get the entire dir for snapshot

* Test with this

* Comment out debug step

* snapshot test jwks

* Update env

* Upload screenshot for linux

* Add debug step

* Update snapshot

* Sync package lock

* Remove local testing snapshots

* Assert using api request

* Update to use api test for jwks endpoint

* Set `JWS_ALG` env

* Prefix openid vars

* Fix env access

* Fix e2e test

* Fix options in tests

* Fix env var access

* Use ttl from env

* Simplify exp value setting

* oidc discovery controller

* Fix typing

* Handle case where signing keys are not set

* return `oidcDiscoveryController`

* Throw a JacksonError like object

* Use controllers and cleanup

* throw JacksonError like object

* Minor formatting

* Fix typing and add check for undefined

* Keep order of packages same as in main

* Update key generation comment

* Initialise `openid` correctly in npm

* Cleanup

* Set `sub` claim

* Set 'sub' only for oidc flow
This commit is contained in:
Aswin V 2022-07-24 00:04:55 +07:00 committed by GitHub
parent 2291aa1081
commit 9b23eed3e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 681 additions and 466 deletions

View File

@ -30,3 +30,18 @@ NEXTAUTH_ACL=
# OpenTelemetry
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=
OTEL_EXPORTER_OTLP_HEADERS=
# JWS Algorithm to be used for signing e.g., RS256
# https://github.com/panva/jose/issues/114#digital-signatures
OPENID_JWS_ALG=
# JWT signing keys
# Generate keys: https://www.scottbrady91.com/openssl/creating-rsa-keys-using-openssl,
# Load into env: https://developer.vonage.com/blog/20/07/29/using-private-keys-in-environment-variables
# openssl genrsa -out private-key.pem 3072
# convert to pkcs8 format: openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem -out private_key.pem
# cat private_key.pem | base64
OPENID_RSA_PRIVATE_KEY=
# openssl rsa -in private_key.pem -pubout -out public_key.pem
# cat public-key.pem | base64
OPENID_RSA_PUBLIC_KEY=

View File

@ -32,6 +32,9 @@ jobs:
DEBUG: pw:webserver
SAML_AUDIENCE: https://saml.boxyhq.com
JACKSON_API_KEYS: secret
OPENID_JWS_ALG: RS256
OPENID_RSA_PUBLIC_KEY: ${{ secrets.OPENID_RSA_PUBLIC_KEY }}
OPENID_RSA_PRIVATE_KEY: ${{ secrets.OPENID_RSA_PRIVATE_KEY }}
PLANETSCALE_URL: ${{ secrets.PLANETSCALE_URL }}
strategy:
matrix:

24
e2e/jwks.spec.ts Normal file
View File

@ -0,0 +1,24 @@
import * as jose from 'jose';
import { test, expect } from '@playwright/test';
test('should return public key in jwk format', async ({ request }) => {
const response = await request.get('/oauth/jwks');
const jwks = await response.json();
const spki = Buffer.from(process.env.OPENID_RSA_PUBLIC_KEY || '', 'base64').toString('ascii');
const importedPublicKey = await jose.importSPKI(spki, process.env.OPENID_JWS_ALG || '');
const publicKeyJWK = await jose.exportJWK(importedPublicKey);
const jwkThumbprint = await jose.calculateJwkThumbprint(publicKeyJWK);
expect(jwks).toStrictEqual({
keys: [
{
...publicKeyJWK,
kid: jwkThumbprint,
alg: process.env.OPENID_JWS_ALG,
use: 'sig',
},
],
});
});

View File

@ -0,0 +1,19 @@
import { test, expect } from '@playwright/test';
test('should return oidc discovery configuration', async ({ request, baseURL }) => {
const response = await request.get('/.well-known/openid-configuration');
const oidcDiscovery = await response.json();
expect(oidcDiscovery).toStrictEqual({
issuer: process.env.SAML_AUDIENCE,
authorization_endpoint: `${baseURL}/api/oauth/authorize`,
token_endpoint: `${baseURL}/api/oauth/token`,
userinfo_endpoint: `${baseURL}/api/oauth/userinfo`,
jwks_uri: `${baseURL}/oauth/jwks`,
response_types_supported: ['code'],
subject_types_supported: ['public'],
id_token_signing_alg_values_supported: ['RS256'],
grant_types_supported: ['authorization_code'],
code_challenge_methods_supported: ['plain', 'S256'],
});
});

View File

@ -33,6 +33,13 @@ const db = {
const clientSecretVerifier = process.env.CLIENT_SECRET_VERIFIER;
const jwsAlg = process.env.OPENID_JWS_ALG || '';
const jwtSigningKeys = {
private: process.env.OPENID_RSA_PRIVATE_KEY || '',
public: process.env.OPENID_RSA_PUBLIC_KEY || '',
};
const openid = { jwsAlg, jwtSigningKeys };
export default {
hostUrl,
hostPort,
@ -45,4 +52,5 @@ export default {
idpEnabled,
db,
clientSecretVerifier,
openid,
};

View File

@ -5,6 +5,7 @@ import jackson, {
ILogoutController,
IOAuthController,
IHealthCheckController,
IOidcDiscoveryController,
} from '@boxyhq/saml-jackson';
import env from '@lib/env';
import '@lib/metrics';
@ -14,6 +15,7 @@ let oauthController: IOAuthController;
let adminController: IAdminController;
let logoutController: ILogoutController;
let healthCheckController: IHealthCheckController;
let oidcDiscoveryController: IOidcDiscoveryController;
const g = global as any;
@ -23,7 +25,8 @@ export default async function init() {
!g.oauthController ||
!g.adminController ||
!g.healthCheckController ||
!g.logoutController
!g.logoutController ||
!g.oidcDiscoveryController
) {
const ret = await jackson(env);
apiController = ret.apiController;
@ -31,12 +34,14 @@ export default async function init() {
adminController = ret.adminController;
logoutController = ret.logoutController;
healthCheckController = ret.healthCheckController;
oidcDiscoveryController = ret.oidcDiscoveryController;
g.apiController = apiController;
g.oauthController = oauthController;
g.adminController = adminController;
g.logoutController = logoutController;
g.healthCheckController = healthCheckController;
g.oidcDiscoveryController = oidcDiscoveryController;
g.isJacksonReady = true;
} else {
apiController = g.apiController;
@ -44,6 +49,7 @@ export default async function init() {
adminController = g.adminController;
logoutController = g.logoutController;
healthCheckController = g.healthCheckController;
oidcDiscoveryController = g.oidcDiscoveryController;
}
return {
@ -52,6 +58,7 @@ export default async function init() {
adminController,
logoutController,
healthCheckController,
oidcDiscoveryController,
};
}

View File

@ -1,6 +1,7 @@
import { NextApiRequest, NextApiResponse } from 'next';
import env from '@lib/env';
import micromatch from 'micromatch';
import * as jose from 'jose';
export const validateApiKey = (token) => {
return env.apiKeys.includes(token);

View File

@ -15,4 +15,16 @@ module.exports = {
return config;
},
rewrites: async () => {
return [
{
source: '/.well-known/openid-configuration',
destination: '/api/well-known/openid-configuration',
},
{
source: '/oauth/jwks',
destination: '/api/oauth/jwks',
},
];
},
};

107
npm/package-lock.json generated
View File

@ -44,7 +44,7 @@
"node": ">=14.18.1 <=16.x"
}
},
"../../../../../jacksonExamples/saml20-maintained": {
"../../jacksonExamples/saml20-maintained": {
"name": "@boxyhq/saml20",
"version": "1.0.0",
"extraneous": true,
@ -61,9 +61,6 @@
"typescript": "4.6.3"
}
},
"../../../../vishallodha/Desktop/boxyhq/jacksonExamples/saml20-maintained": {
"extraneous": true
},
"../../saml20-maintained": {
"name": "@boxyhq/saml20",
"version": "1.0.0-beta-2",
@ -85,6 +82,9 @@
"typescript": "4.6.3"
}
},
"../../vishallodha/Desktop/boxyhq/jacksonExamples/saml20-maintained": {
"extraneous": true
},
"@boxyhq/saml20@1.0.0-beta-1": {
"extraneous": true
},
@ -542,7 +542,7 @@
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"devOptional": true,
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
@ -650,7 +650,7 @@
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz",
"integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6.0.0"
}
@ -659,13 +659,13 @@
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
"integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==",
"devOptional": true
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"devOptional": true,
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
@ -981,25 +981,25 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
"integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
"devOptional": true
"dev": true
},
"node_modules/@tsconfig/node12": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
"integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
"devOptional": true
"dev": true
},
"node_modules/@tsconfig/node14": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
"integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
"devOptional": true
"dev": true
},
"node_modules/@tsconfig/node16": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"devOptional": true
"dev": true
},
"node_modules/@types/json-schema": {
"version": "7.0.11",
@ -1246,7 +1246,7 @@
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"devOptional": true,
"dev": true,
"bin": {
"acorn": "bin/acorn"
},
@ -1267,7 +1267,7 @@
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=0.4.0"
}
@ -1371,7 +1371,7 @@
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"devOptional": true
"dev": true
},
"node_modules/argparse": {
"version": "2.0.1",
@ -1781,7 +1781,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"devOptional": true
"dev": true
},
"node_modules/cross-env": {
"version": "7.0.3",
@ -3073,7 +3073,7 @@
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"devOptional": true
"dev": true
},
"node_modules/memory-pager": {
"version": "1.5.0",
@ -6342,7 +6342,7 @@
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"devOptional": true,
"dev": true,
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@ -6385,7 +6385,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=0.3.1"
}
@ -6669,7 +6669,7 @@
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"devOptional": true,
"dev": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -6743,7 +6743,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"devOptional": true
"dev": true
},
"node_modules/webcrypto-core": {
"version": "1.7.5",
@ -6980,7 +6980,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"devOptional": true,
"dev": true,
"engines": {
"node": ">=6"
}
@ -7342,7 +7342,7 @@
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"devOptional": true,
"dev": true,
"requires": {
"@jridgewell/trace-mapping": "0.3.9"
}
@ -7431,19 +7431,19 @@
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz",
"integrity": "sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==",
"devOptional": true
"dev": true
},
"@jridgewell/sourcemap-codec": {
"version": "1.4.11",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz",
"integrity": "sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==",
"devOptional": true
"dev": true
},
"@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"devOptional": true,
"dev": true,
"requires": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
@ -7452,8 +7452,7 @@
"@node-redis/bloom": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@node-redis/bloom/-/bloom-1.0.1.tgz",
"integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw==",
"requires": {}
"integrity": "sha512-mXEBvEIgF4tUzdIN89LiYsbi6//EdpFA7L8M+DHCvePXg+bfHWi+ct5VI6nHUFQE5+ohm/9wmgihCH3HSkeKsw=="
},
"@node-redis/client": {
"version": "1.0.5",
@ -7469,26 +7468,22 @@
"@node-redis/graph": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@node-redis/graph/-/graph-1.0.0.tgz",
"integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g==",
"requires": {}
"integrity": "sha512-mRSo8jEGC0cf+Rm7q8mWMKKKqkn6EAnA9IA2S3JvUv/gaWW/73vil7GLNwion2ihTptAm05I9LkepzfIXUKX5g=="
},
"@node-redis/json": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@node-redis/json/-/json-1.0.2.tgz",
"integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g==",
"requires": {}
"integrity": "sha512-qVRgn8WfG46QQ08CghSbY4VhHFgaTY71WjpwRBGEuqGPfWwfRcIf3OqSpR7Q/45X+v3xd8mvYjywqh0wqJ8T+g=="
},
"@node-redis/search": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@node-redis/search/-/search-1.0.5.tgz",
"integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ==",
"requires": {}
"integrity": "sha512-MCOL8iCKq4v+3HgEQv8zGlSkZyXSXtERgrAJ4TSryIG/eLFy84b57KmNNa/V7M1Q2Wd2hgn2nPCGNcQtk1R1OQ=="
},
"@node-redis/time-series": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@node-redis/time-series/-/time-series-1.0.2.tgz",
"integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA==",
"requires": {}
"integrity": "sha512-HGQ8YooJ8Mx7l28tD7XjtB3ImLEjlUxG1wC1PAjxu6hPJqjPshUZxAICzDqDjtIbhDTf48WXXUcx8TQJB1XTKA=="
},
"@nodelib/fs.scandir": {
"version": "2.1.5",
@ -7725,25 +7720,25 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz",
"integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==",
"devOptional": true
"dev": true
},
"@tsconfig/node12": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz",
"integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==",
"devOptional": true
"dev": true
},
"@tsconfig/node14": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
"integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
"devOptional": true
"dev": true
},
"@tsconfig/node16": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz",
"integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==",
"devOptional": true
"dev": true
},
"@types/json-schema": {
"version": "7.0.11",
@ -7898,20 +7893,19 @@
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
"integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
"devOptional": true
"dev": true
},
"acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dev": true,
"requires": {}
"dev": true
},
"acorn-walk": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz",
"integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==",
"devOptional": true
"dev": true
},
"aggregate-error": {
"version": "3.1.0",
@ -7987,7 +7981,7 @@
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"devOptional": true
"dev": true
},
"argparse": {
"version": "2.0.1",
@ -8274,7 +8268,7 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"devOptional": true
"dev": true
},
"cross-env": {
"version": "7.0.3",
@ -8462,8 +8456,7 @@
"version": "8.5.0",
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
"integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
"dev": true,
"requires": {}
"dev": true
},
"eslint-scope": {
"version": "5.1.1",
@ -9233,7 +9226,7 @@
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"devOptional": true
"dev": true
},
"memory-pager": {
"version": "1.5.0",
@ -9629,8 +9622,7 @@
"pg-pool": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.1.tgz",
"integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ==",
"requires": {}
"integrity": "sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ=="
},
"pg-protocol": {
"version": "1.5.0",
@ -11339,8 +11331,7 @@
"ws": {
"version": "7.5.7",
"bundled": true,
"dev": true,
"requires": {}
"dev": true
},
"yallist": {
"version": "4.0.0",
@ -11500,7 +11491,7 @@
"version": "10.9.1",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
"devOptional": true,
"dev": true,
"requires": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
@ -11521,7 +11512,7 @@
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"devOptional": true
"dev": true
}
}
},
@ -11679,7 +11670,7 @@
"version": "4.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz",
"integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==",
"devOptional": true
"dev": true
},
"unicode-length": {
"version": "2.0.2",
@ -11738,7 +11729,7 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"devOptional": true
"dev": true
},
"webcrypto-core": {
"version": "1.7.5",
@ -11928,7 +11919,7 @@
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"devOptional": true
"dev": true
}
}
}

View File

@ -72,4 +72,4 @@
"engines": {
"node": ">=14.18.1 <=16.x"
}
}
}

View File

@ -1,6 +1,7 @@
import crypto from 'crypto';
import { promisify } from 'util';
import { deflateRaw } from 'zlib';
import * as jose from 'jose';
import * as dbutils from '../db/utils';
import * as metrics from '../opentelemetry/metrics';
@ -21,7 +22,14 @@ import { JacksonError } from './error';
import * as allowed from './oauth/allowed';
import * as codeVerifier from './oauth/code-verifier';
import * as redirect from './oauth/redirect';
import { relayStatePrefix, IndexNames, OAuthErrorResponse, getErrorMessage } from './utils';
import {
relayStatePrefix,
IndexNames,
OAuthErrorResponse,
getErrorMessage,
loadJWSPrivateKey,
isJWSKeyPairLoaded,
} from './utils';
const deflateRawAsync = promisify(deflateRaw);
@ -57,6 +65,10 @@ function getEncodedTenantProduct(param: string): { tenant: string | null; produc
}
}
function getScopeValues(scope?: string): string[] {
return typeof scope === 'string' ? scope.split(' ').filter((s) => s.length > 0) : [];
}
export class OAuthController implements IOAuthController {
private configStore: Storable;
private sessionStore: Storable;
@ -131,6 +143,7 @@ export class OAuthController implements IOAuthController {
product,
access_type,
scope,
nonce,
code_challenge,
code_challenge_method = '',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -148,6 +161,8 @@ export class OAuthController implements IOAuthController {
}
let samlConfig;
const requestedScopes = getScopeValues(scope);
const requestedOIDCFlow = requestedScopes.includes('openid');
if (tenant && product) {
const samlConfigs = await this.configStore.getByIndex({
@ -188,8 +203,11 @@ export class OAuthController implements IOAuthController {
if (!sp && access_type) {
sp = getEncodedTenantProduct(access_type);
}
if (!sp && scope) {
sp = getEncodedTenantProduct(scope);
if (!sp && requestedScopes) {
const encodedParams = requestedScopes.find((scope) => scope.includes('=') && scope.includes('&')); // for now assume only one encoded param i.e. for tenant/product
if (encodedParams) {
sp = getEncodedTenantProduct(encodedParams);
}
}
if (sp && sp.tenant && sp.product) {
requestedTenant = sp.tenant;
@ -248,6 +266,20 @@ export class OAuthController implements IOAuthController {
throw new JacksonError('Redirect URL is not allowed.', 403);
}
if (
requestedOIDCFlow &&
(!this.opts.openid.jwtSigningKeys || !isJWSKeyPairLoaded(this.opts.openid.jwtSigningKeys))
) {
return {
redirect_url: OAuthErrorResponse({
error: 'server_error',
error_description:
'OAuth server not configured correctly for openid flow, check if JWT signing keys are loaded',
redirect_uri,
}),
};
}
if (!state) {
return {
redirect_url: OAuthErrorResponse({
@ -303,7 +335,7 @@ export class OAuthController implements IOAuthController {
const sessionId = crypto.randomBytes(16).toString('hex');
const requested = { client_id, state } as Record<string, string>;
const requested = { client_id, state } as Record<string, string | boolean | string[]>;
if (requestedTenant) {
requested.tenant = requestedTenant;
}
@ -313,6 +345,15 @@ export class OAuthController implements IOAuthController {
if (idp_hint) {
requested.idp_hint = idp_hint;
}
if (requestedOIDCFlow) {
requested.oidc = true;
if (nonce) {
requested.nonce = nonce;
}
}
if (requestedScopes) {
requested.scope = requestedScopes;
}
await this.sessionStore.put(sessionId, {
id: samlReq.id,
@ -633,6 +674,33 @@ export class OAuthController implements IOAuthController {
...codeVal.profile,
requested: codeVal.requested,
};
const requestedOIDCFlow = !!codeVal.requested.oidc;
const requestHasNonce = !!codeVal.requested.nonce;
if (requestedOIDCFlow) {
const { jwtSigningKeys, jwsAlg } = this.opts.openid;
if (!jwtSigningKeys || !isJWSKeyPairLoaded(jwtSigningKeys)) {
throw new JacksonError('JWT signing keys are not loaded', 500);
}
let claims: Record<string, string> = requestHasNonce ? { nonce: codeVal.requested.nonce } : {};
claims = {
...claims,
id: codeVal.profile.claims.id,
email: codeVal.profile.claims.email,
firstName: codeVal.profile.claims.firstName,
lastName: codeVal.profile.claims.lastName,
};
const signingKey = await loadJWSPrivateKey(jwtSigningKeys.private, jwsAlg);
const id_token = await new jose.SignJWT(claims)
.setProtectedHeader({ alg: jwsAlg })
.setIssuedAt()
.setIssuer(this.opts.samlAudience || '')
.setSubject(codeVal.profile.claims.id)
.setAudience(tokenVal.requested.client_id)
.setExpirationTime(`${this.opts.db.ttl}s`) // identity token only really needs to be valid long enough for it to be verified by the client application.
.sign(signingKey);
tokenVal.id_token = id_token;
tokenVal.claims.sub = codeVal.profile.claims.id;
}
await this.tokenStore.put(token, tokenVal);
@ -643,11 +711,17 @@ export class OAuthController implements IOAuthController {
// ignore error
}
return {
const tokenResponse: OAuthTokenRes = {
access_token: token,
token_type: 'bearer',
expires_in: this.opts.db.ttl!,
};
if (requestedOIDCFlow) {
tokenResponse.id_token = tokenVal.id_token;
}
return tokenResponse;
}
/**

View File

@ -0,0 +1,40 @@
import { IOidcDiscoveryController, JacksonOption } from '../typings';
import { JacksonError } from './error';
import { exportPublicKeyJWK, generateJwkThumbprint, importJWTPublicKey, isJWSKeyPairLoaded } from './utils';
export class OidcDiscoveryController implements IOidcDiscoveryController {
private opts: JacksonOption;
constructor({ opts }) {
this.opts = opts;
}
openidConfig() {
return {
issuer: this.opts.samlAudience!,
authorization_endpoint: `${this.opts.externalUrl}/api/oauth/authorize`,
token_endpoint: `${this.opts.externalUrl}/api/oauth/token`,
userinfo_endpoint: `${this.opts.externalUrl}/api/oauth/userinfo`,
jwks_uri: `${this.opts.externalUrl}/oauth/jwks`,
response_types_supported: ['code'],
subject_types_supported: ['public'],
id_token_signing_alg_values_supported: ['RS256'],
grant_types_supported: ['authorization_code'],
code_challenge_methods_supported: ['plain', 'S256'],
};
}
async jwks() {
const { jwtSigningKeys, jwsAlg } = this.opts.openid;
if (!jwtSigningKeys || !isJWSKeyPairLoaded(jwtSigningKeys)) {
throw new JacksonError('JWT signing keys are not loaded', 501);
}
const importedPublicKey = await importJWTPublicKey(jwtSigningKeys.public, jwsAlg);
const publicKeyJWK = await exportPublicKeyJWK(importedPublicKey);
const jwkThumbprint = await generateJwkThumbprint(publicKeyJWK);
const jwks = {
keys: [{ ...publicKeyJWK, kid: jwkThumbprint, alg: jwsAlg, use: 'sig' }],
};
return jwks;
}
}

View File

@ -1,6 +1,7 @@
import type { OAuthErrorHandlerParams } from '../typings';
import { JacksonError } from './error';
import * as redirect from './oauth/redirect';
import * as jose from 'jose';
export enum IndexNames {
EntityID = 'entityID',
@ -33,3 +34,32 @@ export function getErrorMessage(error: unknown) {
}
return String(error);
}
export async function loadJWSPrivateKey(key: string, alg: string): Promise<jose.KeyLike> {
const pkcs8 = Buffer.from(key, 'base64').toString('ascii');
const privateKey = await jose.importPKCS8(pkcs8, alg);
return privateKey;
}
export function isJWSKeyPairLoaded(jwsKeyPair: { private: string; public: string }) {
if (!jwsKeyPair.private || !jwsKeyPair.public) {
return false;
}
return true;
}
export const importJWTPublicKey = async (key: string, jwsAlg: string): Promise<jose.KeyLike> => {
const spki = Buffer.from(key, 'base64').toString('ascii');
const publicKey = await jose.importSPKI(spki, jwsAlg);
return publicKey;
};
export const exportPublicKeyJWK = async (key: jose.KeyLike): Promise<jose.JWK> => {
const publicJWK = await jose.exportJWK(key);
return publicJWK;
};
export const generateJwkThumbprint = async (jwk: jose.JWK): Promise<string> => {
const thumbprint = await jose.calculateJwkThumbprint(jwk);
return thumbprint;
};

View File

@ -3,6 +3,7 @@ import { APIController } from './controller/api';
import { OAuthController } from './controller/oauth';
import { HealthCheckController } from './controller/health-check';
import { LogoutController } from './controller/logout';
import { OidcDiscoveryController } from './controller/oidc-discovery';
import DB from './db/db';
import defaultDb from './db/defaultDb';
@ -30,6 +31,9 @@ const defaultOpts = (opts: JacksonOption): JacksonOption => {
newOpts.clientSecretVerifier = newOpts.clientSecretVerifier || 'dummy';
newOpts.db.pageLimit = newOpts.db.pageLimit || 50;
newOpts.openid = newOpts.openid || {};
newOpts.openid.jwsAlg = newOpts.openid.jwsAlg || 'RS256';
return newOpts;
};
@ -41,6 +45,7 @@ export const controllers = async (
adminController: AdminController;
logoutController: LogoutController;
healthCheckController: HealthCheckController;
oidcDiscoveryController: OidcDiscoveryController;
}> => {
opts = defaultOpts(opts);
@ -69,7 +74,7 @@ export const controllers = async (
sessionStore,
opts,
});
const oidcDiscoveryController = new OidcDiscoveryController({ opts });
// write pre-loaded config if present
if (opts.preLoadedConfig && opts.preLoadedConfig.length > 0) {
const configs = await readConfig(opts.preLoadedConfig);
@ -91,6 +96,7 @@ export const controllers = async (
adminController,
logoutController,
healthCheckController,
oidcDiscoveryController,
};
};

View File

@ -1,3 +1,5 @@
import { type JWK } from 'jose';
export type IdPConfig = {
defaultRedirectUrl: string;
redirectUrl: string[] | string;
@ -37,6 +39,26 @@ export interface IHealthCheckController {
}>;
init(): Promise<void>;
}
export interface IOidcDiscoveryController {
openidConfig(): {
issuer: string;
authorization_endpoint: string;
token_endpoint: string;
userinfo_endpoint: string;
jwks_uri: string;
response_types_supported: Array<string>;
subject_types_supported: Array<string>;
id_token_signing_alg_values_supported: Array<string>;
grant_types_supported: Array<string>;
code_challenge_methods_supported: Array<string>;
};
jwks(): Promise<{
keys: JWK[];
}>;
}
export interface OAuthReqBody {
response_type: 'code';
client_id: string;
@ -46,6 +68,7 @@ export interface OAuthReqBody {
product?: string;
access_type?: string;
scope?: string;
nonce?: string;
code_challenge: string;
code_challenge_method: 'plain' | 'S256' | '';
provider: 'saml';
@ -68,12 +91,14 @@ export interface OAuthTokenReq {
export interface OAuthTokenRes {
access_token: string;
id_token?: string;
token_type: 'bearer';
expires_in: number;
}
export interface Profile {
id: string;
sub?: string;
email: string;
firstName: string;
lastName: string;
@ -133,6 +158,13 @@ export interface JacksonOption {
db: DatabaseOption;
clientSecretVerifier?: string;
idpDiscoveryPath?: string;
openid: {
jwsAlg: string;
jwtSigningKeys?: {
private: string;
public: string;
};
};
}
export interface SLORequestParams {

View File

@ -18,6 +18,10 @@ const OPTIONS = <JacksonOption>{
db: {
engine: 'mem',
},
openid: {
jwtSigningKeys: { private: 'PRIVATE_KEY', public: 'PUBLIC_KEY' },
jwsAlg: 'RS256',
},
};
tap.before(async () => {

View File

@ -23,6 +23,13 @@ export const authz_request_normal_with_scope: Partial<OAuthReqBody> = {
client_id: 'dummy',
};
export const authz_request_normal_oidc_flow: Partial<OAuthReqBody> = {
redirect_uri: boxyhq.defaultRedirectUrl,
state: 'state-123',
scope: `openid`,
client_id: `tenant=${boxyhq.tenant}&product=${boxyhq.product}`,
};
export const redirect_uri_not_set: Partial<OAuthReqBody> = {
redirect_uri: undefined,
state: 'state',

View File

@ -20,6 +20,10 @@ const options = <JacksonOption>{
db: {
engine: 'mem',
},
openid: {
jwtSigningKeys: { private: 'PRIVATE_KEY', public: 'PUBLIC_KEY' },
jwsAlg: 'RS256',
},
};
// TODO: Move this to a helper file

View File

@ -1,5 +1,6 @@
import crypto from 'crypto';
import { promises as fs } from 'fs';
import * as utils from '../src/controller/utils';
import path from 'path';
import {
IOAuthController,
@ -14,8 +15,10 @@ import tap from 'tap';
import { JacksonError } from '../src/controller/error';
import readConfig from '../src/read-config';
import saml from '@boxyhq/saml20';
import * as jose from 'jose';
import {
authz_request_normal,
authz_request_normal_oidc_flow,
authz_request_normal_with_access_type,
authz_request_normal_with_scope,
bodyWithDummyCredentials,
@ -34,6 +37,7 @@ import {
let apiController: IAPIController;
let oauthController: IOAuthController;
let keyPair: jose.GenerateKeyPairResult;
const code = '1234567890';
const token = '24c1550190dd6a5a9bd6fe2a8ff69d593121c7b9';
@ -48,6 +52,10 @@ const options = <JacksonOption>{
engine: 'mem',
},
clientSecretVerifier: 'TOP-SECRET',
openid: {
jwtSigningKeys: { private: 'PRIVATE_KEY', public: 'PUBLIC_KEY' },
jwsAlg: 'RS256',
},
};
const configRecords: Array<any> = [];
@ -61,6 +69,8 @@ const addMetadata = async (metadataPath) => {
};
tap.before(async () => {
keyPair = await jose.generateKeyPair('RS256', { modulusLength: 3072 });
const controller = await (await import('../src/index')).default(options);
apiController = controller.apiController;
@ -388,76 +398,155 @@ tap.test('token()', (t) => {
t.end();
});
t.test('Should return the `access_token`/`userprofile` for a valid request', async (t) => {
t.test('encoded client_id', async (t) => {
const body = token_req_encoded_client_id;
const stubRandomBytes = sinon
.stub(crypto, 'randomBytes')
.onFirstCall()
t.test(
'Should return the tokens [id_token (if openid requested), access_token] and userprofile for a valid request',
async (t) => {
t.test('encoded client_id', async (t) => {
const body = token_req_encoded_client_id;
const stubRandomBytes = sinon
.stub(crypto, 'randomBytes')
.onFirstCall()
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.returns(token);
const response = await oauthController.token(<OAuthTokenReq>body);
t.ok(stubRandomBytes.calledOnce, 'randomBytes called once');
t.ok('access_token' in response, 'includes access_token');
t.ok('token_type' in response, 'includes token_type');
t.ok('expires_in' in response, 'includes expires_in');
t.notOk('id_token' in response, 'does not include id_token');
t.match(response.access_token, token);
t.match(response.token_type, 'bearer');
t.match(response.expires_in, 300);
stubRandomBytes.restore();
t.end();
});
t.test('unencoded client_id', async (t) => {
// have to call authorize, because previous happy path deletes the code.
const authBody = authz_request_normal;
const { redirect_url } = await oauthController.authorize(<OAuthReqBody>authBody);
const relayState = new URLSearchParams(new URL(redirect_url!).search).get('RelayState');
const rawResponse = await fs.readFile(path.join(__dirname, '/data/saml_response'), 'utf8');
const responseBody = {
SAMLResponse: rawResponse,
RelayState: relayState,
};
const stubValidate = sinon
.stub(saml, 'validate')
.resolves({ audience: '', claims: {}, issuer: '', sessionIndex: '' });
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
.returns(token);
const stubRandomBytes = sinon.stub(crypto, 'randomBytes').returns(code).onSecondCall().returns(token);
const response = await oauthController.token(<OAuthTokenReq>body);
await oauthController.samlResponse(<SAMLResponsePayload>responseBody);
t.ok(stubRandomBytes.calledOnce, 'randomBytes called once');
t.ok('access_token' in response, 'includes access_token');
t.ok('token_type' in response, 'includes token_type');
t.ok('expires_in' in response, 'includes expires_in');
t.match(response.access_token, token);
t.match(response.token_type, 'bearer');
t.match(response.expires_in, 300);
const body = token_req_unencoded_client_id_gen(configRecords);
stubRandomBytes.restore();
const tokenRes = await oauthController.token(<OAuthTokenReq>body);
t.end();
});
t.ok('access_token' in tokenRes, 'includes access_token');
t.ok('token_type' in tokenRes, 'includes token_type');
t.ok('expires_in' in tokenRes, 'includes expires_in');
t.notOk('id_token' in tokenRes, 'does not include id_token');
t.match(tokenRes.access_token, token);
t.match(tokenRes.token_type, 'bearer');
t.match(tokenRes.expires_in, 300);
t.test('unencoded client_id', async (t) => {
// have to call authorize, because previous happy path deletes the code.
const authBody = authz_request_normal;
const profile = await oauthController.userInfo(tokenRes.access_token);
const { redirect_url } = await oauthController.authorize(<OAuthReqBody>authBody);
t.notOk('sub' in profile, 'does not include sub');
t.equal(profile.requested.client_id, authz_request_normal.client_id);
t.equal(profile.requested.state, authz_request_normal.state);
t.equal(profile.requested.tenant, new URLSearchParams(authz_request_normal.client_id).get('tenant'));
t.equal(
profile.requested.product,
new URLSearchParams(authz_request_normal.client_id).get('product')
);
const relayState = new URLSearchParams(new URL(redirect_url!).search).get('RelayState');
stubRandomBytes.restore();
stubValidate.restore();
const rawResponse = await fs.readFile(path.join(__dirname, '/data/saml_response'), 'utf8');
const responseBody = {
SAMLResponse: rawResponse,
RelayState: relayState,
};
t.end();
});
sinon.stub(saml, 'validate').resolves({ audience: '', claims: {}, issuer: '', sessionIndex: '' });
t.test('openid flow', async (t) => {
const authBody = authz_request_normal_oidc_flow;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const stubRandomBytes = sinon.stub(crypto, 'randomBytes').returns(code).onSecondCall().returns(token);
const { redirect_url } = await oauthController.authorize(<OAuthReqBody>authBody);
await oauthController.samlResponse(<SAMLResponsePayload>responseBody);
const relayState = new URLSearchParams(new URL(redirect_url!).search).get('RelayState');
const body = token_req_unencoded_client_id_gen(configRecords);
const rawResponse = await fs.readFile(path.join(__dirname, '/data/saml_response'), 'utf8');
const responseBody = {
SAMLResponse: rawResponse,
RelayState: relayState,
};
const tokenRes = await oauthController.token(<OAuthTokenReq>body);
const stubLoadJWSPrivateKey = sinon.stub(utils, 'loadJWSPrivateKey').resolves(keyPair.privateKey);
const stubValidate = sinon.stub(saml, 'validate').resolves({
audience: '',
claims: { id: 'id', firstName: 'john', lastName: 'doe', email: 'johndoe@example.com' },
issuer: '',
sessionIndex: '',
});
t.ok('access_token' in tokenRes, 'includes access_token');
t.ok('token_type' in tokenRes, 'includes token_type');
t.ok('expires_in' in tokenRes, 'includes expires_in');
t.match(tokenRes.access_token, token);
t.match(tokenRes.token_type, 'bearer');
t.match(tokenRes.expires_in, 300);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const stubRandomBytes = sinon.stub(crypto, 'randomBytes').returns(code).onSecondCall().returns(token);
const profile = await oauthController.userInfo(tokenRes.access_token);
await oauthController.samlResponse(<SAMLResponsePayload>responseBody);
t.equal(profile.requested.client_id, authz_request_normal.client_id);
t.equal(profile.requested.state, authz_request_normal.state);
t.equal(profile.requested.tenant, new URLSearchParams(authz_request_normal.client_id).get('tenant'));
t.equal(profile.requested.product, new URLSearchParams(authz_request_normal.client_id).get('product'));
const body = token_req_encoded_client_id;
stubRandomBytes.restore();
const tokenRes = await oauthController.token(<OAuthTokenReq>body);
t.end();
});
});
t.ok('access_token' in tokenRes, 'includes access_token');
t.ok('token_type' in tokenRes, 'includes token_type');
t.ok('expires_in' in tokenRes, 'includes expires_in');
t.ok('id_token' in tokenRes, 'includes id_token');
if (tokenRes.id_token) {
const claims = jose.decodeJwt(tokenRes.id_token);
const { protectedHeader } = await jose.jwtVerify(tokenRes.id_token, keyPair.publicKey);
t.match(protectedHeader.alg, options.openid.jwsAlg);
t.match(claims.aud, authz_request_normal_oidc_flow.client_id);
t.match(claims.iss, options.samlAudience);
}
t.match(tokenRes.access_token, token);
t.match(tokenRes.token_type, 'bearer');
t.match(tokenRes.expires_in, 300);
const profile = await oauthController.userInfo(tokenRes.access_token);
t.equal(profile.sub, 'id');
t.equal(profile.requested.client_id, authz_request_normal_oidc_flow.client_id);
t.equal(profile.requested.state, authz_request_normal_oidc_flow.state);
t.equal(
profile.requested.tenant,
new URLSearchParams(authz_request_normal_oidc_flow.client_id).get('tenant')
);
t.equal(
profile.requested.product,
new URLSearchParams(authz_request_normal_oidc_flow.client_id).get('product')
);
stubRandomBytes.restore();
stubValidate.restore();
stubLoadJWSPrivateKey.restore();
t.end();
});
}
);
t.end();
});

497
package-lock.json generated
View File

@ -17,6 +17,7 @@
"@opentelemetry/sdk-metrics-base": "0.27.0",
"@supabase/ui": "0.36.5",
"cors": "2.8.5",
"jose": "4.8.1",
"micromatch": "4.0.5",
"next": "12.2.2",
"next-auth": "4.10.0",
@ -1353,119 +1354,119 @@
}
},
"node_modules/@peculiar/asn1-cms": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.1.9.tgz",
"integrity": "sha512-Rlcd1hL1BK/IgWiR3wKTxpfei3OLhW0GiAPdqR6aEYCXqV9UCTFsR7dXI8eyh1wkWXx2VJthdbh+ky+w5adh9w==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.2.0.tgz",
"integrity": "sha512-8lJe4OC83NUA/rP4/J8Zjd3Lsxbx8lkBcVWDkUJNFAsfWqDdhlwmNyhg8h0x0jr8NJ6QOCTwJ18VIs58X54/ow==",
"dependencies": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"@peculiar/asn1-x509-attr": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"@peculiar/asn1-x509-attr": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-csr": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.1.9.tgz",
"integrity": "sha512-ncmR3AaWrm+DC1hsouxfsgDb6ZfhWfwIoqS+esxDlk0honmMfdffrDtXoxxJIQ8Njrk7AKVPMSPyxLeCyPC0tw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.2.0.tgz",
"integrity": "sha512-kj/pjv+Mn0culffDaxjaInZPvoAcJUuJvk3JUeGNoe5N9pIK6Bcrtt7dfA1u+NgI7K1y1TBDAzTQoYxfxmsRbA==",
"dependencies": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-ecc": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.1.9.tgz",
"integrity": "sha512-1+ckVKJ8Bx+WatKcH5PtafWb1JxAZM+u9D6wFNYt4AKzWAwx2dmzkBMdqQmCXRk4fOvaJTr9CAfsJu0g7m6zgQ==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.2.0.tgz",
"integrity": "sha512-/fMm9loLLLQcbZSGM0B4eNedgmagJPvwqklVRtG8OBTLidKZN3U/dmiemiKx8hf3w5IYwu52OxMSch88BHpuGQ==",
"dependencies": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-pfx": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.1.9.tgz",
"integrity": "sha512-JwjYcPFbteDV9+0Pqz6um2oksh2RrngreXxTLgLZ/ENEU9xfmn4IEDAVN67e3V8Ei+ujBjk1UsjrEnCwRArMgg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.2.0.tgz",
"integrity": "sha512-FrGzBQpVr2IwY6iYcmN5cp8IRGDhDsM+EzBRUs6fodvAGm8cchGid9WZszqRpBG8YCOBZXZ38bR0r6gdmH9q7g==",
"dependencies": {
"@peculiar/asn1-cms": "^2.1.9",
"@peculiar/asn1-pkcs8": "^2.1.9",
"@peculiar/asn1-rsa": "^2.1.9",
"@peculiar/asn1-schema": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-cms": "^2.2.0",
"@peculiar/asn1-pkcs8": "^2.2.0",
"@peculiar/asn1-rsa": "^2.2.0",
"@peculiar/asn1-schema": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-pkcs8": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.1.9.tgz",
"integrity": "sha512-KtpVZ/ysOcPvIgHwrqm1BLDGCwnPjxiWlqYfvldZvK0SKXx7ogWWMoh/3M8nEdmu/dT1QdOizt1ErLoZ7xVxOw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.2.0.tgz",
"integrity": "sha512-tbVTUYevN1PftXz8qKvvddroGC1nHZtvsVCTRMGwMV9x5MNs6YWqxAkM5jAV75PVfwQvWSbrvQmQlo2epBYjtg==",
"dependencies": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-pkcs9": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.1.9.tgz",
"integrity": "sha512-QBITHLAdJgEAKDYEaKbmXVdhwi8aIS9JTThXr+5BgPO8vJE3/OvogLnLZbgO3eOT5IarImrHiqiGaPLVVQDbTQ==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.2.0.tgz",
"integrity": "sha512-gJPS7/64OjQSFPFBys2klRb32bKBCraPOmrfzuTM3FvUh+yroomG8RBtnDR4Xn8QxzOIwvMape+FqPsNQ2ldPg==",
"dependencies": {
"@peculiar/asn1-cms": "^2.1.9",
"@peculiar/asn1-pfx": "^2.1.9",
"@peculiar/asn1-pkcs8": "^2.1.9",
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"@peculiar/asn1-x509-attr": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-cms": "^2.2.0",
"@peculiar/asn1-pfx": "^2.2.0",
"@peculiar/asn1-pkcs8": "^2.2.0",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"@peculiar/asn1-x509-attr": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-rsa": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.1.9.tgz",
"integrity": "sha512-O7TQnJp7OFZAI9a2tOHHlDbFub9v1VzJr3aKelpBBKDebwgTgNDgXVDapXh/4SDMShWGsSu3HKbYkZORnlDr1A==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.2.0.tgz",
"integrity": "sha512-t6CIvXYpjEzos5iXwg6QSFdKce3c3Iw9M658qARdIpbWDvosNHeVn7e97IYdmJU0sDS4ech1aS2GLuKFFKmcew==",
"dependencies": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-schema": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.1.9.tgz",
"integrity": "sha512-Ipio+pXGpL/Vb0qB4GnOgFMgc1RAhKHOVy24rQYLvmOAVp9z/aFb+VdIiQH09NjgvGVmaWOUqSWd9vRHk3xbrg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.2.0.tgz",
"integrity": "sha512-1ENEJNY7Lwlua/1wvzpYP194WtjQBfFxvde2FlzfBFh/ln6wvChrtxlORhbKEnYswzn6fOC4c7HdC5izLPMTJg==",
"dependencies": {
"asn1js": "^3.0.4",
"asn1js": "^3.0.5",
"pvtsutils": "^1.3.2",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-x509": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.1.9.tgz",
"integrity": "sha512-mqU8ZlWaBfDSG/iafCYKOa9N7scXiaXfHNgOyqG0GIPZGGjNeeBjgLRrlup6L8/Ipvlun5VIm7vNJdR8/XIqtg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.2.0.tgz",
"integrity": "sha512-YORcf7XFy9TKWGnPmK2MNQqAJgIvGNaCrK8vUF/ZRO+BnahZJOgX2HuEiEmoAozZpSlyJnI6CGJOABOjPiDucA==",
"dependencies": {
"@peculiar/asn1-schema": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"asn1js": "^3.0.5",
"ipaddr.js": "^2.0.1",
"pvtsutils": "^1.3.2",
"tslib": "^2.4.0"
}
},
"node_modules/@peculiar/asn1-x509-attr": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.1.9.tgz",
"integrity": "sha512-tJ7i0ctqREk6XtlRGto81hC3hvp1Dj790N4fnhJQzHC7ZYC+zZSoO/FZr+UxJMIphDDV2BQvCbObJ1xmOEDijA==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.2.0.tgz",
"integrity": "sha512-SWX6pYWTxNy2bBcvOJriBhC7CaqvD9JQ+XUWrmiVrbw8hmzJS3Jcv6JMRfSuitjmmEE+W2wvY0s8zmghDrQK+A==",
"dependencies": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
@ -1496,17 +1497,17 @@
}
},
"node_modules/@peculiar/x509": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.7.2.tgz",
"integrity": "sha512-yMECIsGaRBWV2GFLVxTFD6L9zdRcaFWOQO5MKPHACE4vz/7GJzRfW3trehL5KnvSnQHPXAmt8zPGLctjn488/w==",
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.7.3.tgz",
"integrity": "sha512-TKOAH/o66qWYlODqwszPPKBYPKw9p4caaGfgPd1319IKO1PGPNQ4WK1MzCDUuWXyXDD2CAbP0mxmq9MuHwersA==",
"dependencies": {
"@peculiar/asn1-cms": "^2.1.9",
"@peculiar/asn1-csr": "^2.1.9",
"@peculiar/asn1-ecc": "^2.1.9",
"@peculiar/asn1-pkcs9": "^2.1.9",
"@peculiar/asn1-rsa": "^2.1.9",
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"@peculiar/asn1-cms": "^2.2.0",
"@peculiar/asn1-csr": "^2.2.0",
"@peculiar/asn1-ecc": "^2.2.0",
"@peculiar/asn1-pkcs9": "^2.2.0",
"@peculiar/asn1-rsa": "^2.2.0",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"pvtsutils": "^1.3.2",
"reflect-metadata": "^0.1.13",
"tslib": "^2.4.0",
@ -1933,23 +1934,6 @@
"node": ">=10"
}
},
"node_modules/@typescript-eslint/scope-manager": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz",
"integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.30.5",
"@typescript-eslint/visitor-keys": "5.30.5"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/type-utils": {
"version": "5.30.6",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.6.tgz",
@ -1976,61 +1960,6 @@
}
}
},
"node_modules/@typescript-eslint/types": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz",
"integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/typescript-estree": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz",
"integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.30.5",
"@typescript-eslint/visitor-keys": "5.30.5",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/utils": {
"version": "5.30.6",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.6.tgz",
@ -2144,23 +2073,6 @@
"node": ">=10"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz",
"integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.30.5",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
@ -5188,9 +5100,9 @@
}
},
"node_modules/jose": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/jose/-/jose-4.6.0.tgz",
"integrity": "sha512-0hNAkhMBNi4soKSAX4zYOFV+aqJlEz/4j4fregvasJzEVtjDChvWqRjPvHwLqr5hx28Ayr6bsOs1Kuj87V0O8w==",
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/jose/-/jose-4.8.1.tgz",
"integrity": "sha512-+/hpTbRcCw9YC0TOfN1W47pej4a9lRmltdOVdRLz5FP5UvUq3CenhXjQK7u/8NdMIIShMXYAh9VLPhc7TjhvFw==",
"funding": {
"url": "https://github.com/sponsors/panva"
}
@ -10995,7 +10907,7 @@
"@opentelemetry/api": "1.0.4",
"@opentelemetry/api-metrics": "0.27.0",
"@peculiar/webcrypto": "1.4.0",
"@peculiar/x509": "1.7.2",
"@peculiar/x509": "1.7.3",
"mongodb": "4.8.0",
"mysql2": "2.3.3",
"pg": "8.7.3",
@ -11011,7 +10923,7 @@
"@types/sinon": "10.0.12",
"@types/tap": "15.0.7",
"@typescript-eslint/eslint-plugin": "5.30.6",
"@typescript-eslint/parser": "5.30.5",
"@typescript-eslint/parser": "5.30.6",
"cross-env": "7.0.3",
"eslint": "8.20.0",
"eslint-config-prettier": "8.5.0",
@ -11031,33 +10943,6 @@
"resolved": "npm/@boxyhq/saml20@1.0.0-beta-1",
"link": true
},
"npm/node_modules/@typescript-eslint/parser": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz",
"integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==",
"dev": true,
"dependencies": {
"@typescript-eslint/scope-manager": "5.30.5",
"@typescript-eslint/types": "5.30.5",
"@typescript-eslint/typescript-estree": "5.30.5",
"debug": "^4.3.4"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"npm/node_modules/bson": {
"version": "4.6.5",
"resolved": "https://registry.npmjs.org/bson/-/bson-4.6.5.tgz",
@ -11551,12 +11436,12 @@
"@opentelemetry/api": "1.0.4",
"@opentelemetry/api-metrics": "0.27.0",
"@peculiar/webcrypto": "1.4.0",
"@peculiar/x509": "1.7.2",
"@peculiar/x509": "1.7.3",
"@types/node": "18.0.6",
"@types/sinon": "10.0.12",
"@types/tap": "15.0.7",
"@typescript-eslint/eslint-plugin": "5.30.6",
"@typescript-eslint/parser": "5.30.5",
"@typescript-eslint/parser": "5.30.6",
"cross-env": "7.0.3",
"eslint": "8.20.0",
"eslint-config-prettier": "8.5.0",
@ -11580,18 +11465,6 @@
"@boxyhq/saml20": {
"version": "file:npm/@boxyhq/saml20@1.0.0-beta-1"
},
"@typescript-eslint/parser": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz",
"integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==",
"dev": true,
"requires": {
"@typescript-eslint/scope-manager": "5.30.5",
"@typescript-eslint/types": "5.30.5",
"@typescript-eslint/typescript-estree": "5.30.5",
"debug": "^4.3.4"
}
},
"bson": {
"version": "4.6.5",
"resolved": "https://registry.npmjs.org/bson/-/bson-4.6.5.tgz",
@ -12069,119 +11942,119 @@
"integrity": "sha512-mMyQ9vjpuFqePkfe5bZVIf/H3Dmk6wA8Kjxff9RcO4kqzJo+Ek9pGKwZHpeMr7Eku0QhLXMCd7fNCSnEnRMubg=="
},
"@peculiar/asn1-cms": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.1.9.tgz",
"integrity": "sha512-Rlcd1hL1BK/IgWiR3wKTxpfei3OLhW0GiAPdqR6aEYCXqV9UCTFsR7dXI8eyh1wkWXx2VJthdbh+ky+w5adh9w==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-cms/-/asn1-cms-2.2.0.tgz",
"integrity": "sha512-8lJe4OC83NUA/rP4/J8Zjd3Lsxbx8lkBcVWDkUJNFAsfWqDdhlwmNyhg8h0x0jr8NJ6QOCTwJ18VIs58X54/ow==",
"requires": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"@peculiar/asn1-x509-attr": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"@peculiar/asn1-x509-attr": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-csr": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.1.9.tgz",
"integrity": "sha512-ncmR3AaWrm+DC1hsouxfsgDb6ZfhWfwIoqS+esxDlk0honmMfdffrDtXoxxJIQ8Njrk7AKVPMSPyxLeCyPC0tw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-csr/-/asn1-csr-2.2.0.tgz",
"integrity": "sha512-kj/pjv+Mn0culffDaxjaInZPvoAcJUuJvk3JUeGNoe5N9pIK6Bcrtt7dfA1u+NgI7K1y1TBDAzTQoYxfxmsRbA==",
"requires": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-ecc": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.1.9.tgz",
"integrity": "sha512-1+ckVKJ8Bx+WatKcH5PtafWb1JxAZM+u9D6wFNYt4AKzWAwx2dmzkBMdqQmCXRk4fOvaJTr9CAfsJu0g7m6zgQ==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-ecc/-/asn1-ecc-2.2.0.tgz",
"integrity": "sha512-/fMm9loLLLQcbZSGM0B4eNedgmagJPvwqklVRtG8OBTLidKZN3U/dmiemiKx8hf3w5IYwu52OxMSch88BHpuGQ==",
"requires": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-pfx": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.1.9.tgz",
"integrity": "sha512-JwjYcPFbteDV9+0Pqz6um2oksh2RrngreXxTLgLZ/ENEU9xfmn4IEDAVN67e3V8Ei+ujBjk1UsjrEnCwRArMgg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pfx/-/asn1-pfx-2.2.0.tgz",
"integrity": "sha512-FrGzBQpVr2IwY6iYcmN5cp8IRGDhDsM+EzBRUs6fodvAGm8cchGid9WZszqRpBG8YCOBZXZ38bR0r6gdmH9q7g==",
"requires": {
"@peculiar/asn1-cms": "^2.1.9",
"@peculiar/asn1-pkcs8": "^2.1.9",
"@peculiar/asn1-rsa": "^2.1.9",
"@peculiar/asn1-schema": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-cms": "^2.2.0",
"@peculiar/asn1-pkcs8": "^2.2.0",
"@peculiar/asn1-rsa": "^2.2.0",
"@peculiar/asn1-schema": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-pkcs8": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.1.9.tgz",
"integrity": "sha512-KtpVZ/ysOcPvIgHwrqm1BLDGCwnPjxiWlqYfvldZvK0SKXx7ogWWMoh/3M8nEdmu/dT1QdOizt1ErLoZ7xVxOw==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs8/-/asn1-pkcs8-2.2.0.tgz",
"integrity": "sha512-tbVTUYevN1PftXz8qKvvddroGC1nHZtvsVCTRMGwMV9x5MNs6YWqxAkM5jAV75PVfwQvWSbrvQmQlo2epBYjtg==",
"requires": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-pkcs9": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.1.9.tgz",
"integrity": "sha512-QBITHLAdJgEAKDYEaKbmXVdhwi8aIS9JTThXr+5BgPO8vJE3/OvogLnLZbgO3eOT5IarImrHiqiGaPLVVQDbTQ==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-pkcs9/-/asn1-pkcs9-2.2.0.tgz",
"integrity": "sha512-gJPS7/64OjQSFPFBys2klRb32bKBCraPOmrfzuTM3FvUh+yroomG8RBtnDR4Xn8QxzOIwvMape+FqPsNQ2ldPg==",
"requires": {
"@peculiar/asn1-cms": "^2.1.9",
"@peculiar/asn1-pfx": "^2.1.9",
"@peculiar/asn1-pkcs8": "^2.1.9",
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"@peculiar/asn1-x509-attr": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-cms": "^2.2.0",
"@peculiar/asn1-pfx": "^2.2.0",
"@peculiar/asn1-pkcs8": "^2.2.0",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"@peculiar/asn1-x509-attr": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-rsa": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.1.9.tgz",
"integrity": "sha512-O7TQnJp7OFZAI9a2tOHHlDbFub9v1VzJr3aKelpBBKDebwgTgNDgXVDapXh/4SDMShWGsSu3HKbYkZORnlDr1A==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-rsa/-/asn1-rsa-2.2.0.tgz",
"integrity": "sha512-t6CIvXYpjEzos5iXwg6QSFdKce3c3Iw9M658qARdIpbWDvosNHeVn7e97IYdmJU0sDS4ech1aS2GLuKFFKmcew==",
"requires": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-schema": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.1.9.tgz",
"integrity": "sha512-Ipio+pXGpL/Vb0qB4GnOgFMgc1RAhKHOVy24rQYLvmOAVp9z/aFb+VdIiQH09NjgvGVmaWOUqSWd9vRHk3xbrg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-schema/-/asn1-schema-2.2.0.tgz",
"integrity": "sha512-1ENEJNY7Lwlua/1wvzpYP194WtjQBfFxvde2FlzfBFh/ln6wvChrtxlORhbKEnYswzn6fOC4c7HdC5izLPMTJg==",
"requires": {
"asn1js": "^3.0.4",
"asn1js": "^3.0.5",
"pvtsutils": "^1.3.2",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-x509": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.1.9.tgz",
"integrity": "sha512-mqU8ZlWaBfDSG/iafCYKOa9N7scXiaXfHNgOyqG0GIPZGGjNeeBjgLRrlup6L8/Ipvlun5VIm7vNJdR8/XIqtg==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509/-/asn1-x509-2.2.0.tgz",
"integrity": "sha512-YORcf7XFy9TKWGnPmK2MNQqAJgIvGNaCrK8vUF/ZRO+BnahZJOgX2HuEiEmoAozZpSlyJnI6CGJOABOjPiDucA==",
"requires": {
"@peculiar/asn1-schema": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"asn1js": "^3.0.5",
"ipaddr.js": "^2.0.1",
"pvtsutils": "^1.3.2",
"tslib": "^2.4.0"
}
},
"@peculiar/asn1-x509-attr": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.1.9.tgz",
"integrity": "sha512-tJ7i0ctqREk6XtlRGto81hC3hvp1Dj790N4fnhJQzHC7ZYC+zZSoO/FZr+UxJMIphDDV2BQvCbObJ1xmOEDijA==",
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@peculiar/asn1-x509-attr/-/asn1-x509-attr-2.2.0.tgz",
"integrity": "sha512-SWX6pYWTxNy2bBcvOJriBhC7CaqvD9JQ+XUWrmiVrbw8hmzJS3Jcv6JMRfSuitjmmEE+W2wvY0s8zmghDrQK+A==",
"requires": {
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"asn1js": "^3.0.4",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"asn1js": "^3.0.5",
"tslib": "^2.4.0"
}
},
@ -12206,17 +12079,17 @@
}
},
"@peculiar/x509": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.7.2.tgz",
"integrity": "sha512-yMECIsGaRBWV2GFLVxTFD6L9zdRcaFWOQO5MKPHACE4vz/7GJzRfW3trehL5KnvSnQHPXAmt8zPGLctjn488/w==",
"version": "1.7.3",
"resolved": "https://registry.npmjs.org/@peculiar/x509/-/x509-1.7.3.tgz",
"integrity": "sha512-TKOAH/o66qWYlODqwszPPKBYPKw9p4caaGfgPd1319IKO1PGPNQ4WK1MzCDUuWXyXDD2CAbP0mxmq9MuHwersA==",
"requires": {
"@peculiar/asn1-cms": "^2.1.9",
"@peculiar/asn1-csr": "^2.1.9",
"@peculiar/asn1-ecc": "^2.1.9",
"@peculiar/asn1-pkcs9": "^2.1.9",
"@peculiar/asn1-rsa": "^2.1.9",
"@peculiar/asn1-schema": "^2.1.9",
"@peculiar/asn1-x509": "^2.1.9",
"@peculiar/asn1-cms": "^2.2.0",
"@peculiar/asn1-csr": "^2.2.0",
"@peculiar/asn1-ecc": "^2.2.0",
"@peculiar/asn1-pkcs9": "^2.2.0",
"@peculiar/asn1-rsa": "^2.2.0",
"@peculiar/asn1-schema": "^2.2.0",
"@peculiar/asn1-x509": "^2.2.0",
"pvtsutils": "^1.3.2",
"reflect-metadata": "^0.1.13",
"tslib": "^2.4.0",
@ -12538,16 +12411,6 @@
}
}
},
"@typescript-eslint/scope-manager": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz",
"integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.30.5",
"@typescript-eslint/visitor-keys": "5.30.5"
}
},
"@typescript-eslint/type-utils": {
"version": "5.30.6",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.6.tgz",
@ -12559,38 +12422,6 @@
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/types": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz",
"integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz",
"integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.30.5",
"@typescript-eslint/visitor-keys": "5.30.5",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"dependencies": {
"semver": {
"version": "7.3.7",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
"integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
"dev": true,
"requires": {
"lru-cache": "^6.0.0"
}
}
}
},
"@typescript-eslint/utils": {
"version": "5.30.6",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.6.tgz",
@ -12657,16 +12488,6 @@
}
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.30.5",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz",
"integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.30.5",
"eslint-visitor-keys": "^3.3.0"
}
},
"acorn": {
"version": "8.7.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
@ -14870,9 +14691,9 @@
}
},
"jose": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/jose/-/jose-4.6.0.tgz",
"integrity": "sha512-0hNAkhMBNi4soKSAX4zYOFV+aqJlEz/4j4fregvasJzEVtjDChvWqRjPvHwLqr5hx28Ayr6bsOs1Kuj87V0O8w=="
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/jose/-/jose-4.8.1.tgz",
"integrity": "sha512-+/hpTbRcCw9YC0TOfN1W47pej4a9lRmltdOVdRLz5FP5UvUq3CenhXjQK7u/8NdMIIShMXYAh9VLPhc7TjhvFw=="
},
"js-tokens": {
"version": "4.0.0",

View File

@ -51,6 +51,7 @@
"@opentelemetry/api": "1.0.4",
"@supabase/ui": "0.36.5",
"cors": "2.8.5",
"jose": "4.8.1",
"micromatch": "4.0.5",
"next": "12.2.2",
"next-auth": "4.10.0",
@ -90,4 +91,4 @@
"engines": {
"node": ">=14.18.1 <=16.x"
}
}
}

View File

@ -21,7 +21,7 @@ export const handler = async (req: NextApiRequest, res: NextApiResponse) => {
} else if (req.method === 'DELETE') {
res.status(204).end(await apiController.deleteConfig(req.body));
} else {
throw new Error('Method not allowed');
throw { message: 'Method not allowed', statusCode: 405 };
}
} catch (err: any) {
console.error('config api error:', err);

View File

@ -3,7 +3,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST') {
throw new Error(`Method ${req.method} not allowed`);
throw { message: 'Method not allowed', statusCode: 405 };
}
const { SAMLResponse, RelayState } = req.body;

View File

@ -3,7 +3,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'GET') {
throw new Error(`Method ${req.method} not allowed`);
throw { message: 'Method not allowed', statusCode: 405 };
}
const { nameId, tenant, product, redirectUrl } = req.query;

View File

@ -6,13 +6,14 @@ import { setErrorCookie } from '@lib/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
if (req.method !== 'GET') {
throw new Error('Method not allowed');
if (req.method !== 'GET' && req.method !== 'POST') {
throw { message: 'Method not allowed', statusCode: 405 };
}
const { oauthController } = await jackson();
const requestParams = req.method === 'GET' ? req.query : req.body;
const { redirect_url, authorize_form } = await oauthController.authorize(
req.query as unknown as OAuthReqBody
requestParams as unknown as OAuthReqBody
);
if (redirect_url) {
res.redirect(302, redirect_url);

13
pages/api/oauth/jwks.ts Normal file
View File

@ -0,0 +1,13 @@
import { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'GET') {
throw { message: 'Method not allowed', statusCode: 405 };
}
const { oidcDiscoveryController } = await jackson();
const jwks = await oidcDiscoveryController.jwks();
const response = JSON.stringify(jwks, null, 2);
res.status(200).setHeader('Content-Type', 'application/json').send(response);
}

View File

@ -6,7 +6,7 @@ import { setErrorCookie } from '@lib/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
if (req.method !== 'POST') {
throw new Error('Method not allowed');
throw { message: 'Method not allowed', statusCode: 405 };
}
const { oauthController } = await jackson();

View File

@ -8,7 +8,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await cors(req, res);
if (req.method !== 'POST') {
throw new Error('Method not allowed');
throw { message: 'Method not allowed', statusCode: 405 };
}
const { oauthController } = await jackson();

View File

@ -6,7 +6,7 @@ import { extractAuthToken } from '@lib/utils';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
if (req.method !== 'GET') {
throw new Error('Method not allowed');
throw { message: 'Method not allowed', statusCode: 405 };
}
const { oauthController } = await jackson();

View File

@ -20,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
} else if (req.method === 'DELETE') {
res.status(204).end(await apiController.deleteConfig(req.body));
} else {
throw new Error('Method not allowed');
throw { message: 'Method not allowed', statusCode: 405 };
}
} catch (err: any) {
console.error('config api error:', err);

View File

@ -19,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
res.status(204).end();
}
} else {
throw new Error('Method not allowed');
throw { message: 'Method not allowed', statusCode: 405 };
}
} catch (err: any) {
console.error('config api error:', err);

View File

@ -0,0 +1,13 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import jackson from '@lib/jackson';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'GET') {
throw { message: 'Method not allowed', statusCode: 405 };
}
const { oidcDiscoveryController } = await jackson();
const config = await oidcDiscoveryController.openidConfig();
const response = JSON.stringify(config, null, 2);
res.status(200).setHeader('Content-Type', 'application/json').send(response);
}