s3db.js 13.4.0 → 13.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -10
- package/dist/{s3db.cjs.js → s3db.cjs} +38801 -32446
- package/dist/s3db.cjs.map +1 -0
- package/dist/s3db.es.js +38653 -32291
- package/dist/s3db.es.js.map +1 -1
- package/package.json +218 -22
- package/src/concerns/id.js +90 -6
- package/src/concerns/index.js +2 -1
- package/src/concerns/password-hashing.js +150 -0
- package/src/database.class.js +6 -2
- package/src/plugins/api/auth/basic-auth.js +40 -10
- package/src/plugins/api/auth/index.js +49 -3
- package/src/plugins/api/auth/oauth2-auth.js +171 -0
- package/src/plugins/api/auth/oidc-auth.js +789 -0
- package/src/plugins/api/auth/oidc-client.js +462 -0
- package/src/plugins/api/auth/path-auth-matcher.js +284 -0
- package/src/plugins/api/concerns/event-emitter.js +134 -0
- package/src/plugins/api/concerns/failban-manager.js +651 -0
- package/src/plugins/api/concerns/guards-helpers.js +402 -0
- package/src/plugins/api/concerns/metrics-collector.js +346 -0
- package/src/plugins/api/index.js +510 -57
- package/src/plugins/api/middlewares/failban.js +305 -0
- package/src/plugins/api/middlewares/rate-limit.js +301 -0
- package/src/plugins/api/middlewares/request-id.js +74 -0
- package/src/plugins/api/middlewares/security-headers.js +120 -0
- package/src/plugins/api/middlewares/session-tracking.js +194 -0
- package/src/plugins/api/routes/auth-routes.js +119 -78
- package/src/plugins/api/routes/resource-routes.js +73 -30
- package/src/plugins/api/server.js +1139 -45
- package/src/plugins/api/utils/custom-routes.js +102 -0
- package/src/plugins/api/utils/guards.js +213 -0
- package/src/plugins/api/utils/mime-types.js +154 -0
- package/src/plugins/api/utils/openapi-generator.js +91 -12
- package/src/plugins/api/utils/path-matcher.js +173 -0
- package/src/plugins/api/utils/static-filesystem.js +262 -0
- package/src/plugins/api/utils/static-s3.js +231 -0
- package/src/plugins/api/utils/template-engine.js +188 -0
- package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
- package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
- package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
- package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
- package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
- package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
- package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
- package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
- package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
- package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
- package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
- package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
- package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
- package/src/plugins/cloud-inventory/index.js +20 -0
- package/src/plugins/cloud-inventory/registry.js +146 -0
- package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
- package/src/plugins/cloud-inventory.plugin.js +1333 -0
- package/src/plugins/concerns/plugin-dependencies.js +62 -2
- package/src/plugins/eventual-consistency/analytics.js +1 -0
- package/src/plugins/eventual-consistency/consolidation.js +2 -2
- package/src/plugins/eventual-consistency/garbage-collection.js +2 -2
- package/src/plugins/eventual-consistency/install.js +2 -2
- package/src/plugins/identity/README.md +335 -0
- package/src/plugins/identity/concerns/mfa-manager.js +204 -0
- package/src/plugins/identity/concerns/password.js +138 -0
- package/src/plugins/identity/concerns/resource-schemas.js +273 -0
- package/src/plugins/identity/concerns/token-generator.js +172 -0
- package/src/plugins/identity/email-service.js +422 -0
- package/src/plugins/identity/index.js +1052 -0
- package/src/plugins/identity/oauth2-server.js +1033 -0
- package/src/plugins/identity/oidc-discovery.js +285 -0
- package/src/plugins/identity/rsa-keys.js +323 -0
- package/src/plugins/identity/server.js +500 -0
- package/src/plugins/identity/session-manager.js +453 -0
- package/src/plugins/identity/ui/layouts/base.js +251 -0
- package/src/plugins/identity/ui/middleware.js +135 -0
- package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
- package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
- package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
- package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
- package/src/plugins/identity/ui/pages/admin/users.js +263 -0
- package/src/plugins/identity/ui/pages/consent.js +262 -0
- package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
- package/src/plugins/identity/ui/pages/login.js +144 -0
- package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
- package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
- package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
- package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
- package/src/plugins/identity/ui/pages/profile.js +361 -0
- package/src/plugins/identity/ui/pages/register.js +226 -0
- package/src/plugins/identity/ui/pages/reset-password.js +128 -0
- package/src/plugins/identity/ui/pages/verify-email.js +172 -0
- package/src/plugins/identity/ui/routes.js +2541 -0
- package/src/plugins/identity/ui/styles/main.css +465 -0
- package/src/plugins/index.js +4 -1
- package/src/plugins/ml/base-model.class.js +65 -16
- package/src/plugins/ml/classification-model.class.js +1 -1
- package/src/plugins/ml/timeseries-model.class.js +3 -1
- package/src/plugins/ml.plugin.js +584 -31
- package/src/plugins/shared/error-handler.js +147 -0
- package/src/plugins/shared/index.js +9 -0
- package/src/plugins/shared/middlewares/compression.js +117 -0
- package/src/plugins/shared/middlewares/cors.js +49 -0
- package/src/plugins/shared/middlewares/index.js +11 -0
- package/src/plugins/shared/middlewares/logging.js +54 -0
- package/src/plugins/shared/middlewares/rate-limit.js +73 -0
- package/src/plugins/shared/middlewares/security.js +158 -0
- package/src/plugins/shared/response-formatter.js +264 -0
- package/src/plugins/state-machine.plugin.js +57 -2
- package/src/resource.class.js +140 -12
- package/src/schema.class.js +30 -1
- package/src/validator.class.js +57 -6
- package/dist/s3db.cjs.js.map +0 -1
|
@@ -67,21 +67,31 @@ async function verifyPassword(inputPassword, storedPassword, passphrase) {
|
|
|
67
67
|
* Create Basic Auth middleware
|
|
68
68
|
* @param {Object} options - Basic Auth options
|
|
69
69
|
* @param {string} options.realm - Authentication realm (default: 'API Access')
|
|
70
|
-
* @param {Object} options.
|
|
70
|
+
* @param {Object} options.authResource - Resource for credential validation
|
|
71
|
+
* @param {string} options.usernameField - Field name for username (default: 'email')
|
|
72
|
+
* @param {string} options.passwordField - Field name for password (default: 'password')
|
|
71
73
|
* @param {string} options.passphrase - Passphrase for password decryption
|
|
72
74
|
* @param {boolean} options.optional - If true, allows requests without auth
|
|
75
|
+
* @param {Object} options.adminUser - Root admin credentials (bypasses DB lookup)
|
|
76
|
+
* @param {boolean} options.adminUser.enabled - Enable admin root user bypass (default: false)
|
|
77
|
+
* @param {string} options.adminUser.username - Admin username
|
|
78
|
+
* @param {string} options.adminUser.password - Admin password (plain text)
|
|
79
|
+
* @param {Array<string>} options.adminUser.scopes - Admin scopes (default: ['admin'])
|
|
73
80
|
* @returns {Function} Hono middleware
|
|
74
81
|
*/
|
|
75
82
|
export function basicAuth(options = {}) {
|
|
76
83
|
const {
|
|
77
84
|
realm = 'API Access',
|
|
78
|
-
|
|
85
|
+
authResource,
|
|
86
|
+
usernameField = 'email',
|
|
87
|
+
passwordField = 'password',
|
|
79
88
|
passphrase = 'secret',
|
|
80
|
-
optional = false
|
|
89
|
+
optional = false,
|
|
90
|
+
adminUser = null
|
|
81
91
|
} = options;
|
|
82
92
|
|
|
83
|
-
if (!
|
|
84
|
-
throw new Error('
|
|
93
|
+
if (!authResource) {
|
|
94
|
+
throw new Error('authResource is required for Basic authentication');
|
|
85
95
|
}
|
|
86
96
|
|
|
87
97
|
return async (c, next) => {
|
|
@@ -107,9 +117,26 @@ export function basicAuth(options = {}) {
|
|
|
107
117
|
|
|
108
118
|
const { username, password } = credentials;
|
|
109
119
|
|
|
110
|
-
//
|
|
120
|
+
// Check admin user first (bypasses DB lookup)
|
|
121
|
+
if (adminUser && adminUser.enabled === true) {
|
|
122
|
+
if (username === adminUser.username && password === adminUser.password) {
|
|
123
|
+
c.set('user', {
|
|
124
|
+
id: 'root',
|
|
125
|
+
username: adminUser.username,
|
|
126
|
+
email: adminUser.username,
|
|
127
|
+
scopes: adminUser.scopes || ['admin'],
|
|
128
|
+
authMethod: 'basic-admin'
|
|
129
|
+
});
|
|
130
|
+
c.set('authMethod', 'basic');
|
|
131
|
+
await next();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Query user by configured username field
|
|
111
137
|
try {
|
|
112
|
-
const
|
|
138
|
+
const queryFilter = { [usernameField]: username };
|
|
139
|
+
const users = await authResource.query(queryFilter);
|
|
113
140
|
|
|
114
141
|
if (!users || users.length === 0) {
|
|
115
142
|
c.header('WWW-Authenticate', `Basic realm="${realm}"`);
|
|
@@ -119,14 +146,17 @@ export function basicAuth(options = {}) {
|
|
|
119
146
|
|
|
120
147
|
const user = users[0];
|
|
121
148
|
|
|
122
|
-
if
|
|
149
|
+
// Check if user is active (if field exists)
|
|
150
|
+
if (user.active !== undefined && !user.active) {
|
|
123
151
|
c.header('WWW-Authenticate', `Basic realm="${realm}"`);
|
|
124
152
|
const response = unauthorized('User account is inactive');
|
|
125
153
|
return c.json(response, response._status);
|
|
126
154
|
}
|
|
127
155
|
|
|
128
|
-
// Verify password
|
|
129
|
-
|
|
156
|
+
// Verify password using configured password field
|
|
157
|
+
// Schema handles encryption/decryption for 'secret' field types
|
|
158
|
+
const storedPassword = user[passwordField];
|
|
159
|
+
const isValid = storedPassword === password;
|
|
130
160
|
|
|
131
161
|
if (!isValid) {
|
|
132
162
|
c.header('WWW-Authenticate', `Basic realm="${realm}"`);
|
|
@@ -7,17 +7,23 @@
|
|
|
7
7
|
import { jwtAuth } from './jwt-auth.js';
|
|
8
8
|
import { apiKeyAuth } from './api-key-auth.js';
|
|
9
9
|
import { basicAuth } from './basic-auth.js';
|
|
10
|
+
import { createOAuth2Handler } from './oauth2-auth.js';
|
|
11
|
+
import { OIDCClient } from './oidc-client.js';
|
|
10
12
|
import { unauthorized } from '../utils/response-formatter.js';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Create authentication middleware that supports multiple auth methods
|
|
14
16
|
* @param {Object} options - Authentication options
|
|
15
|
-
* @param {Array<string>} options.methods - Allowed auth methods (['jwt', 'apiKey', 'basic'])
|
|
17
|
+
* @param {Array<string>} options.methods - Allowed auth methods (['jwt', 'apiKey', 'basic', 'oauth2'])
|
|
16
18
|
* @param {Object} options.jwt - JWT configuration
|
|
17
19
|
* @param {Object} options.apiKey - API Key configuration
|
|
18
20
|
* @param {Object} options.basic - Basic Auth configuration
|
|
21
|
+
* @param {Object} options.oauth2 - OAuth2 configuration
|
|
22
|
+
* @param {Function} options.oidc - OIDC middleware (already configured)
|
|
19
23
|
* @param {Object} options.usersResource - Users resource
|
|
20
24
|
* @param {boolean} options.optional - If true, allows requests without auth
|
|
25
|
+
* @param {string} options.strategy - Auth strategy: 'any' (default, OR logic) or 'priority' (waterfall with explicit order)
|
|
26
|
+
* @param {Object} options.priorities - Priority map for 'priority' strategy { jwt: 1, oidc: 2, basic: 3 }
|
|
21
27
|
* @returns {Function} Hono middleware
|
|
22
28
|
*/
|
|
23
29
|
export function createAuthMiddleware(options = {}) {
|
|
@@ -26,8 +32,12 @@ export function createAuthMiddleware(options = {}) {
|
|
|
26
32
|
jwt: jwtConfig = {},
|
|
27
33
|
apiKey: apiKeyConfig = {},
|
|
28
34
|
basic: basicConfig = {},
|
|
35
|
+
oauth2: oauth2Config = {},
|
|
36
|
+
oidc: oidcMiddleware = null,
|
|
29
37
|
usersResource,
|
|
30
|
-
optional = false
|
|
38
|
+
optional = false,
|
|
39
|
+
strategy = 'any',
|
|
40
|
+
priorities = {}
|
|
31
41
|
} = options;
|
|
32
42
|
|
|
33
43
|
// If no methods specified, allow all requests
|
|
@@ -72,6 +82,38 @@ export function createAuthMiddleware(options = {}) {
|
|
|
72
82
|
});
|
|
73
83
|
}
|
|
74
84
|
|
|
85
|
+
if (methods.includes('oauth2') && oauth2Config.issuer) {
|
|
86
|
+
const oauth2Handler = createOAuth2Handler(oauth2Config, usersResource);
|
|
87
|
+
middlewares.push({
|
|
88
|
+
name: 'oauth2',
|
|
89
|
+
middleware: async (c, next) => {
|
|
90
|
+
const user = await oauth2Handler(c);
|
|
91
|
+
if (user) {
|
|
92
|
+
c.set('user', user);
|
|
93
|
+
return await next();
|
|
94
|
+
}
|
|
95
|
+
// No user, try next method
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// OIDC middleware (session-based authentication)
|
|
101
|
+
if (oidcMiddleware) {
|
|
102
|
+
middlewares.push({
|
|
103
|
+
name: 'oidc',
|
|
104
|
+
middleware: oidcMiddleware
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Sort middlewares by priority if strategy is 'priority'
|
|
109
|
+
if (strategy === 'priority' && Object.keys(priorities).length > 0) {
|
|
110
|
+
middlewares.sort((a, b) => {
|
|
111
|
+
const priorityA = priorities[a.name] || 999; // Unspecified = lowest priority
|
|
112
|
+
const priorityB = priorities[b.name] || 999;
|
|
113
|
+
return priorityA - priorityB; // Lower number = higher priority
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
75
117
|
// Return combined middleware
|
|
76
118
|
return async (c, next) => {
|
|
77
119
|
// Try each auth method
|
|
@@ -104,9 +146,13 @@ export function createAuthMiddleware(options = {}) {
|
|
|
104
146
|
};
|
|
105
147
|
}
|
|
106
148
|
|
|
149
|
+
export { OIDCClient };
|
|
150
|
+
|
|
107
151
|
export default {
|
|
108
152
|
createAuthMiddleware,
|
|
109
153
|
jwtAuth,
|
|
110
154
|
apiKeyAuth,
|
|
111
|
-
basicAuth
|
|
155
|
+
basicAuth,
|
|
156
|
+
createOAuth2Handler,
|
|
157
|
+
OIDCClient
|
|
112
158
|
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth2/OIDC Authentication Driver (Resource Server)
|
|
3
|
+
*
|
|
4
|
+
* Validates JWT access tokens issued by an OAuth2/OIDC Authorization Server.
|
|
5
|
+
* Fetches public keys from JWKS endpoint and verifies token signatures.
|
|
6
|
+
*
|
|
7
|
+
* Use this driver when your application acts as a Resource Server
|
|
8
|
+
* consuming tokens from an external Authorization Server (SSO).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* {
|
|
12
|
+
* driver: 'oauth2',
|
|
13
|
+
* config: {
|
|
14
|
+
* issuer: 'http://localhost:4000',
|
|
15
|
+
* jwksUri: 'http://localhost:4000/.well-known/jwks.json',
|
|
16
|
+
* audience: 'my-api',
|
|
17
|
+
* algorithms: ['RS256'],
|
|
18
|
+
* cacheTTL: 3600000 // 1 hour
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
|
24
|
+
|
|
25
|
+
// Cache for JWKS (avoids fetching on every request)
|
|
26
|
+
const jwksCache = new Map();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create OAuth2 authentication handler
|
|
30
|
+
* @param {Object} config - OAuth2 configuration
|
|
31
|
+
* @param {Object} usersResource - s3db.js users resource
|
|
32
|
+
* @returns {Function} Hono middleware
|
|
33
|
+
*/
|
|
34
|
+
export function createOAuth2Handler(config, usersResource) {
|
|
35
|
+
const {
|
|
36
|
+
issuer,
|
|
37
|
+
jwksUri,
|
|
38
|
+
audience = null,
|
|
39
|
+
algorithms = ['RS256', 'ES256'],
|
|
40
|
+
cacheTTL = 3600000, // 1 hour
|
|
41
|
+
clockTolerance = 60, // 60 seconds tolerance for exp/nbf
|
|
42
|
+
validateScopes = true,
|
|
43
|
+
fetchUserInfo = true
|
|
44
|
+
} = config;
|
|
45
|
+
|
|
46
|
+
if (!issuer) {
|
|
47
|
+
throw new Error('[OAuth2 Auth] Missing required config: issuer');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Construct JWKS URI from issuer if not provided
|
|
51
|
+
const finalJwksUri = jwksUri || `${issuer}/.well-known/jwks.json`;
|
|
52
|
+
|
|
53
|
+
// Get or create JWKS fetcher (cached)
|
|
54
|
+
const getJWKS = () => {
|
|
55
|
+
const cacheKey = finalJwksUri;
|
|
56
|
+
|
|
57
|
+
if (jwksCache.has(cacheKey)) {
|
|
58
|
+
const cached = jwksCache.get(cacheKey);
|
|
59
|
+
if (Date.now() - cached.timestamp < cacheTTL) {
|
|
60
|
+
return cached.jwks;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create remote JWKS fetcher
|
|
65
|
+
const jwks = createRemoteJWKSet(new URL(finalJwksUri), {
|
|
66
|
+
cooldownDuration: 30000, // 30 seconds cooldown between fetches
|
|
67
|
+
cacheMaxAge: cacheTTL
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
jwksCache.set(cacheKey, {
|
|
71
|
+
jwks,
|
|
72
|
+
timestamp: Date.now()
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
return jwks;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* OAuth2 authentication middleware
|
|
80
|
+
*/
|
|
81
|
+
return async (c) => {
|
|
82
|
+
// Extract token from Authorization header
|
|
83
|
+
const authHeader = c.req.header('authorization') || c.req.header('Authorization');
|
|
84
|
+
|
|
85
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
86
|
+
return null; // No OAuth2 token, try next auth method
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const token = authHeader.substring(7); // Remove 'Bearer ' prefix
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
// Verify JWT token with remote JWKS
|
|
93
|
+
const jwks = getJWKS();
|
|
94
|
+
|
|
95
|
+
const verifyOptions = {
|
|
96
|
+
issuer,
|
|
97
|
+
algorithms,
|
|
98
|
+
clockTolerance
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if (audience) {
|
|
102
|
+
verifyOptions.audience = audience;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { payload } = await jwtVerify(token, jwks, verifyOptions);
|
|
106
|
+
|
|
107
|
+
// Extract user info from token claims
|
|
108
|
+
const userId = payload.sub; // Subject (user ID)
|
|
109
|
+
const email = payload.email || null;
|
|
110
|
+
const username = payload.preferred_username || payload.username || email;
|
|
111
|
+
const scopes = payload.scope ? payload.scope.split(' ') : (payload.scopes || []);
|
|
112
|
+
const role = payload.role || 'user';
|
|
113
|
+
|
|
114
|
+
// Optionally fetch full user info from database
|
|
115
|
+
let user = null;
|
|
116
|
+
|
|
117
|
+
if (fetchUserInfo && userId && usersResource) {
|
|
118
|
+
try {
|
|
119
|
+
// Try to find user by ID
|
|
120
|
+
user = await usersResource.get(userId).catch(() => null);
|
|
121
|
+
|
|
122
|
+
// If not found by ID, try by email
|
|
123
|
+
if (!user && email) {
|
|
124
|
+
const users = await usersResource.query({ email }, { limit: 1 });
|
|
125
|
+
user = users[0] || null;
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
// User not found in local database, use token claims only
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// If user found in database, merge with token claims
|
|
133
|
+
if (user) {
|
|
134
|
+
return {
|
|
135
|
+
...user,
|
|
136
|
+
scopes: user.scopes || scopes, // Prefer database scopes
|
|
137
|
+
role: user.role || role,
|
|
138
|
+
tokenClaims: payload // Include full token claims
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// User not in database, create virtual user from token
|
|
143
|
+
return {
|
|
144
|
+
id: userId,
|
|
145
|
+
username: username || userId,
|
|
146
|
+
email,
|
|
147
|
+
role,
|
|
148
|
+
scopes,
|
|
149
|
+
active: true,
|
|
150
|
+
tokenClaims: payload,
|
|
151
|
+
isVirtual: true // Flag to indicate user is not in local database
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
} catch (err) {
|
|
155
|
+
// Token verification failed
|
|
156
|
+
if (config.verbose) {
|
|
157
|
+
console.error('[OAuth2 Auth] Token verification failed:', err.message);
|
|
158
|
+
}
|
|
159
|
+
return null; // Invalid token, try next auth method
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Clear JWKS cache (useful for testing or when keys are rotated)
|
|
166
|
+
*/
|
|
167
|
+
export function clearJWKSCache() {
|
|
168
|
+
jwksCache.clear();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export default createOAuth2Handler;
|