@gzl10/nexus-plugin-auth-providers 0.14.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.
@@ -0,0 +1,489 @@
1
+ // src/shared/create-auth-service.ts
2
+ import { randomUUID } from "crypto";
3
+ import { createStateManager, checkAllowedDomain } from "@gzl10/nexus-sdk";
4
+ function buildCallbackUrl(moduleName, req) {
5
+ const backendUrl = process.env["BACKEND_URL"];
6
+ if (backendUrl) {
7
+ return `${backendUrl.replace(/\/$/, "")}/api/v1/${moduleName}/callback`;
8
+ }
9
+ let proto = req?.headers?.["x-forwarded-proto"] || req?.protocol || "http";
10
+ const host = req?.headers?.["x-forwarded-host"] || req?.headers?.["host"] || "localhost";
11
+ if (proto === "http" && host !== "localhost" && !host.startsWith("localhost:") && !host.startsWith("127.0.0.1")) {
12
+ proto = "https";
13
+ }
14
+ return `${proto}://${host}/api/v1/${moduleName}/callback`;
15
+ }
16
+ function createAuthPluginService(options) {
17
+ const { adapter, ctx } = options;
18
+ const { logger, errors } = ctx.core;
19
+ const PROVIDER = adapter.provider;
20
+ let stateManager = null;
21
+ function getStateManager() {
22
+ if (!stateManager) {
23
+ stateManager = createStateManager();
24
+ }
25
+ return stateManager;
26
+ }
27
+ async function getAuthorizationUrl(redirectUri, linkUserId, returnUrl) {
28
+ const config = await adapter.getValidConfig();
29
+ const state = randomUUID();
30
+ const nonce = randomUUID();
31
+ const stateData = {
32
+ state,
33
+ nonce,
34
+ redirectUri,
35
+ createdAt: Date.now(),
36
+ linkUserId,
37
+ returnUrl
38
+ };
39
+ getStateManager().store(state, stateData);
40
+ const scopes = config.scopes?.split(" ").filter(Boolean) || [];
41
+ const url = await adapter.buildAuthorizationUrl(config, {
42
+ redirectUri,
43
+ state,
44
+ nonce,
45
+ scopes
46
+ });
47
+ logger.debug({ state, redirectUri, linkUserId }, `Generated ${PROVIDER} authorization URL`);
48
+ return { url, state };
49
+ }
50
+ async function verifyState(state) {
51
+ return getStateManager().verify(state);
52
+ }
53
+ async function clearState(state) {
54
+ getStateManager().clear(state);
55
+ }
56
+ async function handleCallback(code, state) {
57
+ const stateData = await verifyState(state);
58
+ if (!stateData) {
59
+ throw new errors.ValidationError("Invalid or expired state");
60
+ }
61
+ const config = await adapter.getValidConfig();
62
+ const userInfo = await adapter.exchangeCodeAndGetUserInfo(
63
+ config,
64
+ code,
65
+ stateData.redirectUri,
66
+ stateData.nonce || ""
67
+ );
68
+ logger.debug(
69
+ { providerUserId: userInfo.providerUserId, email: userInfo.email },
70
+ `Got user info from ${PROVIDER}`
71
+ );
72
+ if (userInfo.email) {
73
+ const domainResult = checkAllowedDomain(userInfo.email, config.allowed_domains || null);
74
+ if (!domainResult.allowed) {
75
+ throw new errors.ForbiddenError(`Email domain '${domainResult.domain}' is not allowed`);
76
+ }
77
+ }
78
+ const authService = ctx.services.get("auth");
79
+ const identity = await authService.findIdentity(PROVIDER, userInfo.providerUserId);
80
+ let nexusUser;
81
+ if (stateData.linkUserId) {
82
+ if (identity) {
83
+ throw new errors.ValidationError(`This ${PROVIDER} account is already linked to another user`);
84
+ }
85
+ nexusUser = await authService.findUserById(stateData.linkUserId);
86
+ if (!nexusUser) {
87
+ throw new errors.NotFoundError("User not found");
88
+ }
89
+ await authService.linkIdentity({
90
+ userId: stateData.linkUserId,
91
+ provider: PROVIDER,
92
+ providerUserId: userInfo.providerUserId,
93
+ providerEmail: userInfo.email || null
94
+ });
95
+ logger.info({ userId: stateData.linkUserId, providerUserId: userInfo.providerUserId }, `Linked ${PROVIDER} account`);
96
+ } else if (identity) {
97
+ await authService.updateIdentityLogin(PROVIDER, userInfo.providerUserId);
98
+ nexusUser = await authService.findUserById(identity.user_id);
99
+ if (!nexusUser) {
100
+ throw new errors.NotFoundError("Linked user not found");
101
+ }
102
+ logger.debug({ userId: identity.user_id }, `Existing ${PROVIDER} user logged in`);
103
+ } else {
104
+ if (userInfo.email) {
105
+ nexusUser = await authService.findUserByEmail(userInfo.email);
106
+ }
107
+ if (nexusUser) {
108
+ await authService.linkIdentity({
109
+ userId: nexusUser.id,
110
+ provider: PROVIDER,
111
+ providerUserId: userInfo.providerUserId,
112
+ providerEmail: userInfo.email || null
113
+ });
114
+ logger.info({ userId: nexusUser.id, providerUserId: userInfo.providerUserId }, `Linked ${PROVIDER} to existing user by email`);
115
+ } else {
116
+ if (!userInfo.email) {
117
+ throw new errors.ValidationError("Email is required for registration");
118
+ }
119
+ nexusUser = await authService.createUser({
120
+ email: userInfo.email,
121
+ name: userInfo.name || void 0,
122
+ role: config.default_role || void 0
123
+ });
124
+ await authService.linkIdentity({
125
+ userId: nexusUser.id,
126
+ provider: PROVIDER,
127
+ providerUserId: userInfo.providerUserId,
128
+ providerEmail: userInfo.email
129
+ });
130
+ logger.info({ userId: nexusUser.id, email: userInfo.email }, `Created new user via ${PROVIDER}`);
131
+ }
132
+ }
133
+ await clearState(state);
134
+ const sessionTokens = await authService.createTokens(nexusUser);
135
+ return {
136
+ ...sessionTokens,
137
+ user: {
138
+ id: nexusUser.id,
139
+ email: nexusUser.email,
140
+ name: nexusUser.name
141
+ },
142
+ returnUrl: stateData.returnUrl
143
+ };
144
+ }
145
+ return {
146
+ getAuthorizationUrl,
147
+ handleCallback,
148
+ verifyState,
149
+ clearState
150
+ };
151
+ }
152
+
153
+ // src/shared/create-auth-controller.ts
154
+ var DEFAULT_FRIENDLY_ERRORS = {
155
+ "Auto-creation of users is disabled": "Your account does not exist. Contact an administrator to get access.",
156
+ "Auto-registration is disabled. Please contact administrator.": "Auto-registration is disabled. Contact an administrator to get access."
157
+ };
158
+ function createAuthPluginActions(options, getAuthService) {
159
+ const { provider, moduleName, caslSubject, friendlyErrors } = options;
160
+ const allFriendlyErrors = { ...DEFAULT_FRIENDLY_ERRORS, ...friendlyErrors };
161
+ const authorizeAction = {
162
+ label: { en: "Authorize", es: "Autorizar" },
163
+ icon: "mdi:login",
164
+ key: "authorize",
165
+ scope: "module",
166
+ hidden: true,
167
+ method: "GET",
168
+ skipAuth: true,
169
+ input: {},
170
+ handler: async (_ctx, _input, req, res) => {
171
+ const callbackUrl = buildCallbackUrl(moduleName, req);
172
+ const returnUrl = req?.query?.["redirect_uri"] || req?.headers?.["referer"] || req?.headers?.["origin"];
173
+ const authService = getAuthService();
174
+ const { url } = await authService.getAuthorizationUrl(callbackUrl, void 0, returnUrl);
175
+ res?.redirect(302, url);
176
+ }
177
+ };
178
+ const callbackAction = {
179
+ label: { en: "Callback", es: "Callback" },
180
+ icon: "mdi:arrow-left",
181
+ key: "callback",
182
+ scope: "module",
183
+ hidden: true,
184
+ method: "GET",
185
+ skipAuth: true,
186
+ input: {},
187
+ handler: async (ctx, _input, req, res) => {
188
+ const code = req?.query?.["code"];
189
+ const state = req?.query?.["state"];
190
+ const error = req?.query?.["error"];
191
+ const authService = getAuthService();
192
+ let returnUrl;
193
+ if (state) {
194
+ const stateData = await authService.verifyState(state);
195
+ returnUrl = stateData?.returnUrl;
196
+ }
197
+ function redirectError(message) {
198
+ if (returnUrl && res) {
199
+ const params = new URLSearchParams({ error: message });
200
+ res.redirect(302, `${returnUrl}#${params.toString()}`);
201
+ return;
202
+ }
203
+ throw new ctx.core.errors.ForbiddenError(message);
204
+ }
205
+ if (error) {
206
+ const errorDescription = req?.query?.["error_description"];
207
+ redirectError(errorDescription || error);
208
+ return;
209
+ }
210
+ if (!code || !state) {
211
+ throw new ctx.core.errors.ValidationError("code and state are required");
212
+ }
213
+ try {
214
+ const result = await authService.handleCallback(code, state);
215
+ if (result.returnUrl && res) {
216
+ const params = new URLSearchParams({
217
+ accessToken: result.accessToken,
218
+ refreshToken: result.refreshToken
219
+ });
220
+ res.redirect(302, `${result.returnUrl}#${params.toString()}`);
221
+ return;
222
+ }
223
+ return result;
224
+ } catch (err) {
225
+ const raw = err instanceof Error ? err.message : "Authentication failed";
226
+ redirectError(allFriendlyErrors[raw] || raw);
227
+ }
228
+ }
229
+ };
230
+ const linkAction = {
231
+ label: { en: "Link Account", es: "Vincular Cuenta" },
232
+ icon: "mdi:link-plus",
233
+ key: "link",
234
+ scope: "module",
235
+ method: "GET",
236
+ input: {},
237
+ handler: async (_ctx, _input, req, res) => {
238
+ const authReq = req;
239
+ if (!authReq?.user?.id) {
240
+ throw new _ctx.core.errors.UnauthorizedError("Authentication required");
241
+ }
242
+ const callbackUrl = buildCallbackUrl(moduleName, req);
243
+ const returnUrl = req?.query?.["redirect_uri"] || req?.headers?.["referer"];
244
+ const authService = getAuthService();
245
+ const { url } = await authService.getAuthorizationUrl(callbackUrl, authReq.user.id, returnUrl);
246
+ res?.redirect(302, url);
247
+ },
248
+ casl: {
249
+ subject: caslSubject,
250
+ permissions: {
251
+ "*": { actions: ["update"] }
252
+ }
253
+ }
254
+ };
255
+ const statusAction = {
256
+ label: { en: "Status", es: "Estado" },
257
+ icon: "mdi:information",
258
+ key: "status",
259
+ scope: "module",
260
+ hidden: true,
261
+ method: "GET",
262
+ input: {},
263
+ handler: async (ctx, _input, req) => {
264
+ const authReq = req;
265
+ if (!authReq?.user?.id) {
266
+ throw new ctx.core.errors.UnauthorizedError("Authentication required");
267
+ }
268
+ const coreAuth = ctx.services.get("auth");
269
+ const identities = await coreAuth.findIdentitiesByUser(authReq.user.id, provider);
270
+ return {
271
+ linked: identities.length > 0,
272
+ linkedAt: identities[0]?.linked_at ?? null
273
+ };
274
+ },
275
+ casl: {
276
+ subject: caslSubject,
277
+ permissions: {
278
+ "*": { actions: ["read"] }
279
+ }
280
+ }
281
+ };
282
+ return [authorizeAction, callbackAction, linkAction, statusAction];
283
+ }
284
+
285
+ // src/shared/create-config-entity.ts
286
+ import {
287
+ useIdField,
288
+ useTextField,
289
+ useTextUniqueField,
290
+ useSwitchField,
291
+ usePasswordField
292
+ } from "@gzl10/nexus-sdk/fields";
293
+ var authProvidersConfigEntity = {
294
+ type: "config",
295
+ key: "auth_providers_config",
296
+ label: { en: "Auth Providers", es: "Proveedores de Auth" },
297
+ routePrefix: "/auth-providers-config",
298
+ scopeField: "provider",
299
+ timestamps: true,
300
+ audit: true,
301
+ get defaults() {
302
+ return {
303
+ enabled: false,
304
+ client_id: "",
305
+ client_secret: "",
306
+ scopes: "",
307
+ allowed_domains: null,
308
+ default_role: "USER",
309
+ extra_config: null
310
+ };
311
+ },
312
+ fields: {
313
+ id: useIdField(),
314
+ provider: {
315
+ ...useTextUniqueField({
316
+ label: { en: "Provider", es: "Proveedor" },
317
+ size: 50
318
+ }),
319
+ disabled: true,
320
+ hint: { en: "Provider identifier (e.g., google, github, pocketid)", es: "Identificador del proveedor" }
321
+ },
322
+ enabled: useSwitchField({
323
+ label: { en: "Enabled", es: "Habilitado" }
324
+ }),
325
+ client_id: useTextField({
326
+ label: { en: "Client ID", es: "ID de Cliente" }
327
+ }),
328
+ client_secret: usePasswordField({
329
+ label: { en: "Client Secret", es: "Secret de Cliente" }
330
+ }),
331
+ scopes: useTextField({
332
+ label: { en: "Scopes", es: "Scopes" },
333
+ hint: { en: "Space-separated scopes", es: "Scopes separados por espacios" },
334
+ nullable: false
335
+ }),
336
+ allowed_domains: useTextField({
337
+ label: { en: "Allowed Domains", es: "Dominios Permitidos" },
338
+ hint: { en: "JSON array of allowed email domains (empty = all)", es: "Array JSON de dominios permitidos (vac\xEDo = todos)" }
339
+ }),
340
+ default_role: useTextField({
341
+ label: { en: "Default Role", es: "Rol por Defecto" },
342
+ hint: { en: "Role assigned to new users", es: "Rol asignado a nuevos usuarios" },
343
+ size: 50
344
+ }),
345
+ extra_config: {
346
+ label: { en: "Extra Config", es: "Config Extra" },
347
+ input: "json",
348
+ db: { type: "json", nullable: true },
349
+ hint: { en: "Provider-specific settings (issuer_url, hosted_domain, etc.)", es: "Configuracion especifica del proveedor" }
350
+ }
351
+ },
352
+ casl: {
353
+ subject: "AuthProvidersConfig",
354
+ permissions: {
355
+ ADMIN: { actions: ["read", "update"] }
356
+ }
357
+ }
358
+ };
359
+
360
+ // src/shared/create-config-module.ts
361
+ var CONFIG_TABLE = "auth_providers_config";
362
+ function createProviderConfigService(ctx, provider) {
363
+ const { knex } = ctx.db;
364
+ async function getConfig() {
365
+ const result = await knex(CONFIG_TABLE).where("provider", provider).select("*").first();
366
+ return result || null;
367
+ }
368
+ async function isEnabled() {
369
+ const config = await getConfig();
370
+ return config?.enabled ?? false;
371
+ }
372
+ return { getConfig, isEnabled };
373
+ }
374
+ function createAuthConfigModule(options) {
375
+ const {
376
+ name,
377
+ label,
378
+ provider,
379
+ envPrefix,
380
+ defaultScopes,
381
+ defaultRole = "USER",
382
+ extraConfig
383
+ } = options;
384
+ return {
385
+ name,
386
+ type: "plugin",
387
+ category: "integrations",
388
+ label,
389
+ icon: "mdi:cog",
390
+ // Entity registered by core auth module — plugins only seed + register service
391
+ seed: async (ctx) => {
392
+ const { knex } = ctx.db;
393
+ const clientId = process.env[`${envPrefix}_CLIENT_ID`];
394
+ const clientSecret = process.env[`${envPrefix}_CLIENT_SECRET`];
395
+ if (!clientId || !clientSecret) return;
396
+ const now = ctx.db.nowTimestamp(knex);
397
+ const existing = await knex(CONFIG_TABLE).where("provider", provider).first();
398
+ if (existing) {
399
+ await knex(CONFIG_TABLE).where("id", existing.id).update({
400
+ client_id: clientId,
401
+ client_secret: clientSecret,
402
+ updated_at: now
403
+ });
404
+ ctx.core.logger.info(`${provider} auth config updated from environment variables`);
405
+ } else {
406
+ await knex(CONFIG_TABLE).insert({
407
+ id: ctx.core.generateId(),
408
+ provider,
409
+ enabled: true,
410
+ client_id: clientId,
411
+ client_secret: clientSecret,
412
+ scopes: defaultScopes,
413
+ allowed_domains: null,
414
+ default_role: defaultRole,
415
+ extra_config: extraConfig ? JSON.stringify(extraConfig) : null,
416
+ created_at: now,
417
+ updated_at: now
418
+ });
419
+ ctx.core.logger.info(`${provider} auth config seeded from environment variables`);
420
+ }
421
+ },
422
+ init: (ctx) => {
423
+ const service = createProviderConfigService(ctx, provider);
424
+ ctx.services.register(`${provider}.config`, service);
425
+ ctx.core.logger.info(`${provider} auth config module initialized`);
426
+ }
427
+ };
428
+ }
429
+
430
+ // src/shared/create-auth-module.ts
431
+ function createAuthModule(options) {
432
+ const {
433
+ name,
434
+ label,
435
+ provider,
436
+ caslSubject,
437
+ providerInfo,
438
+ createAdapter,
439
+ friendlyErrors
440
+ } = options;
441
+ let _authService = null;
442
+ function getAuthService() {
443
+ if (!_authService) {
444
+ throw new Error(`${provider} AuthService not initialized`);
445
+ }
446
+ return _authService;
447
+ }
448
+ const actions = createAuthPluginActions(
449
+ { provider, moduleName: name, caslSubject, friendlyErrors },
450
+ getAuthService
451
+ );
452
+ return {
453
+ name,
454
+ type: "auth-plugin",
455
+ category: "security",
456
+ label,
457
+ icon: "mdi:shield-key",
458
+ actions,
459
+ init: (ctx) => {
460
+ const adapter = createAdapter(ctx);
461
+ const service = createAuthPluginService({ adapter, ctx });
462
+ _authService = service;
463
+ ctx.services.register(`${provider}.auth`, service);
464
+ const authProviderService = {
465
+ async getInfo() {
466
+ const configService = ctx.services.get(`${provider}.config`);
467
+ const enabled = await configService.isEnabled();
468
+ if (!enabled) return null;
469
+ return {
470
+ ...providerInfo,
471
+ authorizeEndpoint: `/api/v1/${name}/authorize`
472
+ };
473
+ }
474
+ };
475
+ ctx.services.register(`${provider}.provider`, authProviderService);
476
+ ctx.core.logger.info(`${provider} auth module initialized`);
477
+ }
478
+ };
479
+ }
480
+
481
+ export {
482
+ buildCallbackUrl,
483
+ createAuthPluginService,
484
+ createAuthPluginActions,
485
+ authProvidersConfigEntity,
486
+ createAuthConfigModule,
487
+ createAuthModule
488
+ };
489
+ //# sourceMappingURL=chunk-TPBCCFGG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/shared/create-auth-service.ts","../src/shared/create-auth-controller.ts","../src/shared/create-config-entity.ts","../src/shared/create-config-module.ts","../src/shared/create-auth-module.ts"],"sourcesContent":["/**\n * Auth Plugin Service Factory\n *\n * Creates a complete auth service from a provider adapter.\n * Handles all shared logic: state management, identity resolution,\n * user creation, session tokens, and callback URL construction.\n *\n * This eliminates ~150 lines of duplicated code per auth plugin.\n */\nimport { randomUUID } from 'node:crypto'\nimport type { ModuleContext } from '@gzl10/nexus-sdk'\nimport type { Request } from 'express'\nimport { createStateManager, checkAllowedDomain, type StateManager } from '@gzl10/nexus-sdk'\nimport type { AuthPluginAdapter, AuthPluginBaseConfig } from './adapter.js'\nimport type {\n AuthPluginState,\n AuthPluginSessionResult,\n CoreAuthService\n} from './types.js'\n\n// ============================================================================\n// SERVICE INTERFACE\n// ============================================================================\n\n/**\n * Auth plugin service returned by `createAuthPluginService()`.\n * All auth plugins expose this same interface.\n */\nexport interface AuthPluginService {\n /** Generate authorization URL and redirect state */\n getAuthorizationUrl(redirectUri: string, linkUserId?: string, returnUrl?: string): Promise<{ url: string; state: string }>\n /** Handle OAuth/OIDC callback after provider redirect */\n handleCallback(code: string, state: string): Promise<AuthPluginSessionResult>\n /** Verify state is valid (non-destructive peek) */\n verifyState(state: string): Promise<AuthPluginState | null>\n /** Clear state after use */\n clearState(state: string): Promise<void>\n}\n\n// ============================================================================\n// CALLBACK URL BUILDER\n// ============================================================================\n\n/**\n * Build the backend's callback URL from the request.\n * Supports BACKEND_URL env var override (consistent across all plugins).\n */\nexport function buildCallbackUrl(moduleName: string, req?: Request): string {\n const backendUrl = process.env['BACKEND_URL']\n if (backendUrl) {\n return `${backendUrl.replace(/\\/$/, '')}/api/v1/${moduleName}/callback`\n }\n\n let proto = (req?.headers?.['x-forwarded-proto'] as string) || req?.protocol || 'http'\n const host = (req?.headers?.['x-forwarded-host'] as string) || (req?.headers?.['host'] as string) || 'localhost'\n\n // Infer HTTPS for non-localhost hosts behind reverse proxy\n if (proto === 'http' && host !== 'localhost' && !host.startsWith('localhost:') && !host.startsWith('127.0.0.1')) {\n proto = 'https'\n }\n\n return `${proto}://${host}/api/v1/${moduleName}/callback`\n}\n\n// ============================================================================\n// SERVICE FACTORY\n// ============================================================================\n\n/**\n * Create an auth plugin service from an adapter.\n *\n * @example\n * ```typescript\n * const service = createAuthPluginService({\n * adapter: createGoogleAdapter(ctx),\n * ctx\n * })\n * ```\n */\nexport function createAuthPluginService<TConfig extends AuthPluginBaseConfig>(options: {\n adapter: AuthPluginAdapter<TConfig>\n ctx: ModuleContext\n}): AuthPluginService {\n const { adapter, ctx } = options\n const { logger, errors } = ctx.core\n const PROVIDER = adapter.provider\n\n // State manager (10 minutes TTL)\n let stateManager: StateManager<AuthPluginState> | null = null\n function getStateManager(): StateManager<AuthPluginState> {\n if (!stateManager) {\n stateManager = createStateManager<AuthPluginState>()\n }\n return stateManager\n }\n\n /**\n * Generate authorization URL\n */\n async function getAuthorizationUrl(\n redirectUri: string,\n linkUserId?: string,\n returnUrl?: string\n ): Promise<{ url: string; state: string }> {\n const config = await adapter.getValidConfig()\n\n const state = randomUUID()\n const nonce = randomUUID()\n\n const stateData: AuthPluginState = {\n state,\n nonce,\n redirectUri,\n createdAt: Date.now(),\n linkUserId,\n returnUrl\n }\n getStateManager().store(state, stateData)\n\n const scopes = config.scopes?.split(' ').filter(Boolean) || []\n const url = await adapter.buildAuthorizationUrl(config, {\n redirectUri,\n state,\n nonce,\n scopes\n })\n\n logger.debug({ state, redirectUri, linkUserId }, `Generated ${PROVIDER} authorization URL`)\n return { url, state }\n }\n\n /**\n * Verify state is valid (non-destructive)\n */\n async function verifyState(state: string): Promise<AuthPluginState | null> {\n return getStateManager().verify(state)\n }\n\n /**\n * Clear state after use\n */\n async function clearState(state: string): Promise<void> {\n getStateManager().clear(state)\n }\n\n /**\n * Handle callback from provider.\n * This is the core shared logic (~70 lines) that was duplicated across all plugins.\n */\n async function handleCallback(code: string, state: string): Promise<AuthPluginSessionResult> {\n // 1. Verify state\n const stateData = await verifyState(state)\n if (!stateData) {\n throw new errors.ValidationError('Invalid or expired state')\n }\n\n // 2. Get validated config\n const config = await adapter.getValidConfig()\n\n // 3. Exchange code and get normalized user info (provider-specific)\n const userInfo = await adapter.exchangeCodeAndGetUserInfo(\n config,\n code,\n stateData.redirectUri,\n stateData.nonce || ''\n )\n\n logger.debug(\n { providerUserId: userInfo.providerUserId, email: userInfo.email },\n `Got user info from ${PROVIDER}`\n )\n\n // 4. Check allowed domains\n if (userInfo.email) {\n const domainResult = checkAllowedDomain(userInfo.email, config.allowed_domains || null)\n if (!domainResult.allowed) {\n throw new errors.ForbiddenError(`Email domain '${domainResult.domain}' is not allowed`)\n }\n }\n\n // 5. Identity resolution (shared across all plugins)\n const authService = ctx.services.get<CoreAuthService>('auth')\n const identity = await authService.findIdentity(PROVIDER, userInfo.providerUserId)\n let nexusUser\n\n if (stateData.linkUserId) {\n // Case A: Linking existing account\n if (identity) {\n throw new errors.ValidationError(`This ${PROVIDER} account is already linked to another user`)\n }\n\n nexusUser = await authService.findUserById(stateData.linkUserId)\n if (!nexusUser) {\n throw new errors.NotFoundError('User not found')\n }\n\n await authService.linkIdentity({\n userId: stateData.linkUserId,\n provider: PROVIDER,\n providerUserId: userInfo.providerUserId,\n providerEmail: userInfo.email || null\n })\n\n logger.info({ userId: stateData.linkUserId, providerUserId: userInfo.providerUserId }, `Linked ${PROVIDER} account`)\n } else if (identity) {\n // Case B: Existing identity - update last login\n await authService.updateIdentityLogin(PROVIDER, userInfo.providerUserId)\n\n nexusUser = await authService.findUserById(identity.user_id)\n if (!nexusUser) {\n throw new errors.NotFoundError('Linked user not found')\n }\n\n logger.debug({ userId: identity.user_id }, `Existing ${PROVIDER} user logged in`)\n } else {\n // Case C: New user - auto-creation controlled by nexus-backend (AUTH_DISABLE_AUTO_CREATE)\n\n // Check if user with this email already exists\n if (userInfo.email) {\n nexusUser = await authService.findUserByEmail(userInfo.email)\n }\n\n if (nexusUser) {\n // Link existing user by email match\n await authService.linkIdentity({\n userId: nexusUser.id,\n provider: PROVIDER,\n providerUserId: userInfo.providerUserId,\n providerEmail: userInfo.email || null\n })\n logger.info({ userId: nexusUser.id, providerUserId: userInfo.providerUserId }, `Linked ${PROVIDER} to existing user by email`)\n } else {\n // Create new user\n if (!userInfo.email) {\n throw new errors.ValidationError('Email is required for registration')\n }\n\n nexusUser = await authService.createUser({\n email: userInfo.email,\n name: userInfo.name || undefined,\n role: config.default_role || undefined\n })\n\n await authService.linkIdentity({\n userId: nexusUser.id,\n provider: PROVIDER,\n providerUserId: userInfo.providerUserId,\n providerEmail: userInfo.email\n })\n\n logger.info({ userId: nexusUser.id, email: userInfo.email }, `Created new user via ${PROVIDER}`)\n }\n }\n\n // 6. Clear state and create session\n await clearState(state)\n const sessionTokens = await authService.createTokens(nexusUser)\n\n return {\n ...sessionTokens,\n user: {\n id: nexusUser.id,\n email: nexusUser.email,\n name: nexusUser.name\n },\n returnUrl: stateData.returnUrl\n }\n }\n\n return {\n getAuthorizationUrl,\n handleCallback,\n verifyState,\n clearState\n }\n}\n","/**\n * Auth Plugin Controller Factory\n *\n * Generates the 4 standard action definitions that every auth plugin needs:\n * - authorize: Start OAuth/OIDC flow (GET, skipAuth)\n * - callback: Handle provider callback (GET, skipAuth)\n * - link: Link existing account (GET, requires auth)\n * - status: Check if provider is linked (GET, requires auth)\n */\nimport type { ActionDefinition, ModuleContext, AuthRequest, LocalizedString } from '@gzl10/nexus-sdk'\nimport type { Request, Response } from 'express'\nimport type { AuthPluginService } from './create-auth-service.js'\nimport { buildCallbackUrl } from './create-auth-service.js'\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport interface AuthControllerOptions {\n /** Provider identifier (e.g., 'google', 'github') */\n provider: string\n /** Module name for URL paths (e.g., 'google_auth') */\n moduleName: string\n /** CASL subject for permissions (e.g., 'GoogleAuth') */\n caslSubject: string\n /** Additional friendly error messages for the callback handler */\n friendlyErrors?: Record<string, string>\n}\n\n// ============================================================================\n// DEFAULT FRIENDLY ERRORS\n// ============================================================================\n\nconst DEFAULT_FRIENDLY_ERRORS: Record<string, string> = {\n 'Auto-creation of users is disabled': 'Your account does not exist. Contact an administrator to get access.',\n 'Auto-registration is disabled. Please contact administrator.': 'Auto-registration is disabled. Contact an administrator to get access.',\n}\n\n// ============================================================================\n// FACTORY\n// ============================================================================\n\n/**\n * Create the 4 standard auth plugin actions.\n *\n * @param options - Controller configuration\n * @param getAuthService - Getter for the auth plugin service (deferred to avoid init order issues)\n */\nexport function createAuthPluginActions(\n options: AuthControllerOptions,\n getAuthService: () => AuthPluginService\n): ActionDefinition[] {\n const { provider, moduleName, caslSubject, friendlyErrors } = options\n\n const allFriendlyErrors = { ...DEFAULT_FRIENDLY_ERRORS, ...friendlyErrors }\n\n // ---- authorize ----\n const authorizeAction: ActionDefinition = {\n\n label: { en: 'Authorize', es: 'Autorizar' } as LocalizedString,\n icon: 'mdi:login',\n key: 'authorize',\n scope: 'module',\n hidden: true,\n method: 'GET',\n skipAuth: true,\n input: {},\n\n handler: async (_ctx: ModuleContext, _input: unknown, req?: Request, res?: Response) => {\n const callbackUrl = buildCallbackUrl(moduleName, req)\n const returnUrl = (req?.query?.['redirect_uri'] as string)\n || (req?.headers?.['referer'] as string)\n || (req?.headers?.['origin'] as string)\n\n const authService = getAuthService()\n const { url } = await authService.getAuthorizationUrl(callbackUrl, undefined, returnUrl)\n\n res?.redirect(302, url)\n }\n }\n\n // ---- callback ----\n const callbackAction: ActionDefinition = {\n\n label: { en: 'Callback', es: 'Callback' } as LocalizedString,\n icon: 'mdi:arrow-left',\n key: 'callback',\n scope: 'module',\n hidden: true,\n method: 'GET',\n skipAuth: true,\n input: {},\n\n handler: async (ctx: ModuleContext, _input: unknown, req?: Request, res?: Response) => {\n const code = req?.query?.['code'] as string\n const state = req?.query?.['state'] as string\n const error = req?.query?.['error'] as string\n\n const authService = getAuthService()\n\n // Peek at state for error redirects (non-destructive)\n let returnUrl: string | undefined\n if (state) {\n const stateData = await authService.verifyState(state)\n returnUrl = stateData?.returnUrl\n }\n\n /** Redirect to frontend with error, or throw for API consumers */\n function redirectError(message: string): void {\n if (returnUrl && res) {\n const params = new URLSearchParams({ error: message })\n res.redirect(302, `${returnUrl}#${params.toString()}`)\n return\n }\n throw new ctx.core.errors.ForbiddenError(message)\n }\n\n if (error) {\n const errorDescription = req?.query?.['error_description'] as string\n redirectError(errorDescription || error)\n return\n }\n\n if (!code || !state) {\n throw new ctx.core.errors.ValidationError('code and state are required')\n }\n\n try {\n const result = await authService.handleCallback(code, state)\n\n // Server-side flow: redirect with tokens in URL fragment\n if (result.returnUrl && res) {\n const params = new URLSearchParams({\n accessToken: result.accessToken,\n refreshToken: result.refreshToken\n })\n res.redirect(302, `${result.returnUrl}#${params.toString()}`)\n return\n }\n\n // API flow: return JSON\n return result\n } catch (err) {\n const raw = err instanceof Error ? err.message : 'Authentication failed'\n redirectError(allFriendlyErrors[raw] || raw)\n }\n }\n }\n\n // ---- link ----\n const linkAction: ActionDefinition = {\n\n label: { en: 'Link Account', es: 'Vincular Cuenta' } as LocalizedString,\n icon: 'mdi:link-plus',\n key: 'link',\n scope: 'module',\n method: 'GET',\n input: {},\n\n handler: async (_ctx: ModuleContext, _input: unknown, req?: Request, res?: Response) => {\n const authReq = req as AuthRequest\n\n if (!authReq?.user?.id) {\n throw new _ctx.core.errors.UnauthorizedError('Authentication required')\n }\n\n const callbackUrl = buildCallbackUrl(moduleName, req)\n const returnUrl = (req?.query?.['redirect_uri'] as string)\n || (req?.headers?.['referer'] as string)\n\n const authService = getAuthService()\n const { url } = await authService.getAuthorizationUrl(callbackUrl, authReq.user.id, returnUrl)\n\n res?.redirect(302, url)\n },\n\n casl: {\n subject: caslSubject,\n permissions: {\n '*': { actions: ['update'] }\n }\n }\n }\n\n // ---- status ----\n const statusAction: ActionDefinition = {\n\n label: { en: 'Status', es: 'Estado' } as LocalizedString,\n icon: 'mdi:information',\n key: 'status',\n scope: 'module',\n hidden: true,\n method: 'GET',\n input: {},\n\n handler: async (ctx: ModuleContext, _input: unknown, req?: Request) => {\n const authReq = req as AuthRequest\n\n if (!authReq?.user?.id) {\n throw new ctx.core.errors.UnauthorizedError('Authentication required')\n }\n\n const coreAuth = ctx.services.get<{\n findIdentitiesByUser(userId: string, provider?: string): Promise<Array<{ linked_at: string }>>\n }>('auth')\n\n const identities = await coreAuth.findIdentitiesByUser(authReq.user.id, provider)\n\n return {\n linked: identities.length > 0,\n linkedAt: identities[0]?.linked_at ?? null\n }\n },\n\n casl: {\n subject: caslSubject,\n permissions: {\n '*': { actions: ['read'] }\n }\n }\n }\n\n return [authorizeAction, callbackAction, linkAction, statusAction]\n}\n","/**\n * Unified Auth Providers Config Entity\n *\n * Single table `auth_providers_config` with one row per provider.\n * Replaces separate tables (google_auth_config, pocketid_config, github_auth_config).\n *\n * Provider-specific fields (hosted_domain, issuer_url) go in `extra_config` JSON.\n */\nimport type { SingleEntityDefinition } from '@gzl10/nexus-sdk'\nimport {\n useIdField,\n useTextField,\n useTextUniqueField,\n useSwitchField,\n usePasswordField\n} from '@gzl10/nexus-sdk/fields'\n\n/**\n * Unified config entity for all auth providers.\n *\n * Each provider gets one row, identified by the `provider` column.\n * The `scopeField: 'provider'` makes each provider a separate \"scope\",\n * so the config UI shows each provider's settings independently.\n *\n * Usage: Include this entity in the core auth module's definitions.\n *\n * @example Backend auth module\n * ```typescript\n * import { authProvidersConfigEntity } from '@gzl10/nexus-sdk'\n *\n * export const authModule: ModuleManifest = {\n * definitions: [..., authProvidersConfigEntity],\n * }\n * ```\n */\nexport const authProvidersConfigEntity: SingleEntityDefinition = {\n type: 'config',\n key: 'auth_providers_config',\n label: { en: 'Auth Providers', es: 'Proveedores de Auth' },\n routePrefix: '/auth-providers-config',\n scopeField: 'provider',\n timestamps: true,\n audit: true,\n\n get defaults() {\n return {\n enabled: false,\n client_id: '',\n client_secret: '',\n scopes: '',\n allowed_domains: null,\n default_role: 'USER',\n extra_config: null\n }\n },\n\n fields: {\n id: useIdField(),\n provider: {\n ...useTextUniqueField({\n label: { en: 'Provider', es: 'Proveedor' },\n size: 50\n }),\n disabled: true,\n hint: { en: 'Provider identifier (e.g., google, github, pocketid)', es: 'Identificador del proveedor' }\n },\n enabled: useSwitchField({\n label: { en: 'Enabled', es: 'Habilitado' }\n }),\n client_id: useTextField({\n label: { en: 'Client ID', es: 'ID de Cliente' }\n }),\n client_secret: usePasswordField({\n label: { en: 'Client Secret', es: 'Secret de Cliente' }\n }),\n scopes: useTextField({\n label: { en: 'Scopes', es: 'Scopes' },\n hint: { en: 'Space-separated scopes', es: 'Scopes separados por espacios' },\n nullable: false\n }),\n allowed_domains: useTextField({\n label: { en: 'Allowed Domains', es: 'Dominios Permitidos' },\n hint: { en: 'JSON array of allowed email domains (empty = all)', es: 'Array JSON de dominios permitidos (vacío = todos)' }\n }),\n default_role: useTextField({\n label: { en: 'Default Role', es: 'Rol por Defecto' },\n hint: { en: 'Role assigned to new users', es: 'Rol asignado a nuevos usuarios' },\n size: 50\n }),\n extra_config: {\n label: { en: 'Extra Config', es: 'Config Extra' },\n input: 'json' as const,\n db: { type: 'json' as const, nullable: true },\n hint: { en: 'Provider-specific settings (issuer_url, hosted_domain, etc.)', es: 'Configuracion especifica del proveedor' }\n }\n },\n\n casl: {\n subject: 'AuthProvidersConfig',\n permissions: {\n ADMIN: { actions: ['read', 'update'] }\n }\n }\n}\n","/**\n * Auth Config Module Factory\n *\n * Creates a module that seeds the provider's row in the unified\n * `auth_providers_config` table and registers a config service.\n *\n * The entity itself is registered by the core auth module.\n * This module only handles seeding + config service registration.\n */\nimport type { ModuleManifest, ModuleContext, LocalizedString } from '@gzl10/nexus-sdk'\nimport type { AuthPluginBaseConfig } from './adapter.js'\nimport type { AuthPluginConfigService } from './types.js'\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport interface AuthConfigModuleOptions {\n /** Module name (e.g., 'google_auth_config') */\n name: string\n /** Module label */\n label: LocalizedString\n /** Provider identifier (e.g., 'google', 'github', 'pocketid') */\n provider: string\n /** Environment variable prefix (e.g., 'GOOGLE' reads GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET) */\n envPrefix: string\n /** Default scopes for this provider */\n defaultScopes: string\n /** Default role for new users (default: 'USER') */\n defaultRole?: string\n /** Extra config values for the seed (e.g., { issuer_url: '...' }) */\n extraConfig?: Record<string, unknown>\n}\n\n// ============================================================================\n// CONFIG SERVICE FACTORY\n// ============================================================================\n\nconst CONFIG_TABLE = 'auth_providers_config'\n\n/**\n * Create a config service scoped to a specific provider.\n */\nfunction createProviderConfigService<TConfig extends AuthPluginBaseConfig = AuthPluginBaseConfig>(\n ctx: ModuleContext,\n provider: string\n): AuthPluginConfigService<TConfig> {\n const { knex } = ctx.db\n\n async function getConfig(): Promise<TConfig | null> {\n const result = await knex(CONFIG_TABLE)\n .where('provider', provider)\n .select('*')\n .first()\n return result || null\n }\n\n async function isEnabled(): Promise<boolean> {\n const config = await getConfig()\n return config?.enabled ?? false\n }\n\n return { getConfig, isEnabled }\n}\n\n// ============================================================================\n// MODULE FACTORY\n// ============================================================================\n\n/**\n * Create the config module for an auth plugin.\n *\n * This module:\n * 1. Seeds the provider's row from environment variables\n * 2. Registers a config service scoped to this provider\n *\n * Note: The `auth_providers_config` entity is registered by the core auth module.\n *\n * @example\n * ```typescript\n * const configModule = createAuthConfigModule({\n * name: 'google_auth_config',\n * label: { en: 'Google Auth Config' },\n * provider: 'google',\n * envPrefix: 'GOOGLE',\n * defaultScopes: 'openid profile email',\n * extraConfig: { hosted_domain: null }\n * })\n * ```\n */\nexport function createAuthConfigModule(options: AuthConfigModuleOptions): ModuleManifest {\n const {\n name,\n label,\n provider,\n envPrefix,\n defaultScopes,\n defaultRole = 'USER',\n extraConfig\n } = options\n\n return {\n name,\n type: 'plugin',\n category: 'integrations',\n label,\n icon: 'mdi:cog',\n\n // Entity registered by core auth module — plugins only seed + register service\n\n seed: async (ctx) => {\n const { knex } = ctx.db\n\n const clientId = process.env[`${envPrefix}_CLIENT_ID`]\n const clientSecret = process.env[`${envPrefix}_CLIENT_SECRET`]\n\n if (!clientId || !clientSecret) return\n\n const now = ctx.db.nowTimestamp(knex)\n const existing = await knex(CONFIG_TABLE)\n .where('provider', provider)\n .first()\n\n if (existing) {\n // Upsert: update credentials from env vars (consistent across all plugins)\n await knex(CONFIG_TABLE)\n .where('id', existing.id)\n .update({\n client_id: clientId,\n client_secret: clientSecret,\n updated_at: now\n })\n ctx.core.logger.info(`${provider} auth config updated from environment variables`)\n } else {\n await knex(CONFIG_TABLE).insert({\n id: ctx.core.generateId(),\n provider,\n enabled: true,\n client_id: clientId,\n client_secret: clientSecret,\n scopes: defaultScopes,\n allowed_domains: null,\n default_role: defaultRole,\n extra_config: extraConfig ? JSON.stringify(extraConfig) : null,\n created_at: now,\n updated_at: now\n })\n ctx.core.logger.info(`${provider} auth config seeded from environment variables`)\n }\n },\n\n init: (ctx) => {\n const service = createProviderConfigService(ctx, provider)\n ctx.services.register(`${provider}.config`, service)\n ctx.core.logger.info(`${provider} auth config module initialized`)\n }\n }\n}\n","/**\n * Auth Module Factory\n *\n * Creates the complete auth module for a plugin, including:\n * - Auth plugin service (state management, identity resolution, session creation)\n * - Auth controller (4 actions: authorize, callback, link, status)\n * - AuthProviderService registration (for dynamic login buttons in UI)\n */\nimport type { ModuleManifest, ModuleContext, AuthProviderInfo, AuthProviderService, LocalizedString } from '@gzl10/nexus-sdk'\nimport type { AuthPluginAdapter, AuthPluginBaseConfig } from './adapter.js'\nimport type { AuthPluginConfigService } from './types.js'\nimport { createAuthPluginService, type AuthPluginService } from './create-auth-service.js'\nimport { createAuthPluginActions } from './create-auth-controller.js'\n\n// ============================================================================\n// OPTIONS\n// ============================================================================\n\nexport interface AuthModuleOptions<TConfig extends AuthPluginBaseConfig = AuthPluginBaseConfig> {\n /** Module name (e.g., 'google_auth') */\n name: string\n /** Module label */\n label: LocalizedString\n /** Config module dependency name (e.g., 'google_auth_config') */\n configDependency: string\n /** Provider identifier (e.g., 'google') */\n provider: string\n /** CASL subject for permissions (e.g., 'GoogleAuth') */\n caslSubject: string\n /** Provider info for the dynamic login button (icon, color, label) */\n providerInfo: Omit<AuthProviderInfo, 'authorizeEndpoint'>\n /** Factory function to create the provider-specific adapter */\n createAdapter: (ctx: ModuleContext) => AuthPluginAdapter<TConfig>\n /** Additional friendly error messages for the callback */\n friendlyErrors?: Record<string, string>\n}\n\n// ============================================================================\n// MODULE FACTORY\n// ============================================================================\n\n/**\n * Create the auth module for a plugin.\n *\n * @example Google Auth\n * ```typescript\n * const authModule = createAuthModule({\n * name: 'google_auth',\n * label: { en: 'Google Auth' },\n * configDependency: 'google_auth_config',\n * provider: 'google',\n * caslSubject: 'GoogleAuth',\n * providerInfo: {\n * code: 'GOOGLE_AUTH',\n * provider: 'google',\n * icon: 'mdi:google',\n * label: { en: 'Sign in with Google' },\n * color: '#4285F4'\n * },\n * createAdapter: createGoogleAdapter\n * })\n * ```\n */\nexport function createAuthModule<TConfig extends AuthPluginBaseConfig = AuthPluginBaseConfig>(\n options: AuthModuleOptions<TConfig>\n): ModuleManifest {\n const {\n name,\n label,\n provider,\n caslSubject,\n providerInfo,\n createAdapter,\n friendlyErrors\n } = options\n\n // Deferred service reference (set during init)\n let _authService: AuthPluginService | null = null\n function getAuthService(): AuthPluginService {\n if (!_authService) {\n throw new Error(`${provider} AuthService not initialized`)\n }\n return _authService\n }\n\n // Create actions with deferred service getter\n const actions = createAuthPluginActions(\n { provider, moduleName: name, caslSubject, friendlyErrors },\n getAuthService\n )\n\n return {\n name,\n type: 'auth-plugin',\n category: 'security',\n label,\n icon: 'mdi:shield-key',\n\n actions,\n\n init: (ctx) => {\n // Create adapter and auth service\n const adapter = createAdapter(ctx)\n const service = createAuthPluginService({ adapter, ctx })\n _authService = service\n\n // Register auth service for other modules to use\n ctx.services.register(`${provider}.auth`, service)\n\n // Register auth provider for dynamic login buttons\n const authProviderService: AuthProviderService = {\n async getInfo(): Promise<AuthProviderInfo | null> {\n const configService = ctx.services.get<AuthPluginConfigService>(`${provider}.config`)\n const enabled = await configService.isEnabled()\n if (!enabled) return null\n\n return {\n ...providerInfo,\n authorizeEndpoint: `/api/v1/${name}/authorize`\n }\n }\n }\n ctx.services.register(`${provider}.provider`, authProviderService)\n\n ctx.core.logger.info(`${provider} auth module initialized`)\n }\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,5 @@
1
+ import { PluginManifest } from '@gzl10/nexus-sdk';
2
+
3
+ declare const authProvidersPlugin: PluginManifest;
4
+
5
+ export { authProvidersPlugin, authProvidersPlugin as default };