add actions
This commit is contained in:
24
.devcontainer/devcontainer.json
Normal file
24
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,24 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/typescript-node
|
||||
{
|
||||
"name": "Node.js & TypeScript",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-18-bullseye",
|
||||
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
"features": {
|
||||
"ghcr.io/devcontainers-contrib/features/redis-homebrew:1": {}
|
||||
}
|
||||
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [],
|
||||
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
// "postCreateCommand": "yarn install",
|
||||
|
||||
// Configure tool-specific properties.
|
||||
// "customizations": {},
|
||||
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
59
.env.example
Normal file
59
.env.example
Normal file
@@ -0,0 +1,59 @@
|
||||
# IP or host the API listens on ["0.0.0.0"]
|
||||
HOST="0.0.0.0"
|
||||
|
||||
# The port Directus will run on [8055]
|
||||
PORT=8055
|
||||
|
||||
PUBLIC_URL="http://localhost:8055"
|
||||
|
||||
AUTH_PROVIDER_CLIENT_ID=YOUR_CLIENT_ID
|
||||
AUTH_PROVIDER_CLIENT_SECRET=YOUR_CLIENT_SECRET
|
||||
AUTH_PROVIDER_ISSUER_URL=YOUR_ISSUER_URL
|
||||
AUTH_PROVIDER_TRUSTED=true
|
||||
AUTH_PROVIDER_JWKS_URL=YOUR_JWKS_URL
|
||||
AUTH_PROVIDER_JWT_ROLE_KEY=YOUR_JWT_ROLE_KEY
|
||||
AUTH_PROVIDER_JWT_ADMIN_KEY=YOUR_JWT_ADMIN_KEY
|
||||
AUTH_PROVIDER_APP_KEY=YOUR_APP_KEY
|
||||
AUTH_PROVIDER_JWT_USEDB=true
|
||||
|
||||
####################################################################################################
|
||||
### Redis
|
||||
REDIS_JWT_DB="directus-jwt"
|
||||
REDIS_DB="directus"
|
||||
REDIS_HOST="localhost"
|
||||
REDIS_PORT=6379
|
||||
|
||||
####################################################################################################
|
||||
### Database
|
||||
|
||||
# All DB_* environment variables are passed to the connection configuration of a Knex instance.
|
||||
# Based on your project's needs, you can extend the DB_* environment variables with any config
|
||||
# you need to pass to the database instance.
|
||||
|
||||
DB_CLIENT="sqlite3"
|
||||
DB_FILENAME="/workspaces/directus-extension-external-jwt/data.db"
|
||||
|
||||
|
||||
####################################################################################################
|
||||
### File Storage
|
||||
|
||||
# A CSV of storage locations (eg: local,digitalocean,amazon) to use. You can use any names you'd like for these keys ["local"]
|
||||
STORAGE_LOCATIONS="local"
|
||||
STORAGE_LOCAL_DRIVER="local"
|
||||
STORAGE_LOCAL_ROOT="./uploads"
|
||||
|
||||
|
||||
####################################################################################################
|
||||
### Security
|
||||
|
||||
KEY="1d5a73c0-fd56-4d8b-ac52-11abade5981e"
|
||||
SECRET="fa09-kK5PzcRN7R0UiXRiWChdGySjCuJ"
|
||||
|
||||
####################################################################################################
|
||||
### Extensions
|
||||
|
||||
# Path to your local extensions folder ["./extensions"]
|
||||
EXTENSIONS_PATH="./extensions"
|
||||
|
||||
# Automatically reload extensions when they have changed [false]
|
||||
EXTENSIONS_AUTO_RELOAD=true
|
||||
31
.eslintrc.cjs
Normal file
31
.eslintrc.cjs
Normal file
@@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
"env": {
|
||||
"browser": false,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"files": [
|
||||
".eslintrc.{js,cjs}"
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "script"
|
||||
}
|
||||
}
|
||||
],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"rules": {
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
}
|
||||
0
.github/CODEOWNERS
vendored
Normal file
0
.github/CODEOWNERS
vendored
Normal file
13
.github/FUNDING.yaml
vendored
Normal file
13
.github/FUNDING.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: #zerosubnet
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
otechie: # Replace with a single Otechie username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
custom: # []
|
||||
23
.github/workflows/main.yml
vendored
Normal file
23
.github/workflows/main.yml
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: Merge to Main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: [ 18 ]
|
||||
|
||||
name: Node ${{ matrix.node }} sample
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run linting rules and tests
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
- run: npm run test
|
||||
24
.github/workflows/pr.yml
vendored
Normal file
24
.github/workflows/pr.yml
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
name: Pull Request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: [ 16, 18, 20 ]
|
||||
|
||||
name: Node ${{ matrix.node }} sample
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run linting rules and tests
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
- run: npm run test
|
||||
37
.github/workflows/release.yml.disabled
vendored
Normal file
37
.github/workflows/release.yml.disabled
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read # for checkout
|
||||
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write # to be able to publish a GitHub release
|
||||
issues: write # to be able to comment on released issues
|
||||
pull-requests: write # to be able to comment on released pull requests
|
||||
id-token: write # to enable use of OIDC for npm provenance
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install dependencies
|
||||
run: npm clean-install
|
||||
- name: Verify the integrity of provenance attestations and registry signatures for installed dependencies
|
||||
run: npm audit signatures
|
||||
- name: Release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
run: npx semantic-release
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
.pnpm-store
|
||||
dist
|
||||
|
||||
# directus files
|
||||
|
||||
10
Dockerfile
Normal file
10
Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
FROM busybox
|
||||
|
||||
|
||||
RUN mkdir -p /app/hooks/directus-extension-external-jwt/
|
||||
WORKDIR /app
|
||||
|
||||
ADD dist/index.js ./hooks/directus-extension-external-jwt/
|
||||
ADD docker/install.sh /install.sh
|
||||
|
||||
ENTRYPOINT [ "/install.sh" ]
|
||||
4
docker/install.sh
Executable file
4
docker/install.sh
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
# check if directory /extensions is mounted and copy extension into that dir
|
||||
[ -d /extensions ] && (echo "Found extension directory on /extensions, copying ext to that directory" && cp -r /app/* /extensions/) || echo "No extension directory found on /extensions, skipping copy"
|
||||
5050
package-lock.json
generated
5050
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "directus-extension-external-jwt",
|
||||
"description": "Please enter a description for your extension",
|
||||
"description": "External JWT Directus Extension allow directus to trust tokens issued by an oauth2 or OIDC provider",
|
||||
"icon": "extension",
|
||||
"version": "1.0.0",
|
||||
"keywords": [
|
||||
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@directus/errors": "^0.0.2",
|
||||
"@directus/extensions-sdk": "10.1.7",
|
||||
"@directus/extensions-sdk": "^10.1.0",
|
||||
"@directus/tsconfig": "^1.0.0",
|
||||
"@directus/types": "^10.1.3",
|
||||
"@directus/utils": "^10.0.8",
|
||||
@@ -35,8 +35,14 @@
|
||||
"@types/jsonwebtoken": "^9.0.2",
|
||||
"@types/lodash-es": "^4.17.8",
|
||||
"@types/node": "^20.4.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.52.0",
|
||||
"config": "^3.3.9",
|
||||
"dotenv": "^16.3.1",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-standard-with-typescript": "^37.0.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
|
||||
"eslint-plugin-promise": "^6.0.0",
|
||||
"fs-extra": "^11.1.1",
|
||||
"js-yaml": "^4.1.0",
|
||||
"knex": "^2.5.1",
|
||||
@@ -47,6 +53,6 @@
|
||||
"jsonwebtoken": "^9.0.1",
|
||||
"jwks-rsa": "^3.0.1",
|
||||
"openid-client": "^5.4.3",
|
||||
"remove": "^0.1.5"
|
||||
"sqlite3": "^5.1.6"
|
||||
}
|
||||
}
|
||||
|
||||
2044
pnpm-lock.yaml
generated
2044
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@ import { createError } from '@directus/errors';
|
||||
const InvalidJWKIssuerMetadata = createError('INVALID_JWKS_ISSUER_ERROR', 'No JWKS_URL or JWKS_KEYS and could not discover JWKS_URL from openid metadata', 500);
|
||||
const InvalidJWKSUrl = createError('INVALID_JWKS_ISSUER_ERROR', 'Could not retrieve any valid keys from JWKS_URL', 500);
|
||||
|
||||
|
||||
export interface AuthProvider {
|
||||
label: string;
|
||||
name: string;
|
||||
@@ -32,7 +33,7 @@ export interface AuthProvider {
|
||||
|
||||
export async function getAuthProviders(): Promise<AuthProvider[]> {
|
||||
console.log("calling auth providers")
|
||||
return new Promise(async (resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const authProviders: AuthProvider[] = toArray(env['AUTH_PROVIDERS'])
|
||||
.filter((provider) => provider && env[`AUTH_${provider.toUpperCase()}_DRIVER`] === ('openid' || 'oauth2'))
|
||||
.map((provider) => ({
|
||||
@@ -57,7 +58,7 @@ export async function getAuthProviders(): Promise<AuthProvider[]> {
|
||||
|
||||
|
||||
|
||||
var promises = [];
|
||||
const promises = [];
|
||||
|
||||
for (const authProvider of authProviders) {
|
||||
switch (authProvider.driver) {
|
||||
@@ -84,65 +85,56 @@ export async function getAuthProviders(): Promise<AuthProvider[]> {
|
||||
});
|
||||
}
|
||||
|
||||
function getJWKS(provider: AuthProvider): Promise<AuthProvider>{
|
||||
return new Promise(async (resolve, reject) => {
|
||||
if(provider.jwks_keys != null && provider.issuer_url == null && provider.jwks_url == null) {
|
||||
|
||||
const jwksClient = new JwksClient({
|
||||
getKeysInterceptor: () => {
|
||||
return JSON.parse(provider.jwks_keys);
|
||||
},
|
||||
jwksUri: ''
|
||||
})
|
||||
|
||||
|
||||
provider.JWKSClient = jwksClient;
|
||||
|
||||
resolve(provider);
|
||||
return;
|
||||
}
|
||||
|
||||
if(provider.issuer_url && !provider.jwks_url) {
|
||||
//try to discover with openid
|
||||
try {
|
||||
const issuer = await Issuer.discover(provider.issuer_url);
|
||||
if(issuer.metadata.jwks_uri != null) {
|
||||
provider.jwks_url = issuer.metadata.jwks_uri;
|
||||
}
|
||||
} catch (error) {
|
||||
//throw new InvalidJWKIssuerMetadata();
|
||||
reject("Could not discover JWKS_URL from openid metadata")
|
||||
}
|
||||
}
|
||||
|
||||
if(!provider.jwks_url) {
|
||||
reject("No JWKS_URL or JWKS_KEYS and could not discover JWKS_URL from openid metadata")
|
||||
return;
|
||||
}
|
||||
|
||||
async function getJWKS(provider: AuthProvider) {
|
||||
if(provider.jwks_keys !== undefined && provider.issuer_url == null && provider.jwks_url == null) {
|
||||
const jwks_keys = JSON.parse(provider.jwks_keys);
|
||||
const jwksClient = new JwksClient({
|
||||
jwksUri: provider.jwks_url,
|
||||
cache: true,
|
||||
cacheMaxAge: 36000000, // 10 hours
|
||||
cacheMaxEntries: 10,
|
||||
timeout: 30000, // 30 seconds
|
||||
});
|
||||
|
||||
// try to get the keys
|
||||
try {
|
||||
const keys = await jwksClient.getSigningKeys()
|
||||
if (keys.length == 0) {
|
||||
reject("Could not retrieve any valid keys from JWKS_URL")
|
||||
}
|
||||
} catch (error) {
|
||||
throw new InvalidJWKSUrl();
|
||||
}
|
||||
getKeysInterceptor: () => {
|
||||
return jwks_keys;
|
||||
},
|
||||
jwksUri: ''
|
||||
})
|
||||
|
||||
|
||||
provider.JWKSClient = jwksClient;
|
||||
|
||||
resolve(provider);
|
||||
}
|
||||
|
||||
})
|
||||
if(provider.issuer_url && !provider.jwks_url) {
|
||||
//try to discover with openid
|
||||
const issuer = await Issuer.discover(provider.issuer_url);
|
||||
if(issuer.metadata.jwks_uri != null) {
|
||||
provider.jwks_url = issuer.metadata.jwks_uri;
|
||||
}
|
||||
}
|
||||
|
||||
if (provider.jwks_url == null) throw new InvalidJWKIssuerMetadata();
|
||||
|
||||
const jwksClient = await getJWKSClient(provider.jwks_url);
|
||||
|
||||
provider.JWKSClient = jwksClient;
|
||||
|
||||
return provider;
|
||||
}
|
||||
|
||||
async function getJWKSClient(url: string) {
|
||||
const jwksClient = new JwksClient({
|
||||
jwksUri: url,
|
||||
cache: true,
|
||||
cacheMaxAge: 36000000, // 10 hours
|
||||
cacheMaxEntries: 10,
|
||||
timeout: 30000, // 30 seconds
|
||||
});
|
||||
|
||||
// try to get the keys
|
||||
try {
|
||||
const keys = await jwksClient.getSigningKeys()
|
||||
if (keys.length == 0) {
|
||||
throw new InvalidJWKSUrl();
|
||||
}
|
||||
} catch (error) {
|
||||
throw new InvalidJWKSUrl();
|
||||
}
|
||||
|
||||
return jwksClient;
|
||||
}
|
||||
@@ -10,6 +10,28 @@ import { requireYAML } from '../require-yaml.js';
|
||||
const allowedEnvironmentVars = [
|
||||
// general
|
||||
'CONFIG_PATH',
|
||||
// cache
|
||||
'CACHE_ENABLED',
|
||||
'CACHE_TTL',
|
||||
'CACHE_CONTROL_S_MAXAGE',
|
||||
'CACHE_AUTO_PURGE',
|
||||
'CACHE_AUTO_PURGE_IGNORE_LIST',
|
||||
'CACHE_SYSTEM_TTL',
|
||||
'CACHE_SCHEMA',
|
||||
'CACHE_PERMISSIONS',
|
||||
'CACHE_NAMESPACE',
|
||||
'CACHE_STORE',
|
||||
'CACHE_STATUS_HEADER',
|
||||
'CACHE_VALUE_MAX_SIZE',
|
||||
'CACHE_SKIP_ALLOWED',
|
||||
'CACHE_HEALTHCHECK_THRESHOLD',
|
||||
// redis
|
||||
'REDIS',
|
||||
'REDIS_HOST',
|
||||
'REDIS_PORT',
|
||||
'REDIS_USERNAME',
|
||||
'REDIS_PASSWORD',
|
||||
'REDIS_JWT_DB',
|
||||
// auth
|
||||
'AUTH_PROVIDERS',
|
||||
'AUTH_.+_DRIVER',
|
||||
@@ -29,19 +51,7 @@ const allowedEnvironmentVars = [
|
||||
'AUTH_.+_LABEL',
|
||||
'AUTH_.+_PARAMS',
|
||||
'AUTH_.+_ISSUER_URL',
|
||||
'AUTH_.+_AUTH_REQUIRE_VERIFIED_EMAIL',
|
||||
'AUTH_.+_CLIENT_URL',
|
||||
'AUTH_.+_BIND_DN',
|
||||
'AUTH_.+_BIND_PASSWORD',
|
||||
'AUTH_.+_USER_DN',
|
||||
'AUTH_.+_USER_ATTRIBUTE',
|
||||
'AUTH_.+_USER_SCOPE',
|
||||
'AUTH_.+_MAIL_ATTRIBUTE',
|
||||
'AUTH_.+_FIRST_NAME_ATTRIBUTE',
|
||||
'AUTH_.+_LAST_NAME_ATTRIBUTE',
|
||||
'AUTH_.+_GROUP_DN',
|
||||
'AUTH_.+_GROUP_ATTRIBUTE',
|
||||
'AUTH_.+_GROUP_SCOPE',
|
||||
'AUTH_.+_TRUSTED',
|
||||
'AUTH_.+_JWKS_URL',
|
||||
'AUTH_.+_JWKS_KEYS',
|
||||
@@ -49,8 +59,6 @@ const allowedEnvironmentVars = [
|
||||
'AUTH_.+_JWT_ADMIN_KEY',
|
||||
'AUTH_.+_JWT_APP_KEY',
|
||||
'AUTH_.+_JWT_USEDB',
|
||||
'AUTH_.+_IDP.+',
|
||||
'AUTH_.+_SP.+',
|
||||
].map((name) => new RegExp(`^${name}$`));
|
||||
|
||||
const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
|
||||
@@ -97,7 +105,7 @@ export function refreshEnv(): void {
|
||||
|
||||
|
||||
|
||||
function toBoolean(value: any): boolean {
|
||||
function toBoolean(value: string | boolean | number): boolean {
|
||||
return value === 'true' || value === true || value === '1' || value === 1;
|
||||
}
|
||||
|
||||
@@ -110,6 +118,7 @@ function processConfiguration() {
|
||||
const fileExt = path.extname(configPath).toLowerCase();
|
||||
|
||||
if (fileExt === '.js') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const module = require(configPath);
|
||||
const exported = module.default || module;
|
||||
|
||||
@@ -161,8 +170,8 @@ function getEnvironmentValueWithPrefix(envArray: Array<string>): Array<string |
|
||||
}
|
||||
|
||||
function getEnvironmentValueByType(envVariableString: string) {
|
||||
const variableType = getVariableType(envVariableString)!;
|
||||
const envVariableValue = getEnvVariableValue(envVariableString, variableType)!;
|
||||
const variableType = getVariableType(envVariableString) ?? false;
|
||||
const envVariableValue = getEnvVariableValue(envVariableString, variableType) ?? false;
|
||||
|
||||
switch (variableType) {
|
||||
case 'number':
|
||||
@@ -285,7 +294,7 @@ function processValues(env: Record<string, any>) {
|
||||
return env;
|
||||
}
|
||||
|
||||
function tryJSON(value: any) {
|
||||
function tryJSON(value: string) {
|
||||
try {
|
||||
return parseJSON(value);
|
||||
} catch {
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import type { Accountability } from '@directus/types';
|
||||
import type { JwtHeader, VerifyCallback} from 'jsonwebtoken';
|
||||
import {JsonWebTokenError} from 'jsonwebtoken';
|
||||
import { getAuthProviders } from './authProvider/get-auth-providers.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import type { Knex } from 'knex';
|
||||
import { createError } from '@directus/errors';
|
||||
import { verify_token } from './verify-token.js';
|
||||
import { forEach } from 'lodash-es';
|
||||
|
||||
|
||||
|
||||
const authProviders = await getAuthProviders();
|
||||
|
||||
/*
|
||||
const MissingJWTHeaderError = createError('INVALID_JWKS_ISSUER_ERROR', 'No header in JWT Token', 500);
|
||||
const NoValidKeysError = createError('INVALID_JWKS_ISSUER_ERROR', 'could not retrieve any valid keys with key id(kid)', 500);
|
||||
const NoAuthProvidersError = createError('INVALID_JWKS_ISSUER_ERROR', 'No auth providers in the list', 500);
|
||||
|
||||
*/
|
||||
|
||||
// TODO: optimize this function, reduce the amount of loops
|
||||
|
||||
@@ -38,7 +35,7 @@ export async function getAccountabilityForToken(
|
||||
return accountability
|
||||
}
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const providers = authProviders.filter((provider) => provider && iss.includes(provider.client_id));
|
||||
if(providers.length === 0) return accountability;
|
||||
if(providers.length > 1) {
|
||||
@@ -48,7 +45,7 @@ export async function getAccountabilityForToken(
|
||||
|
||||
const provider = providers[0];
|
||||
|
||||
let promises = [];
|
||||
|
||||
|
||||
verify_token(provider, token).then(async (result) => {
|
||||
if(accountability) {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { JwksClient } from "jwks-rsa";
|
||||
import type { AuthProvider } from "./authProvider/get-auth-providers.js";
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
|
||||
13
src/index.ts
13
src/index.ts
@@ -1,21 +1,14 @@
|
||||
import { defineHook } from '@directus/extensions-sdk';
|
||||
import { createError } from '@directus/errors';
|
||||
|
||||
import { getAccountabilityForToken } from './external-jwt/get-accountability-for-token';
|
||||
import type { Request } from 'express';
|
||||
import type { Accountability } from '@directus/types';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
|
||||
const InvalidTokenError = createError('INVALID_TOKEN_ERROR', 'Could not validate external JWT token', 500);
|
||||
|
||||
|
||||
export default defineHook(({ filter }) => {
|
||||
|
||||
// get all configuration
|
||||
|
||||
filter('authenticate', (defaultAccountability, event, context) => {
|
||||
let req = <Request>event['req'];
|
||||
const req = <Request>event['req'];
|
||||
if(!req.token) return defaultAccountability;
|
||||
|
||||
if(!context.database) {
|
||||
@@ -32,9 +25,9 @@ export default defineHook(({ filter }) => {
|
||||
return getAccountabilityForToken(req.token, decodedToken?.iss, context.accountability, context.database)
|
||||
});
|
||||
|
||||
filter('auth.jwt', (status, user, provider) => {
|
||||
/*filter('auth.jwt', (status, user, provider) => {
|
||||
|
||||
})
|
||||
})*/
|
||||
|
||||
});
|
||||
|
||||
|
||||
45528
test/hooks/directus-extension-external-jwt/index.js
Normal file
45528
test/hooks/directus-extension-external-jwt/index.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user