This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -5,7 +5,7 @@ dist
|
|||||||
|
|
||||||
# directus files
|
# directus files
|
||||||
*.db
|
*.db
|
||||||
#extensions/*/**
|
extensions/*/**
|
||||||
uploads/
|
uploads/
|
||||||
.env
|
.env
|
||||||
|
|
||||||
@@ -19,4 +19,4 @@ uploads/
|
|||||||
coverage/
|
coverage/
|
||||||
|
|
||||||
# test secrets
|
# test secrets
|
||||||
redispass
|
redispass
|
||||||
|
|||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
4
.idea/vcs.xml
generated
Normal file
4
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings" defaultProject="true" />
|
||||||
|
</project>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"host": "^10.1.7"
|
"host": "^10.1.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=22.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "directus-extension build && npm run sync",
|
"build": "directus-extension build && npm run sync",
|
||||||
@@ -71,7 +71,8 @@
|
|||||||
"directus": "pnpm dlx directus start",
|
"directus": "pnpm dlx directus start",
|
||||||
"lint": "eslint . --ext .ts",
|
"lint": "eslint . --ext .ts",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:coverage": "vitest run --coverage"
|
"test:coverage": "vitest run --coverage",
|
||||||
|
"ncu": "npx npm-check-updates --target minor --upgrade --packageFile package.json "
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"extension": [
|
"extension": [
|
||||||
@@ -95,56 +96,56 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@directus/errors": "^0.3.2",
|
"@directus/errors": "^2.0.1",
|
||||||
"@directus/extensions-sdk": "^13.0.1",
|
"@directus/extensions-sdk": "^13.1.1",
|
||||||
"@directus/tsconfig": "^1.0.1",
|
"@directus/tsconfig": "^3.0.0",
|
||||||
"@directus/types": "^11.1.2",
|
"@directus/types": "^13.1.2",
|
||||||
"@directus/utils": "^11.0.9",
|
"@directus/utils": "^13.0.6",
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/commit-analyzer": "^10.0.4",
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||||
"@semantic-release/exec": "^6.0.3",
|
"@semantic-release/exec": "^7.1.0",
|
||||||
"@semantic-release/github": "^9.2.6",
|
"@semantic-release/github": "^11.0.3",
|
||||||
"@semantic-release/npm": "^10.0.6",
|
"@semantic-release/npm": "^12.0.1",
|
||||||
"@types/chai": "^4.3.16",
|
"@types/chai": "^5.2.2",
|
||||||
"@types/chai-as-promised": "^7.1.8",
|
"@types/chai-as-promised": "^8.0.2",
|
||||||
"@types/config": "^3.3.4",
|
"@types/config": "^3.3.5",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^5.0.2",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.9",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.10",
|
||||||
"@types/node": "^20.14.2",
|
"@types/node": "^22.15.29",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
||||||
"@vitest/coverage-istanbul": "^0.34.6",
|
"@vitest/coverage-istanbul": "^3.1.4",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.9.0",
|
||||||
"config": "^3.3.11",
|
"config": "^4.0.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.5.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-config-standard-with-typescript": "^37.0.0",
|
"eslint-config-standard-with-typescript": "^43.0.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-n": "^15.7.0",
|
"eslint-plugin-n": "^17.18.0",
|
||||||
"eslint-plugin-no-loops": "^0.3.0",
|
"eslint-plugin-no-loops": "^0.4.0",
|
||||||
"eslint-plugin-promise": "^6.2.0",
|
"eslint-plugin-promise": "^7.2.1",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^17.1.0",
|
||||||
"semantic-release": "^21.1.2",
|
"semantic-release": "^24.2.5",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"ts-mocha": "^10.0.0",
|
"ts-mocha": "^11.1.0",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.8.3",
|
||||||
"vitest": "^0.34.6"
|
"vitest": "^3.1.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@directus/extensions": "^3.0.5",
|
"@directus/extensions": "^3.0.6",
|
||||||
"@keyv/redis": "^2.8.5",
|
"@keyv/redis": "^4.4.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jwks-rsa": "^3.1.0",
|
"jwks-rsa": "^3.2.0",
|
||||||
"keyv": "^4.5.4",
|
"keyv": "^5.3.3",
|
||||||
"openid-client": "^5.6.5",
|
"openid-client": "^6.5.0",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
@@ -169,4 +170,4 @@
|
|||||||
"vite@<=4.5.5": ">=4.5.6"
|
"vite@<=4.5.5": ">=4.5.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
83
package.json
83
package.json
@@ -61,7 +61,7 @@
|
|||||||
"host": "^10.1.7"
|
"host": "^10.1.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=22.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "directus-extension build && npm run sync",
|
"build": "directus-extension build && npm run sync",
|
||||||
@@ -71,7 +71,8 @@
|
|||||||
"directus": "pnpm dlx directus start",
|
"directus": "pnpm dlx directus start",
|
||||||
"lint": "eslint . --ext .ts",
|
"lint": "eslint . --ext .ts",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:coverage": "vitest run --coverage"
|
"test:coverage": "vitest run --coverage",
|
||||||
|
"ncu": "npx npm-check-updates --target minor --upgrade --packageFile package.json "
|
||||||
},
|
},
|
||||||
"nyc": {
|
"nyc": {
|
||||||
"extension": [
|
"extension": [
|
||||||
@@ -95,56 +96,56 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@directus/errors": "^0.3.2",
|
"@directus/errors": "^2.0.1",
|
||||||
"@directus/extensions-sdk": "^13.0.1",
|
"@directus/extensions-sdk": "^13.1.1",
|
||||||
"@directus/tsconfig": "^1.0.1",
|
"@directus/tsconfig": "^3.0.0",
|
||||||
"@directus/types": "^11.1.2",
|
"@directus/types": "^13.1.2",
|
||||||
"@directus/utils": "^11.0.9",
|
"@directus/utils": "^13.0.6",
|
||||||
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
"@semantic-release/changelog": "^6.0.3",
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
"@semantic-release/commit-analyzer": "^10.0.4",
|
"@semantic-release/commit-analyzer": "^13.0.1",
|
||||||
"@semantic-release/exec": "^6.0.3",
|
"@semantic-release/exec": "^7.1.0",
|
||||||
"@semantic-release/github": "^9.2.6",
|
"@semantic-release/github": "^11.0.3",
|
||||||
"@semantic-release/npm": "^10.0.6",
|
"@semantic-release/npm": "^12.0.1",
|
||||||
"@types/chai": "^4.3.16",
|
"@types/chai": "^5.2.2",
|
||||||
"@types/chai-as-promised": "^7.1.8",
|
"@types/chai-as-promised": "^8.0.2",
|
||||||
"@types/config": "^3.3.4",
|
"@types/config": "^3.3.5",
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^5.0.2",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/fs-extra": "^11.0.4",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/jsonwebtoken": "^9.0.6",
|
"@types/jsonwebtoken": "^9.0.9",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/mocha": "^10.0.6",
|
"@types/mocha": "^10.0.10",
|
||||||
"@types/node": "^20.14.2",
|
"@types/node": "^22.15.29",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
"@typescript-eslint/eslint-plugin": "^8.33.0",
|
||||||
"@vitest/coverage-istanbul": "^0.34.6",
|
"@vitest/coverage-istanbul": "^3.1.4",
|
||||||
"axios": "^1.7.2",
|
"axios": "^1.9.0",
|
||||||
"config": "^3.3.11",
|
"config": "^4.0.0",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.5.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^9.28.0",
|
||||||
"eslint-config-standard-with-typescript": "^37.0.0",
|
"eslint-config-standard-with-typescript": "^43.0.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.31.0",
|
||||||
"eslint-plugin-n": "^15.7.0",
|
"eslint-plugin-n": "^17.18.0",
|
||||||
"eslint-plugin-no-loops": "^0.3.0",
|
"eslint-plugin-no-loops": "^0.4.0",
|
||||||
"eslint-plugin-promise": "^6.2.0",
|
"eslint-plugin-promise": "^7.2.1",
|
||||||
"fs-extra": "^11.2.0",
|
"fs-extra": "^11.3.0",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"nyc": "^15.1.0",
|
"nyc": "^17.1.0",
|
||||||
"semantic-release": "^21.1.2",
|
"semantic-release": "^24.2.5",
|
||||||
"sqlite3": "^5.1.7",
|
"sqlite3": "^5.1.7",
|
||||||
"ts-mocha": "^10.0.0",
|
"ts-mocha": "^11.1.0",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.8.3",
|
||||||
"vitest": "^0.34.6"
|
"vitest": "^3.1.4"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@directus/extensions": "^3.0.5",
|
"@directus/extensions": "^3.0.6",
|
||||||
"@keyv/redis": "^2.8.5",
|
"@keyv/redis": "^4.4.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jwks-rsa": "^3.1.0",
|
"jwks-rsa": "^3.2.0",
|
||||||
"keyv": "^4.5.4",
|
"keyv": "^5.3.3",
|
||||||
"openid-client": "^5.6.5",
|
"openid-client": "^6.5.0",
|
||||||
"uuid": "^11.1.0"
|
"uuid": "^11.1.0"
|
||||||
},
|
},
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
@@ -169,4 +170,4 @@
|
|||||||
"vite@<=4.5.5": ">=4.5.6"
|
"vite@<=4.5.5": ">=4.5.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10197
pnpm-lock.yaml
generated
10197
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,14 @@
|
|||||||
import { toArray } from '@directus/utils';
|
import { toArray } from "@directus/utils";
|
||||||
import {JwksClient} from 'jwks-rsa';
|
import { JwksClient } from "jwks-rsa";
|
||||||
|
|
||||||
import { Issuer } from 'openid-client';
|
import { discovery } from "openid-client";
|
||||||
|
|
||||||
import env from '../config/config';
|
import env from "../config/config";
|
||||||
import { createError } from '@directus/errors';
|
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 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);
|
const InvalidJWKSUrl = createError("INVALID_JWKS_ISSUER_ERROR", "Could not retrieve any valid keys from JWKS_URL", 500);
|
||||||
const InvalidJWKKeys = createError('INVALID_JWKS_ISSUER_ERROR', 'No signing keys in response from provider')
|
const InvalidJWKKeys = createError("INVALID_JWKS_ISSUER_ERROR", "No signing keys in response from provider");
|
||||||
|
|
||||||
|
|
||||||
export interface AuthProvider {
|
export interface AuthProvider {
|
||||||
@@ -33,16 +33,14 @@ export interface AuthProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export async function getAuthProviders(): Promise<AuthProvider[]> {
|
export async function getAuthProviders(): Promise<AuthProvider[]> {
|
||||||
console.log("calling auth providers _")
|
console.log("calling auth providers _");
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const authProviders: AuthProvider[] = toArray(env["AUTH_PROVIDERS"])
|
const authProviders: AuthProvider[] = toArray(env["AUTH_PROVIDERS"])
|
||||||
.filter(
|
.filter(
|
||||||
(provider) =>
|
(provider) =>
|
||||||
provider &&
|
provider &&
|
||||||
env[`AUTH_${provider.toUpperCase()}_DRIVER`] ===
|
["openid", "oauth2"].includes(env[`AUTH_${provider.toUpperCase()}_DRIVER`])
|
||||||
("openid" || "oauth2")
|
|
||||||
)
|
)
|
||||||
.map((provider) => ({
|
.map((provider) => ({
|
||||||
name: provider,
|
name: provider,
|
||||||
@@ -60,92 +58,92 @@ export async function getAuthProviders(): Promise<AuthProvider[]> {
|
|||||||
client_secret: env[`AUTH_${provider.toUpperCase()}_CLIENT_SECRET`],
|
client_secret: env[`AUTH_${provider.toUpperCase()}_CLIENT_SECRET`],
|
||||||
use_database: env[`AUTH_${provider.toUpperCase()}_JWT_USEDB`],
|
use_database: env[`AUTH_${provider.toUpperCase()}_JWT_USEDB`],
|
||||||
|
|
||||||
default_role_id: env[`AUTH_${provider.toUpperCase()}_DEFAULT_ROLE_ID`],
|
default_role_id: env[`AUTH_${provider.toUpperCase()}_DEFAULT_ROLE_ID`]
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
if(authProviders.length === 0) return resolve([]);
|
|
||||||
|
|
||||||
|
if (authProviders.length === 0) return resolve([]);
|
||||||
|
|
||||||
const promises = [];
|
|
||||||
|
|
||||||
for (const authProvider of authProviders) {
|
const promises = [];
|
||||||
switch (authProvider.driver) {
|
|
||||||
case 'openid':
|
|
||||||
|
|
||||||
if (!authProvider.trusted || (authProvider.issuer_url == null && authProvider.jwks_url == null && authProvider.jwks_keys == null)) break;
|
|
||||||
//promises.push(getJWKS(authProvider.issuer_url, authProvider.jwks_url, authProvider.jwks_keys));
|
|
||||||
promises.push(getJWKS(authProvider));
|
|
||||||
break;
|
|
||||||
case 'oauth2':
|
|
||||||
if (!authProvider.trusted || (authProvider.issuer_url == null && authProvider.jwks_url == null && authProvider.jwks_keys == null)) break;
|
|
||||||
//promises.push(getJWKS(authProvider.issuer_url, authProvider.jwks_url, authProvider.jwks_keys));
|
|
||||||
promises.push(getJWKS(authProvider));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Promise.all(promises).then((values) => {
|
for (const authProvider of authProviders) {
|
||||||
console.log("resolved auth providers", values)
|
switch (authProvider.driver) {
|
||||||
resolve(values);
|
case "openid":
|
||||||
}).catch((error) => {
|
|
||||||
reject(error);
|
|
||||||
})
|
|
||||||
|
|
||||||
});
|
if (!authProvider.trusted || (authProvider.issuer_url == null && authProvider.jwks_url == null && authProvider.jwks_keys == null)) break;
|
||||||
|
//promises.push(getJWKS(authProvider.issuer_url, authProvider.jwks_url, authProvider.jwks_keys));
|
||||||
|
promises.push(getJWKS(authProvider));
|
||||||
|
break;
|
||||||
|
case "oauth2":
|
||||||
|
if (!authProvider.trusted || (authProvider.issuer_url == null && authProvider.jwks_url == null && authProvider.jwks_keys == null)) break;
|
||||||
|
//promises.push(getJWKS(authProvider.issuer_url, authProvider.jwks_url, authProvider.jwks_keys));
|
||||||
|
promises.push(getJWKS(authProvider));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Promise.all(promises).then((values) => {
|
||||||
|
console.log("resolved auth providers", values);
|
||||||
|
resolve(values);
|
||||||
|
}).catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getJWKS(provider: AuthProvider) {
|
async function getJWKS(provider: AuthProvider) {
|
||||||
if(provider.jwks_keys !== undefined && provider.issuer_url == null && provider.jwks_url == null) {
|
if (provider.jwks_keys !== undefined && provider.issuer_url == null && provider.jwks_url == null) {
|
||||||
const jwks_keys = JSON.parse(provider.jwks_keys);
|
const jwks_keys = JSON.parse(provider.jwks_keys);
|
||||||
const jwksClient = new JwksClient({
|
const jwksClient = new JwksClient({
|
||||||
getKeysInterceptor: () => {
|
getKeysInterceptor: () => {
|
||||||
return jwks_keys;
|
return jwks_keys;
|
||||||
},
|
},
|
||||||
jwksUri: ''
|
jwksUri: ""
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
provider.JWKSClient = jwksClient;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(provider.issuer_url && !provider.jwks_url) {
|
provider.JWKSClient = jwksClient;
|
||||||
//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);
|
if (provider.issuer_url && !provider.jwks_url) {
|
||||||
|
//try to discover with openid
|
||||||
|
const issuer = await discovery(new URL(provider.issuer_url), provider.client_id);
|
||||||
|
if (issuer.serverMetadata().jwks_uri != null) {
|
||||||
|
provider.jwks_url = issuer.serverMetadata().jwks_uri;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
provider.JWKSClient = jwksClient;
|
if (provider.jwks_url == null) throw new InvalidJWKIssuerMetadata();
|
||||||
|
|
||||||
return provider;
|
const jwksClient = await getJWKSClient(provider.jwks_url);
|
||||||
|
|
||||||
|
provider.JWKSClient = jwksClient;
|
||||||
|
|
||||||
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getJWKSClient(url: string) {
|
async function getJWKSClient(url: string) {
|
||||||
const jwksClient = new JwksClient({
|
const jwksClient = new JwksClient({
|
||||||
jwksUri: url,
|
jwksUri: url,
|
||||||
cache: true,
|
cache: true,
|
||||||
cacheMaxAge: 36000000, // 10 hours
|
cacheMaxAge: 36000000, // 10 hours
|
||||||
cacheMaxEntries: 10,
|
cacheMaxEntries: 10,
|
||||||
timeout: 30000, // 30 seconds
|
timeout: 30000 // 30 seconds
|
||||||
});
|
});
|
||||||
|
|
||||||
// try to get the keys
|
// try to get the keys
|
||||||
try {
|
try {
|
||||||
const keys = await jwksClient.getSigningKeys()
|
const keys = await jwksClient.getSigningKeys();
|
||||||
if (keys.length == 0) {
|
if (keys.length == 0) {
|
||||||
throw new InvalidJWKKeys();
|
throw new InvalidJWKKeys();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
throw new InvalidJWKSUrl();
|
} catch (error) {
|
||||||
}
|
throw new InvalidJWKSUrl();
|
||||||
|
}
|
||||||
|
|
||||||
return jwksClient;
|
return jwksClient;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,75 +1,64 @@
|
|||||||
import {default as Keyv, Store} from 'keyv';
|
import { default as Keyv } from "keyv";
|
||||||
import env from './config/config';
|
import env from "./config/config";
|
||||||
import {default as KeyvRedis} from '@keyv/redis';
|
import { default as KeyvRedis } from "@keyv/redis";
|
||||||
|
|
||||||
// check if redis is defined
|
|
||||||
|
|
||||||
const cache: Keyv | null = getCache();
|
const cache: Keyv | null = getCache();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function getCache(): Keyv | null {
|
function getCache(): Keyv | null {
|
||||||
if(env['CACHE_ENABLED'] !== true) return null;
|
if (env["CACHE_ENABLED"] !== true) return null;
|
||||||
|
|
||||||
// check namespace
|
let namespace = env["CACHE_JWT_NAMESPACE"];
|
||||||
let namespace = env['CACHE_JWT_NAMESPACE'];
|
if (namespace == null || namespace === "") {
|
||||||
if(namespace == null || namespace === '') {
|
namespace = "exjwt";
|
||||||
namespace = 'exjwt';
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let ttl = env['CACHE_JWT_TTL'];
|
let ttl = env["CACHE_JWT_TTL"];
|
||||||
if (ttl == null || ttl === '') {
|
if (ttl == null || ttl === "") {
|
||||||
ttl = 5000
|
ttl = 5000;
|
||||||
}
|
}
|
||||||
|
|
||||||
let uri = '';
|
let uri = "";
|
||||||
let store: Store<string | undefined> | undefined = undefined;
|
let store: KeyvRedis<string | undefined> | undefined = undefined;
|
||||||
if(env['CACHE_STORE'] === 'redis') {
|
if (env["CACHE_STORE"] === "redis") {
|
||||||
uri = env['REDIS']
|
uri = env["REDIS"];
|
||||||
|
|
||||||
if(uri == null || uri === '') {
|
|
||||||
uri = `redis://${env['REDIS_USERNAME'] || '' }:${env['REDIS_PASSWORD'] || ''}@${env['REDIS_HOST']}:${env['REDIS_PORT'] || '6379'} /${env['REDIS_JWT_DB'] || '2'}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
if (uri == null || uri === "") {
|
||||||
store = new KeyvRedis(uri);
|
uri = `redis://${env["REDIS_USERNAME"] || ""}:${env["REDIS_PASSWORD"] || ""}@${env["REDIS_HOST"]}:${env["REDIS_PORT"] || "6379"} /${env["REDIS_JWT_DB"] || "2"}`;
|
||||||
} catch(e) {
|
|
||||||
throw new Error("CACHE: could not connect to database: " + e)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const keyv = new Keyv(uri, {
|
store = new KeyvRedis(uri);
|
||||||
namespace: namespace,
|
} catch (e) {
|
||||||
ttl,
|
throw new Error("CACHE: could not connect to database: " + e);
|
||||||
store: store
|
|
||||||
});
|
|
||||||
|
|
||||||
keyv.on('error', (err) => {
|
|
||||||
throw new Error("CACHE: could not connect: " + err)
|
|
||||||
});
|
|
||||||
|
|
||||||
return keyv
|
|
||||||
} catch(e) {
|
|
||||||
throw new Error("CACHE: could not connect to database: " + e)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keyv = new Keyv(store, {
|
||||||
|
namespace: namespace,
|
||||||
|
ttl
|
||||||
|
});
|
||||||
|
|
||||||
|
keyv.on("error", (err) => {
|
||||||
|
throw new Error("CACHE: could not connect: " + err);
|
||||||
|
});
|
||||||
|
|
||||||
|
return keyv;
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("CACHE: could not connect to database: " + e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CacheEnabled(): boolean {
|
export function CacheEnabled(): boolean {
|
||||||
return cache !== null;
|
return cache !== null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function CacheSet(key: string, value: any) {
|
export async function CacheSet(key: string, value: unknown) {
|
||||||
if(cache === null) return false;
|
if (cache === null) return false;
|
||||||
return cache.set(key, value);
|
return cache.set(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function CacheGet(key: string) {
|
export async function CacheGet(key: string) {
|
||||||
if(cache === null) return null;
|
if (cache === null) return null;
|
||||||
return cache.get(key);
|
return cache.get(key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -303,4 +303,4 @@ function tryJSON(value: string) {
|
|||||||
} catch {
|
} catch {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,16 @@ import { CacheEnabled, CacheGet, CacheSet } from "./cache.js";
|
|||||||
import type { Knex } from "knex";
|
import type { Knex } from "knex";
|
||||||
import * as uuid from "uuid";
|
import * as uuid from "uuid";
|
||||||
|
|
||||||
const authProviders = await getAuthProviders();
|
// Instead of using top-level await, declare a variable for providers
|
||||||
|
let authProviders: Awaited<ReturnType<typeof getAuthProviders>>;
|
||||||
|
|
||||||
|
// Initialize providers function to be called at runtime
|
||||||
|
const initAuthProviders = async () => {
|
||||||
|
if (!authProviders) {
|
||||||
|
authProviders = await getAuthProviders();
|
||||||
|
}
|
||||||
|
return authProviders;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
const MissingJWTHeaderError = createError('INVALID_JWKS_ISSUER_ERROR', 'No header in JWT Token', 500);
|
const MissingJWTHeaderError = createError('INVALID_JWKS_ISSUER_ERROR', 'No header in JWT Token', 500);
|
||||||
@@ -35,14 +44,14 @@ const getUser = async (
|
|||||||
)
|
)
|
||||||
.where({
|
.where({
|
||||||
"directus_users.external_identifier": externalIdentifier,
|
"directus_users.external_identifier": externalIdentifier,
|
||||||
"directus_users.provider": provider,
|
"directus_users.provider": provider
|
||||||
})
|
})
|
||||||
.first();
|
.first();
|
||||||
};
|
};
|
||||||
|
|
||||||
const insertUser = async (database: Knex, user: Record<string, any>): Promise<any> => {
|
const insertUser = async (database: Knex, user: Record<string, string | undefined>): Promise<unknown> => {
|
||||||
await database("directus_users").insert(user);
|
await database("directus_users").insert(user);
|
||||||
return getUser(database, user.external_identifier, user.provider);
|
return getUser(database, user.external_identifier, user.provider!);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: optimize this function, reduce the amount of loops
|
// TODO: optimize this function, reduce the amount of loops
|
||||||
@@ -58,6 +67,8 @@ export async function getAccountabilityForToken(
|
|||||||
role: null,
|
role: null,
|
||||||
admin: false,
|
admin: false,
|
||||||
app: false,
|
app: false,
|
||||||
|
roles: [],
|
||||||
|
ip: null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +76,7 @@ export async function getAccountabilityForToken(
|
|||||||
return accountability;
|
return accountability;
|
||||||
}
|
}
|
||||||
|
|
||||||
const providers = authProviders.filter(
|
const providers = (await initAuthProviders()).filter(
|
||||||
(provider) =>
|
(provider) =>
|
||||||
provider.issuer_url && provider.issuer_url.includes(iss.toString())
|
provider.issuer_url && provider.issuer_url.includes(iss.toString())
|
||||||
);
|
);
|
||||||
@@ -100,7 +111,7 @@ export async function getAccountabilityForToken(
|
|||||||
id: uuid.v4(),
|
id: uuid.v4(),
|
||||||
role: provider.default_role_id,
|
role: provider.default_role_id,
|
||||||
provider: provider.name,
|
provider: provider.name,
|
||||||
external_identifier: result.sub,
|
external_identifier: result.sub
|
||||||
});
|
});
|
||||||
console.debug("Inserted new user:", user);
|
console.debug("Inserted new user:", user);
|
||||||
}
|
}
|
||||||
@@ -115,7 +126,7 @@ export async function getAccountabilityForToken(
|
|||||||
accountability.app = user.app_access === true || user.app_access == 1;
|
accountability.app = user.app_access === true || user.app_access == 1;
|
||||||
|
|
||||||
if (CacheEnabled() && result.sub) {
|
if (CacheEnabled() && result.sub) {
|
||||||
CacheSet(result.sub, accountability);
|
await CacheSet(result.sub, accountability);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.debug("Accountability set from database:", accountability);
|
console.debug("Accountability set from database:", accountability);
|
||||||
@@ -150,6 +161,9 @@ export async function getAccountabilityForToken(
|
|||||||
// accountability.role = "d737d4bd-ae35-4a68-a907-e913bcdfcc53";
|
// accountability.role = "d737d4bd-ae35-4a68-a907-e913bcdfcc53";
|
||||||
// accountability.admin = true;
|
// accountability.admin = true;
|
||||||
// accountability.app = true;
|
// accountability.app = true;
|
||||||
|
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return accountability;
|
return accountability;
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/index.ts
55
src/index.ts
@@ -1,42 +1,35 @@
|
|||||||
import { defineHook } from '@directus/extensions-sdk';
|
import { defineHook } from "@directus/extensions-sdk";
|
||||||
import { getAccountabilityForToken } from './external-jwt/get-accountability-for-token';
|
import { getAccountabilityForToken } from "./external-jwt/get-accountability-for-token";
|
||||||
import type { Request } from 'express';
|
import type { Request } from "express";
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from "jsonwebtoken";
|
||||||
import type {HookConfig} from '@directus/extensions'
|
import type { Accountability, EventContext } from "@directus/types";
|
||||||
import type { Accountability, EventContext } from '@directus/types';
|
|
||||||
|
|
||||||
export default defineHook<HookConfig>(({ filter }) => {
|
export default defineHook(({ filter }) => {
|
||||||
|
|
||||||
// get all configuration
|
|
||||||
|
|
||||||
filter('authenticate', (defaultAccountability: Accountability, event, context: EventContext) => {
|
|
||||||
console.log("authenticate hook called");
|
|
||||||
const req = <Request>event['req'];
|
|
||||||
if(!req.token) return defaultAccountability;
|
|
||||||
|
|
||||||
if(!context.database) {
|
// get all configuration
|
||||||
return defaultAccountability
|
filter("authenticate", (defaultAccountability: Accountability, event, context: EventContext) => {
|
||||||
}
|
console.log("authenticate hook called");
|
||||||
|
const req = <Request>event["req"];
|
||||||
|
if (!req.token) return defaultAccountability;
|
||||||
|
|
||||||
|
if (!context.database) {
|
||||||
|
return defaultAccountability;
|
||||||
|
}
|
||||||
|
|
||||||
const decodedToken = jwt.decode(req.token);
|
const decodedToken = jwt.decode(req.token);
|
||||||
console.log("decoded token", decodedToken);
|
console.log("decoded token", decodedToken);
|
||||||
|
|
||||||
if(typeof decodedToken === 'string' || decodedToken == null) return defaultAccountability; // if token is not a jwt, let directus handle it
|
|
||||||
if(decodedToken?.iss == 'directus') return defaultAccountability; // if token issued by directus, let directus handle it
|
|
||||||
|
|
||||||
|
if (typeof decodedToken === "string" || decodedToken == null) return defaultAccountability; // if token is not a jwt, let directus handle it
|
||||||
|
if (decodedToken?.iss == "directus") return defaultAccountability; // if token issued by directus, let directus handle it
|
||||||
|
|
||||||
|
console.log("getting accountability for token", req.token, decodedToken?.iss, context.accountability, context.database);
|
||||||
|
|
||||||
|
return getAccountabilityForToken(req.token, decodedToken?.iss, context.accountability, context.database);
|
||||||
console.log("getting accountability for token", req.token, decodedToken?.iss, context.accountability, context.database);
|
});
|
||||||
|
|
||||||
return getAccountabilityForToken(req.token, decodedToken?.iss, context.accountability, context.database)
|
/*filter('auth.jwt', (status, user, provider) => {
|
||||||
});
|
|
||||||
|
|
||||||
|
})*/
|
||||||
/*filter('auth.jwt', (status, user, provider) => {
|
|
||||||
|
|
||||||
})*/
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"lib": ["ES2022"],
|
"lib": [
|
||||||
"module": "ES2022",
|
"ES2022"
|
||||||
"preserveConstEnums": true,
|
],
|
||||||
"moduleResolution": "node",
|
"module": "ES2022",
|
||||||
"strict": true,
|
"preserveConstEnums": true,
|
||||||
"sourceMap": true,
|
"moduleResolution": "node",
|
||||||
"declaration": true,
|
"strict": true,
|
||||||
"noUnusedLocals": true,
|
"sourceMap": true,
|
||||||
"target": "es2022",
|
"declaration": true,
|
||||||
"types": ["node"],
|
"noUnusedLocals": true,
|
||||||
"outDir": "dist",
|
"target": "es2022",
|
||||||
"typeRoots": ["node_modules/@types"],
|
"types": [
|
||||||
"allowSyntheticDefaultImports": true,
|
"node"
|
||||||
},
|
],
|
||||||
"include": ["src/**/*.ts"],
|
"outDir": "dist",
|
||||||
"exclude": []
|
"typeRoots": [
|
||||||
|
"node_modules/@types"
|
||||||
|
],
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
|||||||
197
yarn-error.log
Normal file
197
yarn-error.log
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
Arguments:
|
||||||
|
/Users/ian/.nvm/versions/node/v20.18.0/bin/node /usr/local/bin/yarn
|
||||||
|
|
||||||
|
PATH:
|
||||||
|
/opt/homebrew/opt/openjdk@17/bin:/Users/ian/.nvm/versions/node/v20.18.0/bin:/Users/ian/Library/pnpm:/Users/ian/.bun/bin:/Library/Frameworks/Python.framework/Versions/3.7/bin:/Library/Frameworks/Python.framework/Versions/3.9/bin:/Library/Frameworks/Python.framework/Versions/3.12/bin:/Library/Frameworks/Python.framework/Versions/2.7/bin:/Library/Frameworks/Python.framework/Versions/3.10/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/Applications/VMware Fusion.app/Contents/Public:/Users/ian/development/repos/directus-extension-external-jwt/node_modules/.bin:/Users/ian/.orbstack/bin:/platform-tools:/emulator:/platform-tools:/opt/homebrew/bin:/opt/homebrew/sbin:/Users/ian/go/bin/:/Users/ian/Library/pnpm:/Users/ian/Library/Android/sdk/emulator:/Users/ian/Library/Android/sdk/platform-tools:/Users/ian/Library/Python/3.7/bin:/Users/ian/.dotnet/tools:/Users/ian/.yarn/bin
|
||||||
|
|
||||||
|
Yarn version:
|
||||||
|
1.22.19
|
||||||
|
|
||||||
|
Node version:
|
||||||
|
20.18.0
|
||||||
|
|
||||||
|
Platform:
|
||||||
|
darwin arm64
|
||||||
|
|
||||||
|
Trace:
|
||||||
|
Error: EEXIST: file already exists, mkdir '/Users/ian/development/repos/directus-extension-external-jwt/node_modules/@directus/extensions-sdk/node_modules/@directus'
|
||||||
|
|
||||||
|
npm manifest:
|
||||||
|
{
|
||||||
|
"name": "directus-extension-external-jwt",
|
||||||
|
"description": "External JWT Directus Extension allow directus to trust tokens issued by an oauth2 or OIDC provider",
|
||||||
|
"icon": "extension",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"keywords": [
|
||||||
|
"directus",
|
||||||
|
"directus-extension",
|
||||||
|
"directus-custom-hook",
|
||||||
|
"directus-external-jwt"
|
||||||
|
],
|
||||||
|
"homepage": "https://github.com/Zerosubnet/directus-extension-external-jwt",
|
||||||
|
"license": "LGPL-3.0-only",
|
||||||
|
"author": {
|
||||||
|
"name": "zerosubnet"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Zerosubnet/directus-extension-external-jwt.git"
|
||||||
|
},
|
||||||
|
"type": "module",
|
||||||
|
"release": {
|
||||||
|
"branches": [
|
||||||
|
"main",
|
||||||
|
"next",
|
||||||
|
{
|
||||||
|
"name": "beta",
|
||||||
|
"prerelease": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@semantic-release/commit-analyzer",
|
||||||
|
"@semantic-release/release-notes-generator",
|
||||||
|
"@semantic-release/changelog",
|
||||||
|
"@semantic-release/npm",
|
||||||
|
[
|
||||||
|
"@semantic-release/github",
|
||||||
|
{
|
||||||
|
"assets": [
|
||||||
|
"dist/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"@semantic-release/exec",
|
||||||
|
{
|
||||||
|
"tagImage": "docker tag ${SRCIMAGE} ${DSTIMAGE}:${nextRelease.version}",
|
||||||
|
"publishImage": "docker push ${DSTIMAGE}:${nextRelease.version}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"preset": "angular"
|
||||||
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
|
"directus:extension": {
|
||||||
|
"type": "hook",
|
||||||
|
"path": "dist/index.js",
|
||||||
|
"source": "src/index.ts",
|
||||||
|
"host": "^10.1.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.0.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "directus-extension build && npm run sync",
|
||||||
|
"dev": "directus-extension build -w --no-minify",
|
||||||
|
"link": "directus-extension link",
|
||||||
|
"sync": "rm -rf ./extensions/directus-extension-external-jwt && mkdir -p ./extensions/directus-extension-external-jwt/dist && ln ./package.json ./extensions/directus-extension-external-jwt/package.json && ln ./dist/index.js ./extensions/directus-extension-external-jwt/dist/index.js",
|
||||||
|
"directus": "pnpm dlx directus start",
|
||||||
|
"lint": "eslint . --ext .ts",
|
||||||
|
"test": "vitest",
|
||||||
|
"test:coverage": "vitest run --coverage"
|
||||||
|
},
|
||||||
|
"nyc": {
|
||||||
|
"extension": [
|
||||||
|
".ts",
|
||||||
|
".tsx"
|
||||||
|
],
|
||||||
|
"reporter": [
|
||||||
|
"text",
|
||||||
|
"lcov"
|
||||||
|
],
|
||||||
|
"report-dir": "coverage",
|
||||||
|
"all": true,
|
||||||
|
"extends": "@istanbuljs/nyc-config-typescript",
|
||||||
|
"check-coverage": true,
|
||||||
|
"include": [
|
||||||
|
"src/**/*.[tj]s?(x)"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"src/_tests_/**/*.*",
|
||||||
|
"src/**/*.test.[tj]s?(x)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@directus/errors": "^0.3.2",
|
||||||
|
"@directus/extensions-sdk": "^13.0.1",
|
||||||
|
"@directus/tsconfig": "^1.0.1",
|
||||||
|
"@directus/types": "^11.1.2",
|
||||||
|
"@directus/utils": "^11.0.9",
|
||||||
|
"@istanbuljs/nyc-config-typescript": "^1.0.2",
|
||||||
|
"@semantic-release/changelog": "^6.0.3",
|
||||||
|
"@semantic-release/commit-analyzer": "^10.0.4",
|
||||||
|
"@semantic-release/exec": "^6.0.3",
|
||||||
|
"@semantic-release/github": "^9.2.6",
|
||||||
|
"@semantic-release/npm": "^10.0.6",
|
||||||
|
"@types/chai": "^4.3.16",
|
||||||
|
"@types/chai-as-promised": "^7.1.8",
|
||||||
|
"@types/config": "^3.3.4",
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/fs-extra": "^11.0.4",
|
||||||
|
"@types/js-yaml": "^4.0.9",
|
||||||
|
"@types/jsonwebtoken": "^9.0.6",
|
||||||
|
"@types/lodash-es": "^4.17.12",
|
||||||
|
"@types/mocha": "^10.0.6",
|
||||||
|
"@types/node": "^20.14.2",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
|
"@vitest/coverage-istanbul": "^0.34.6",
|
||||||
|
"axios": "^1.7.2",
|
||||||
|
"config": "^3.3.11",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-config-standard-with-typescript": "^37.0.0",
|
||||||
|
"eslint-plugin-import": "^2.29.1",
|
||||||
|
"eslint-plugin-n": "^15.7.0",
|
||||||
|
"eslint-plugin-no-loops": "^0.3.0",
|
||||||
|
"eslint-plugin-promise": "^6.2.0",
|
||||||
|
"fs-extra": "^11.2.0",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
|
"lodash-es": "^4.17.21",
|
||||||
|
"nyc": "^15.1.0",
|
||||||
|
"semantic-release": "^21.1.2",
|
||||||
|
"sqlite3": "^5.1.7",
|
||||||
|
"ts-mocha": "^10.0.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.4.5",
|
||||||
|
"vitest": "^0.34.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@directus/extensions": "^3.0.5",
|
||||||
|
"@keyv/redis": "^2.8.5",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"jwks-rsa": "^3.1.0",
|
||||||
|
"keyv": "^4.5.4",
|
||||||
|
"openid-client": "^5.6.5",
|
||||||
|
"uuid": "^11.1.0"
|
||||||
|
},
|
||||||
|
"pnpm": {
|
||||||
|
"overrides": {
|
||||||
|
"vite@<4.3.9": "^4.3.9",
|
||||||
|
"vite@>4.3.9": "^4.5.3",
|
||||||
|
"zod@<=3.22.2": ">=3.22.3",
|
||||||
|
"axios@<=1.4.0": ">=1.4.1",
|
||||||
|
"axios@>=1.3.2 <=1.7.3": ">=1.7.4",
|
||||||
|
"micromatch@<4.0.8": ">=4.0.8",
|
||||||
|
"vite@>=4.0.0 <4.5.4": ">=4.5.4",
|
||||||
|
"vite@>=4.0.0 <=4.5.3": ">=4.5.4",
|
||||||
|
"rollup@>=3.0.0 <3.29.5": ">=3.29.5",
|
||||||
|
"cross-spawn@>=7.0.0 <7.0.5": ">=7.0.5",
|
||||||
|
"nanoid@<3.3.8": ">=3.3.8",
|
||||||
|
"@octokit/request-error@>=1.0.0 <5.1.1": ">=5.1.1",
|
||||||
|
"@octokit/endpoint@>=9.0.5 <9.0.6": ">=9.0.6",
|
||||||
|
"@octokit/request@>=1.0.0 <9.2.1": ">=9.2.1",
|
||||||
|
"@octokit/plugin-paginate-rest@>=1.0.0 <11.4.1": ">=11.4.1",
|
||||||
|
"serialize-javascript@<6.0.2": ">=6.0.2",
|
||||||
|
"esbuild@<=0.24.2": ">=0.25.0",
|
||||||
|
"vite@<=4.5.5": ">=4.5.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
yarn manifest:
|
||||||
|
No manifest
|
||||||
|
|
||||||
|
Lockfile:
|
||||||
|
No lockfile
|
||||||
Reference in New Issue
Block a user