s3db.js 13.5.1 → 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.
Files changed (105) hide show
  1. package/README.md +25 -10
  2. package/dist/{s3db.cjs.js → s3db.cjs} +30323 -24958
  3. package/dist/s3db.cjs.map +1 -0
  4. package/dist/s3db.es.js +24026 -18654
  5. package/dist/s3db.es.js.map +1 -1
  6. package/package.json +216 -20
  7. package/src/concerns/id.js +90 -6
  8. package/src/concerns/index.js +2 -1
  9. package/src/concerns/password-hashing.js +150 -0
  10. package/src/database.class.js +4 -0
  11. package/src/plugins/api/auth/basic-auth.js +23 -1
  12. package/src/plugins/api/auth/index.js +49 -3
  13. package/src/plugins/api/auth/oauth2-auth.js +171 -0
  14. package/src/plugins/api/auth/oidc-auth.js +789 -0
  15. package/src/plugins/api/auth/oidc-client.js +462 -0
  16. package/src/plugins/api/auth/path-auth-matcher.js +284 -0
  17. package/src/plugins/api/concerns/event-emitter.js +134 -0
  18. package/src/plugins/api/concerns/failban-manager.js +651 -0
  19. package/src/plugins/api/concerns/guards-helpers.js +402 -0
  20. package/src/plugins/api/concerns/metrics-collector.js +346 -0
  21. package/src/plugins/api/index.js +503 -54
  22. package/src/plugins/api/middlewares/failban.js +305 -0
  23. package/src/plugins/api/middlewares/rate-limit.js +301 -0
  24. package/src/plugins/api/middlewares/request-id.js +74 -0
  25. package/src/plugins/api/middlewares/security-headers.js +120 -0
  26. package/src/plugins/api/middlewares/session-tracking.js +194 -0
  27. package/src/plugins/api/routes/auth-routes.js +23 -3
  28. package/src/plugins/api/routes/resource-routes.js +71 -29
  29. package/src/plugins/api/server.js +1017 -94
  30. package/src/plugins/api/utils/guards.js +213 -0
  31. package/src/plugins/api/utils/mime-types.js +154 -0
  32. package/src/plugins/api/utils/openapi-generator.js +44 -11
  33. package/src/plugins/api/utils/path-matcher.js +173 -0
  34. package/src/plugins/api/utils/static-filesystem.js +262 -0
  35. package/src/plugins/api/utils/static-s3.js +231 -0
  36. package/src/plugins/api/utils/template-engine.js +188 -0
  37. package/src/plugins/cloud-inventory/drivers/alibaba-driver.js +853 -0
  38. package/src/plugins/cloud-inventory/drivers/aws-driver.js +2554 -0
  39. package/src/plugins/cloud-inventory/drivers/azure-driver.js +637 -0
  40. package/src/plugins/cloud-inventory/drivers/base-driver.js +99 -0
  41. package/src/plugins/cloud-inventory/drivers/cloudflare-driver.js +620 -0
  42. package/src/plugins/cloud-inventory/drivers/digitalocean-driver.js +698 -0
  43. package/src/plugins/cloud-inventory/drivers/gcp-driver.js +645 -0
  44. package/src/plugins/cloud-inventory/drivers/hetzner-driver.js +559 -0
  45. package/src/plugins/cloud-inventory/drivers/linode-driver.js +614 -0
  46. package/src/plugins/cloud-inventory/drivers/mock-drivers.js +449 -0
  47. package/src/plugins/cloud-inventory/drivers/mongodb-atlas-driver.js +771 -0
  48. package/src/plugins/cloud-inventory/drivers/oracle-driver.js +768 -0
  49. package/src/plugins/cloud-inventory/drivers/vultr-driver.js +636 -0
  50. package/src/plugins/cloud-inventory/index.js +20 -0
  51. package/src/plugins/cloud-inventory/registry.js +146 -0
  52. package/src/plugins/cloud-inventory/terraform-exporter.js +362 -0
  53. package/src/plugins/cloud-inventory.plugin.js +1333 -0
  54. package/src/plugins/concerns/plugin-dependencies.js +61 -1
  55. package/src/plugins/eventual-consistency/analytics.js +1 -0
  56. package/src/plugins/identity/README.md +335 -0
  57. package/src/plugins/identity/concerns/mfa-manager.js +204 -0
  58. package/src/plugins/identity/concerns/password.js +138 -0
  59. package/src/plugins/identity/concerns/resource-schemas.js +273 -0
  60. package/src/plugins/identity/concerns/token-generator.js +172 -0
  61. package/src/plugins/identity/email-service.js +422 -0
  62. package/src/plugins/identity/index.js +1052 -0
  63. package/src/plugins/identity/oauth2-server.js +1033 -0
  64. package/src/plugins/identity/oidc-discovery.js +285 -0
  65. package/src/plugins/identity/rsa-keys.js +323 -0
  66. package/src/plugins/identity/server.js +500 -0
  67. package/src/plugins/identity/session-manager.js +453 -0
  68. package/src/plugins/identity/ui/layouts/base.js +251 -0
  69. package/src/plugins/identity/ui/middleware.js +135 -0
  70. package/src/plugins/identity/ui/pages/admin/client-form.js +247 -0
  71. package/src/plugins/identity/ui/pages/admin/clients.js +179 -0
  72. package/src/plugins/identity/ui/pages/admin/dashboard.js +181 -0
  73. package/src/plugins/identity/ui/pages/admin/user-form.js +283 -0
  74. package/src/plugins/identity/ui/pages/admin/users.js +263 -0
  75. package/src/plugins/identity/ui/pages/consent.js +262 -0
  76. package/src/plugins/identity/ui/pages/forgot-password.js +104 -0
  77. package/src/plugins/identity/ui/pages/login.js +144 -0
  78. package/src/plugins/identity/ui/pages/mfa-backup-codes.js +180 -0
  79. package/src/plugins/identity/ui/pages/mfa-enrollment.js +187 -0
  80. package/src/plugins/identity/ui/pages/mfa-verification.js +178 -0
  81. package/src/plugins/identity/ui/pages/oauth-error.js +225 -0
  82. package/src/plugins/identity/ui/pages/profile.js +361 -0
  83. package/src/plugins/identity/ui/pages/register.js +226 -0
  84. package/src/plugins/identity/ui/pages/reset-password.js +128 -0
  85. package/src/plugins/identity/ui/pages/verify-email.js +172 -0
  86. package/src/plugins/identity/ui/routes.js +2541 -0
  87. package/src/plugins/identity/ui/styles/main.css +465 -0
  88. package/src/plugins/index.js +4 -1
  89. package/src/plugins/ml/base-model.class.js +32 -7
  90. package/src/plugins/ml/classification-model.class.js +1 -1
  91. package/src/plugins/ml/timeseries-model.class.js +3 -1
  92. package/src/plugins/ml.plugin.js +124 -32
  93. package/src/plugins/shared/error-handler.js +147 -0
  94. package/src/plugins/shared/index.js +9 -0
  95. package/src/plugins/shared/middlewares/compression.js +117 -0
  96. package/src/plugins/shared/middlewares/cors.js +49 -0
  97. package/src/plugins/shared/middlewares/index.js +11 -0
  98. package/src/plugins/shared/middlewares/logging.js +54 -0
  99. package/src/plugins/shared/middlewares/rate-limit.js +73 -0
  100. package/src/plugins/shared/middlewares/security.js +158 -0
  101. package/src/plugins/shared/response-formatter.js +264 -0
  102. package/src/resource.class.js +140 -12
  103. package/src/schema.class.js +30 -1
  104. package/src/validator.class.js +57 -6
  105. package/dist/s3db.cjs.js.map +0 -1
@@ -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;