@objectstack/plugin-auth 4.0.4 → 4.0.5
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 +4 -1
- package/dist/index.d.mts +332 -19942
- package/dist/index.d.ts +332 -19942
- package/dist/index.js +351 -882
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +355 -862
- package/dist/index.mjs.map +1 -1
- package/package.json +35 -12
- package/.turbo/turbo-build.log +0 -78
- package/ARCHITECTURE.md +0 -176
- package/CHANGELOG.md +0 -333
- package/IMPLEMENTATION_SUMMARY.md +0 -192
- package/examples/basic-usage.ts +0 -107
- package/objectstack.config.ts +0 -24
- package/src/auth-manager.test.ts +0 -883
- package/src/auth-manager.ts +0 -419
- package/src/auth-plugin.test.ts +0 -446
- package/src/auth-plugin.ts +0 -314
- package/src/auth-schema-config.ts +0 -339
- package/src/index.ts +0 -16
- package/src/objectql-adapter.test.ts +0 -281
- package/src/objectql-adapter.ts +0 -279
- package/src/objects/auth-account.object.ts +0 -7
- package/src/objects/auth-session.object.ts +0 -7
- package/src/objects/auth-user.object.ts +0 -7
- package/src/objects/auth-verification.object.ts +0 -7
- package/src/objects/index.ts +0 -40
- package/src/objects/sys-account.object.ts +0 -111
- package/src/objects/sys-api-key.object.ts +0 -104
- package/src/objects/sys-invitation.object.ts +0 -93
- package/src/objects/sys-member.object.ts +0 -68
- package/src/objects/sys-organization.object.ts +0 -82
- package/src/objects/sys-session.object.ts +0 -84
- package/src/objects/sys-team-member.object.ts +0 -61
- package/src/objects/sys-team.object.ts +0 -69
- package/src/objects/sys-two-factor.object.ts +0 -73
- package/src/objects/sys-user-preference.object.ts +0 -82
- package/src/objects/sys-user.object.ts +0 -91
- package/src/objects/sys-verification.object.ts +0 -75
- package/tsconfig.json +0 -18
package/dist/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,15 +17,30 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
21
31
|
var index_exports = {};
|
|
22
32
|
__export(index_exports, {
|
|
23
33
|
AUTH_ACCOUNT_CONFIG: () => AUTH_ACCOUNT_CONFIG,
|
|
34
|
+
AUTH_DEVICE_CODE_SCHEMA: () => AUTH_DEVICE_CODE_SCHEMA,
|
|
24
35
|
AUTH_INVITATION_SCHEMA: () => AUTH_INVITATION_SCHEMA,
|
|
36
|
+
AUTH_JWKS_SCHEMA: () => AUTH_JWKS_SCHEMA,
|
|
25
37
|
AUTH_MEMBER_SCHEMA: () => AUTH_MEMBER_SCHEMA,
|
|
26
38
|
AUTH_MODEL_TO_PROTOCOL: () => AUTH_MODEL_TO_PROTOCOL,
|
|
39
|
+
AUTH_OAUTH_ACCESS_TOKEN_SCHEMA: () => AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,
|
|
40
|
+
AUTH_OAUTH_APPLICATION_SCHEMA: () => AUTH_OAUTH_APPLICATION_SCHEMA,
|
|
41
|
+
AUTH_OAUTH_CLIENT_SCHEMA: () => AUTH_OAUTH_CLIENT_SCHEMA,
|
|
42
|
+
AUTH_OAUTH_CONSENT_SCHEMA: () => AUTH_OAUTH_CONSENT_SCHEMA,
|
|
43
|
+
AUTH_OAUTH_REFRESH_TOKEN_SCHEMA: () => AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,
|
|
27
44
|
AUTH_ORGANIZATION_SCHEMA: () => AUTH_ORGANIZATION_SCHEMA,
|
|
28
45
|
AUTH_ORG_SESSION_FIELDS: () => AUTH_ORG_SESSION_FIELDS,
|
|
29
46
|
AUTH_SESSION_CONFIG: () => AUTH_SESSION_CONFIG,
|
|
@@ -33,24 +50,12 @@ __export(index_exports, {
|
|
|
33
50
|
AUTH_TWO_FACTOR_USER_FIELDS: () => AUTH_TWO_FACTOR_USER_FIELDS,
|
|
34
51
|
AUTH_USER_CONFIG: () => AUTH_USER_CONFIG,
|
|
35
52
|
AUTH_VERIFICATION_CONFIG: () => AUTH_VERIFICATION_CONFIG,
|
|
36
|
-
AuthAccount: () => SysAccount,
|
|
37
53
|
AuthManager: () => AuthManager,
|
|
38
54
|
AuthPlugin: () => AuthPlugin,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
SysApiKey: () => SysApiKey,
|
|
44
|
-
SysInvitation: () => SysInvitation,
|
|
45
|
-
SysMember: () => SysMember,
|
|
46
|
-
SysOrganization: () => SysOrganization,
|
|
47
|
-
SysSession: () => SysSession,
|
|
48
|
-
SysTeam: () => SysTeam,
|
|
49
|
-
SysTeamMember: () => SysTeamMember,
|
|
50
|
-
SysTwoFactor: () => SysTwoFactor,
|
|
51
|
-
SysUser: () => SysUser,
|
|
52
|
-
SysUserPreference: () => SysUserPreference,
|
|
53
|
-
SysVerification: () => SysVerification,
|
|
55
|
+
buildDeviceAuthorizationPluginSchema: () => buildDeviceAuthorizationPluginSchema,
|
|
56
|
+
buildJwtPluginSchema: () => buildJwtPluginSchema,
|
|
57
|
+
buildOauthProviderPluginSchema: () => buildOauthProviderPluginSchema,
|
|
58
|
+
buildOidcProviderPluginSchema: () => buildOidcProviderPluginSchema,
|
|
54
59
|
buildOrganizationPluginSchema: () => buildOrganizationPluginSchema,
|
|
55
60
|
buildTwoFactorPluginSchema: () => buildTwoFactorPluginSchema,
|
|
56
61
|
createObjectQLAdapter: () => createObjectQLAdapter,
|
|
@@ -59,11 +64,8 @@ __export(index_exports, {
|
|
|
59
64
|
});
|
|
60
65
|
module.exports = __toCommonJS(index_exports);
|
|
61
66
|
|
|
62
|
-
// src/auth-
|
|
63
|
-
var
|
|
64
|
-
var import_organization = require("better-auth/plugins/organization");
|
|
65
|
-
var import_two_factor = require("better-auth/plugins/two-factor");
|
|
66
|
-
var import_magic_link = require("better-auth/plugins/magic-link");
|
|
67
|
+
// src/auth-plugin.ts
|
|
68
|
+
var import_apps = require("@objectstack/platform-objects/apps");
|
|
67
69
|
|
|
68
70
|
// src/objectql-adapter.ts
|
|
69
71
|
var import_adapters = require("better-auth/adapters");
|
|
@@ -337,6 +339,81 @@ var AUTH_TWO_FACTOR_SCHEMA = {
|
|
|
337
339
|
var AUTH_TWO_FACTOR_USER_FIELDS = {
|
|
338
340
|
twoFactorEnabled: "two_factor_enabled"
|
|
339
341
|
};
|
|
342
|
+
var AUTH_OAUTH_CLIENT_SCHEMA = {
|
|
343
|
+
modelName: import_system2.SystemObjectName.OAUTH_APPLICATION,
|
|
344
|
+
// 'sys_oauth_application'
|
|
345
|
+
fields: {
|
|
346
|
+
clientId: "client_id",
|
|
347
|
+
clientSecret: "client_secret",
|
|
348
|
+
skipConsent: "skip_consent",
|
|
349
|
+
enableEndSession: "enable_end_session",
|
|
350
|
+
subjectType: "subject_type",
|
|
351
|
+
userId: "user_id",
|
|
352
|
+
createdAt: "created_at",
|
|
353
|
+
updatedAt: "updated_at",
|
|
354
|
+
redirectUris: "redirect_uris",
|
|
355
|
+
postLogoutRedirectUris: "post_logout_redirect_uris",
|
|
356
|
+
tokenEndpointAuthMethod: "token_endpoint_auth_method",
|
|
357
|
+
grantTypes: "grant_types",
|
|
358
|
+
responseTypes: "response_types",
|
|
359
|
+
requirePKCE: "require_pkce",
|
|
360
|
+
softwareId: "software_id",
|
|
361
|
+
softwareVersion: "software_version",
|
|
362
|
+
softwareStatement: "software_statement",
|
|
363
|
+
referenceId: "reference_id"
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
var AUTH_OAUTH_APPLICATION_SCHEMA = AUTH_OAUTH_CLIENT_SCHEMA;
|
|
367
|
+
var AUTH_OAUTH_ACCESS_TOKEN_SCHEMA = {
|
|
368
|
+
modelName: import_system2.SystemObjectName.OAUTH_ACCESS_TOKEN,
|
|
369
|
+
// 'sys_oauth_access_token'
|
|
370
|
+
fields: {
|
|
371
|
+
clientId: "client_id",
|
|
372
|
+
sessionId: "session_id",
|
|
373
|
+
userId: "user_id",
|
|
374
|
+
referenceId: "reference_id",
|
|
375
|
+
refreshId: "refresh_id",
|
|
376
|
+
expiresAt: "expires_at",
|
|
377
|
+
createdAt: "created_at"
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
var AUTH_OAUTH_REFRESH_TOKEN_SCHEMA = {
|
|
381
|
+
modelName: import_system2.SystemObjectName.OAUTH_REFRESH_TOKEN,
|
|
382
|
+
// 'sys_oauth_refresh_token'
|
|
383
|
+
fields: {
|
|
384
|
+
clientId: "client_id",
|
|
385
|
+
sessionId: "session_id",
|
|
386
|
+
userId: "user_id",
|
|
387
|
+
referenceId: "reference_id",
|
|
388
|
+
expiresAt: "expires_at",
|
|
389
|
+
createdAt: "created_at",
|
|
390
|
+
authTime: "auth_time"
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
var AUTH_OAUTH_CONSENT_SCHEMA = {
|
|
394
|
+
modelName: import_system2.SystemObjectName.OAUTH_CONSENT,
|
|
395
|
+
// 'sys_oauth_consent'
|
|
396
|
+
fields: {
|
|
397
|
+
clientId: "client_id",
|
|
398
|
+
userId: "user_id",
|
|
399
|
+
referenceId: "reference_id",
|
|
400
|
+
createdAt: "created_at",
|
|
401
|
+
updatedAt: "updated_at"
|
|
402
|
+
}
|
|
403
|
+
};
|
|
404
|
+
var AUTH_DEVICE_CODE_SCHEMA = {
|
|
405
|
+
modelName: import_system2.SystemObjectName.DEVICE_CODE,
|
|
406
|
+
// 'sys_device_code'
|
|
407
|
+
fields: {
|
|
408
|
+
deviceCode: "device_code",
|
|
409
|
+
userCode: "user_code",
|
|
410
|
+
userId: "user_id",
|
|
411
|
+
expiresAt: "expires_at",
|
|
412
|
+
lastPolledAt: "last_polled_at",
|
|
413
|
+
pollingInterval: "polling_interval",
|
|
414
|
+
clientId: "client_id"
|
|
415
|
+
}
|
|
416
|
+
};
|
|
340
417
|
function buildTwoFactorPluginSchema() {
|
|
341
418
|
return {
|
|
342
419
|
twoFactor: AUTH_TWO_FACTOR_SCHEMA,
|
|
@@ -357,6 +434,35 @@ function buildOrganizationPluginSchema() {
|
|
|
357
434
|
}
|
|
358
435
|
};
|
|
359
436
|
}
|
|
437
|
+
var AUTH_JWKS_SCHEMA = {
|
|
438
|
+
modelName: import_system2.SystemObjectName.JWKS,
|
|
439
|
+
// 'sys_jwks'
|
|
440
|
+
fields: {
|
|
441
|
+
publicKey: "public_key",
|
|
442
|
+
privateKey: "private_key",
|
|
443
|
+
createdAt: "created_at",
|
|
444
|
+
expiresAt: "expires_at"
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
function buildJwtPluginSchema() {
|
|
448
|
+
return {
|
|
449
|
+
jwks: AUTH_JWKS_SCHEMA
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
function buildOauthProviderPluginSchema() {
|
|
453
|
+
return {
|
|
454
|
+
oauthClient: AUTH_OAUTH_CLIENT_SCHEMA,
|
|
455
|
+
oauthAccessToken: AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,
|
|
456
|
+
oauthRefreshToken: AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,
|
|
457
|
+
oauthConsent: AUTH_OAUTH_CONSENT_SCHEMA
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
var buildOidcProviderPluginSchema = buildOauthProviderPluginSchema;
|
|
461
|
+
function buildDeviceAuthorizationPluginSchema() {
|
|
462
|
+
return {
|
|
463
|
+
deviceCode: AUTH_DEVICE_CODE_SCHEMA
|
|
464
|
+
};
|
|
465
|
+
}
|
|
360
466
|
|
|
361
467
|
// src/auth-manager.ts
|
|
362
468
|
var AuthManager = class {
|
|
@@ -370,16 +476,18 @@ var AuthManager = class {
|
|
|
370
476
|
/**
|
|
371
477
|
* Get or create the better-auth instance (lazy initialization)
|
|
372
478
|
*/
|
|
373
|
-
getOrCreateAuth() {
|
|
479
|
+
async getOrCreateAuth() {
|
|
374
480
|
if (!this.auth) {
|
|
375
|
-
this.auth = this.createAuthInstance();
|
|
481
|
+
this.auth = await this.createAuthInstance();
|
|
376
482
|
}
|
|
377
483
|
return this.auth;
|
|
378
484
|
}
|
|
379
485
|
/**
|
|
380
486
|
* Create a better-auth instance from configuration
|
|
381
487
|
*/
|
|
382
|
-
createAuthInstance() {
|
|
488
|
+
async createAuthInstance() {
|
|
489
|
+
const { betterAuth } = await import("better-auth");
|
|
490
|
+
const plugins = await this.buildPluginList();
|
|
383
491
|
const betterAuthConfig = {
|
|
384
492
|
// Base configuration
|
|
385
493
|
secret: this.config.secret || this.generateSecret(),
|
|
@@ -431,7 +539,7 @@ var AuthManager = class {
|
|
|
431
539
|
// 1 day default
|
|
432
540
|
},
|
|
433
541
|
// better-auth plugins — registered based on AuthPluginConfig flags
|
|
434
|
-
plugins
|
|
542
|
+
plugins,
|
|
435
543
|
// Trusted origins for CSRF protection (supports wildcards like "https://*.example.com")
|
|
436
544
|
// Auto-includes origins from CORS_ORIGIN env var so CORS and CSRF stay in sync.
|
|
437
545
|
...(() => {
|
|
@@ -457,7 +565,7 @@ var AuthManager = class {
|
|
|
457
565
|
}
|
|
458
566
|
} : {}
|
|
459
567
|
};
|
|
460
|
-
return
|
|
568
|
+
return betterAuth(betterAuthConfig);
|
|
461
569
|
}
|
|
462
570
|
/**
|
|
463
571
|
* Build the list of better-auth plugins based on AuthPluginConfig flags.
|
|
@@ -466,21 +574,52 @@ var AuthManager = class {
|
|
|
466
574
|
* a `schema` option containing the appropriate snake_case field mappings,
|
|
467
575
|
* so that `createAdapterFactory` transforms them automatically.
|
|
468
576
|
*/
|
|
469
|
-
buildPluginList() {
|
|
470
|
-
const pluginConfig = this.config.plugins;
|
|
577
|
+
async buildPluginList() {
|
|
578
|
+
const pluginConfig = this.config.plugins ?? {};
|
|
471
579
|
const plugins = [];
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
580
|
+
const enabled = {
|
|
581
|
+
organization: pluginConfig.organization ?? true,
|
|
582
|
+
twoFactor: pluginConfig.twoFactor ?? false,
|
|
583
|
+
passkeys: pluginConfig.passkeys ?? false,
|
|
584
|
+
magicLink: pluginConfig.magicLink ?? false,
|
|
585
|
+
oidcProvider: pluginConfig.oidcProvider ?? false,
|
|
586
|
+
deviceAuthorization: pluginConfig.deviceAuthorization ?? false
|
|
587
|
+
};
|
|
588
|
+
const { bearer } = await import("better-auth/plugins/bearer");
|
|
589
|
+
plugins.push(bearer());
|
|
590
|
+
if (enabled.organization) {
|
|
591
|
+
const { organization } = await import("better-auth/plugins/organization");
|
|
592
|
+
plugins.push(organization({
|
|
593
|
+
schema: buildOrganizationPluginSchema(),
|
|
594
|
+
// Enable the team sub-feature so the framework's `sys_team` /
|
|
595
|
+
// `sys_team_member` tables (already declared in platform-objects)
|
|
596
|
+
// are actually wired up to better-auth's CRUD endpoints
|
|
597
|
+
// (`/organization/{create,update,remove,list}-team[s]` and
|
|
598
|
+
// `/organization/{add,remove,list}-team-member[s]`). The Account
|
|
599
|
+
// portal exposes a Teams page; without this flag those endpoints
|
|
600
|
+
// 404 and the section silently breaks.
|
|
601
|
+
teams: { enabled: true },
|
|
602
|
+
// No mailer is wired in framework yet — log the accept URL so
|
|
603
|
+
// operators / UI can fall back to copy-paste flows. Replace this
|
|
604
|
+
// with a real mail integration when available.
|
|
605
|
+
sendInvitationEmail: async ({ email, invitation, organization: org, inviter }) => {
|
|
606
|
+
const baseUrl = (this.config.baseUrl ?? "").replace(/\/$/, "");
|
|
607
|
+
const acceptUrl = `${baseUrl}/accept-invitation/${invitation.id}`;
|
|
608
|
+
console.warn(
|
|
609
|
+
`[AuthManager] Invitation email not configured. To: ${email} (org: ${org?.name ?? invitation.organizationId}, role: ${invitation.role}, inviter: ${inviter?.user?.email ?? "unknown"}) URL: ${acceptUrl}`
|
|
610
|
+
);
|
|
611
|
+
}
|
|
475
612
|
}));
|
|
476
613
|
}
|
|
477
|
-
if (
|
|
478
|
-
|
|
614
|
+
if (enabled.twoFactor) {
|
|
615
|
+
const { twoFactor } = await import("better-auth/plugins/two-factor");
|
|
616
|
+
plugins.push(twoFactor({
|
|
479
617
|
schema: buildTwoFactorPluginSchema()
|
|
480
618
|
}));
|
|
481
619
|
}
|
|
482
|
-
if (
|
|
483
|
-
|
|
620
|
+
if (enabled.magicLink) {
|
|
621
|
+
const { magicLink } = await import("better-auth/plugins/magic-link");
|
|
622
|
+
plugins.push(magicLink({
|
|
484
623
|
sendMagicLink: async ({ email, url }) => {
|
|
485
624
|
console.warn(
|
|
486
625
|
`[AuthManager] Magic-link requested for ${email} but no sendMagicLink handler configured. URL: ${url}`
|
|
@@ -488,6 +627,43 @@ var AuthManager = class {
|
|
|
488
627
|
}
|
|
489
628
|
}));
|
|
490
629
|
}
|
|
630
|
+
if (this.config.oidcProviders?.length) {
|
|
631
|
+
const { genericOAuth } = await import("better-auth/plugins/generic-oauth");
|
|
632
|
+
plugins.push(genericOAuth({
|
|
633
|
+
config: this.config.oidcProviders.map((p) => ({
|
|
634
|
+
providerId: p.providerId,
|
|
635
|
+
...p.discoveryUrl ? { discoveryUrl: p.discoveryUrl } : {},
|
|
636
|
+
...p.issuer ? { issuer: p.issuer } : {},
|
|
637
|
+
...p.authorizationUrl ? { authorizationUrl: p.authorizationUrl } : {},
|
|
638
|
+
...p.tokenUrl ? { tokenUrl: p.tokenUrl } : {},
|
|
639
|
+
...p.userInfoUrl ? { userInfoUrl: p.userInfoUrl } : {},
|
|
640
|
+
clientId: p.clientId,
|
|
641
|
+
clientSecret: p.clientSecret,
|
|
642
|
+
...p.scopes ? { scopes: p.scopes } : {},
|
|
643
|
+
...p.pkce != null ? { pkce: p.pkce } : {}
|
|
644
|
+
}))
|
|
645
|
+
}));
|
|
646
|
+
}
|
|
647
|
+
if (enabled.oidcProvider) {
|
|
648
|
+
const { jwt } = await import("better-auth/plugins");
|
|
649
|
+
plugins.push(jwt({ schema: buildJwtPluginSchema() }));
|
|
650
|
+
const { oauthProvider } = await import("@better-auth/oauth-provider");
|
|
651
|
+
const baseUrl = (this.config.baseUrl ?? "").replace(/\/$/, "");
|
|
652
|
+
plugins.push(oauthProvider({
|
|
653
|
+
// Account SPA renders both pages — see apps/account.
|
|
654
|
+
loginPage: `${baseUrl}/_account/login`,
|
|
655
|
+
consentPage: `${baseUrl}/_account/oauth/consent`,
|
|
656
|
+
schema: buildOauthProviderPluginSchema()
|
|
657
|
+
}));
|
|
658
|
+
}
|
|
659
|
+
if (enabled.deviceAuthorization) {
|
|
660
|
+
const { deviceAuthorization } = await import("better-auth/plugins/device-authorization");
|
|
661
|
+
const baseUrl = (this.config.baseUrl ?? "").replace(/\/$/, "");
|
|
662
|
+
plugins.push(deviceAuthorization({
|
|
663
|
+
verificationUri: `${baseUrl}/_account/auth/device`,
|
|
664
|
+
schema: buildDeviceAuthorizationPluginSchema()
|
|
665
|
+
}));
|
|
666
|
+
}
|
|
491
667
|
return plugins;
|
|
492
668
|
}
|
|
493
669
|
/**
|
|
@@ -563,7 +739,7 @@ var AuthManager = class {
|
|
|
563
739
|
* @returns Web standard Response object
|
|
564
740
|
*/
|
|
565
741
|
async handleRequest(request) {
|
|
566
|
-
const auth = this.getOrCreateAuth();
|
|
742
|
+
const auth = await this.getOrCreateAuth();
|
|
567
743
|
const response = await auth.handler(request);
|
|
568
744
|
if (response.status >= 500) {
|
|
569
745
|
try {
|
|
@@ -579,18 +755,19 @@ var AuthManager = class {
|
|
|
579
755
|
* Get the better-auth API for programmatic access
|
|
580
756
|
* Use this for server-side operations (e.g., creating users, checking sessions)
|
|
581
757
|
*/
|
|
582
|
-
|
|
583
|
-
|
|
758
|
+
async getApi() {
|
|
759
|
+
const auth = await this.getOrCreateAuth();
|
|
760
|
+
return auth.api;
|
|
584
761
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
762
|
+
// ---------------------------------------------------------------------------
|
|
763
|
+
// Device Flow (CLI browser-based login)
|
|
764
|
+
//
|
|
765
|
+
// The device authorization flow (RFC 8628) is now handled entirely by
|
|
766
|
+
// better-auth's `device-authorization` plugin. Endpoints are exposed at
|
|
767
|
+
// `${basePath}/device/{code,token,approve,deny}` and persisted in
|
|
768
|
+
// `sys_device_code`. Enable via `plugins.deviceAuthorization: true` in
|
|
769
|
+
// AuthPluginConfig.
|
|
770
|
+
// ---------------------------------------------------------------------------
|
|
594
771
|
getPublicConfig() {
|
|
595
772
|
const socialProviders = [];
|
|
596
773
|
if (this.config.socialProviders) {
|
|
@@ -610,11 +787,22 @@ var AuthManager = class {
|
|
|
610
787
|
socialProviders.push({
|
|
611
788
|
id,
|
|
612
789
|
name: nameMap[id] || id.charAt(0).toUpperCase() + id.slice(1),
|
|
613
|
-
enabled: true
|
|
790
|
+
enabled: true,
|
|
791
|
+
type: "social"
|
|
614
792
|
});
|
|
615
793
|
}
|
|
616
794
|
}
|
|
617
795
|
}
|
|
796
|
+
if (this.config.oidcProviders?.length) {
|
|
797
|
+
for (const p of this.config.oidcProviders) {
|
|
798
|
+
socialProviders.push({
|
|
799
|
+
id: p.providerId,
|
|
800
|
+
name: p.name ?? p.providerId.charAt(0).toUpperCase() + p.providerId.slice(1),
|
|
801
|
+
enabled: true,
|
|
802
|
+
type: "oidc"
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
}
|
|
618
806
|
const emailPasswordConfig = this.config.emailAndPassword ?? {};
|
|
619
807
|
const emailPassword = {
|
|
620
808
|
enabled: emailPasswordConfig.enabled !== false,
|
|
@@ -627,7 +815,9 @@ var AuthManager = class {
|
|
|
627
815
|
twoFactor: pluginConfig.twoFactor ?? false,
|
|
628
816
|
passkeys: pluginConfig.passkeys ?? false,
|
|
629
817
|
magicLink: pluginConfig.magicLink ?? false,
|
|
630
|
-
organization: pluginConfig.organization ??
|
|
818
|
+
organization: pluginConfig.organization ?? true,
|
|
819
|
+
oidcProvider: pluginConfig.oidcProvider ?? false,
|
|
820
|
+
deviceAuthorization: pluginConfig.deviceAuthorization ?? false
|
|
631
821
|
};
|
|
632
822
|
return {
|
|
633
823
|
emailPassword,
|
|
@@ -635,776 +825,50 @@ var AuthManager = class {
|
|
|
635
825
|
features
|
|
636
826
|
};
|
|
637
827
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
label: "User",
|
|
646
|
-
pluralLabel: "Users",
|
|
647
|
-
icon: "user",
|
|
648
|
-
isSystem: true,
|
|
649
|
-
description: "User accounts for authentication",
|
|
650
|
-
titleFormat: "{name} ({email})",
|
|
651
|
-
compactLayout: ["name", "email", "email_verified"],
|
|
652
|
-
fields: {
|
|
653
|
-
id: import_data.Field.text({
|
|
654
|
-
label: "User ID",
|
|
655
|
-
required: true,
|
|
656
|
-
readonly: true
|
|
657
|
-
}),
|
|
658
|
-
created_at: import_data.Field.datetime({
|
|
659
|
-
label: "Created At",
|
|
660
|
-
defaultValue: "NOW()",
|
|
661
|
-
readonly: true
|
|
662
|
-
}),
|
|
663
|
-
updated_at: import_data.Field.datetime({
|
|
664
|
-
label: "Updated At",
|
|
665
|
-
defaultValue: "NOW()",
|
|
666
|
-
readonly: true
|
|
667
|
-
}),
|
|
668
|
-
email: import_data.Field.email({
|
|
669
|
-
label: "Email",
|
|
670
|
-
required: true,
|
|
671
|
-
searchable: true
|
|
672
|
-
}),
|
|
673
|
-
email_verified: import_data.Field.boolean({
|
|
674
|
-
label: "Email Verified",
|
|
675
|
-
defaultValue: false
|
|
676
|
-
}),
|
|
677
|
-
name: import_data.Field.text({
|
|
678
|
-
label: "Name",
|
|
679
|
-
required: true,
|
|
680
|
-
searchable: true,
|
|
681
|
-
maxLength: 255
|
|
682
|
-
}),
|
|
683
|
-
image: import_data.Field.url({
|
|
684
|
-
label: "Profile Image",
|
|
685
|
-
required: false
|
|
686
|
-
})
|
|
687
|
-
},
|
|
688
|
-
indexes: [
|
|
689
|
-
{ fields: ["email"], unique: true },
|
|
690
|
-
{ fields: ["created_at"], unique: false }
|
|
691
|
-
],
|
|
692
|
-
enable: {
|
|
693
|
-
trackHistory: true,
|
|
694
|
-
searchable: true,
|
|
695
|
-
apiEnabled: true,
|
|
696
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
697
|
-
trash: true,
|
|
698
|
-
mru: true
|
|
699
|
-
},
|
|
700
|
-
validations: [
|
|
701
|
-
{
|
|
702
|
-
name: "email_unique",
|
|
703
|
-
type: "unique",
|
|
704
|
-
severity: "error",
|
|
705
|
-
message: "Email must be unique",
|
|
706
|
-
fields: ["email"],
|
|
707
|
-
caseSensitive: false
|
|
708
|
-
}
|
|
709
|
-
]
|
|
710
|
-
});
|
|
711
|
-
|
|
712
|
-
// src/objects/sys-session.object.ts
|
|
713
|
-
var import_data2 = require("@objectstack/spec/data");
|
|
714
|
-
var SysSession = import_data2.ObjectSchema.create({
|
|
715
|
-
namespace: "sys",
|
|
716
|
-
name: "session",
|
|
717
|
-
label: "Session",
|
|
718
|
-
pluralLabel: "Sessions",
|
|
719
|
-
icon: "key",
|
|
720
|
-
isSystem: true,
|
|
721
|
-
description: "Active user sessions",
|
|
722
|
-
titleFormat: "Session {token}",
|
|
723
|
-
compactLayout: ["user_id", "expires_at", "ip_address"],
|
|
724
|
-
fields: {
|
|
725
|
-
id: import_data2.Field.text({
|
|
726
|
-
label: "Session ID",
|
|
727
|
-
required: true,
|
|
728
|
-
readonly: true
|
|
729
|
-
}),
|
|
730
|
-
created_at: import_data2.Field.datetime({
|
|
731
|
-
label: "Created At",
|
|
732
|
-
defaultValue: "NOW()",
|
|
733
|
-
readonly: true
|
|
734
|
-
}),
|
|
735
|
-
updated_at: import_data2.Field.datetime({
|
|
736
|
-
label: "Updated At",
|
|
737
|
-
defaultValue: "NOW()",
|
|
738
|
-
readonly: true
|
|
739
|
-
}),
|
|
740
|
-
user_id: import_data2.Field.text({
|
|
741
|
-
label: "User ID",
|
|
742
|
-
required: true
|
|
743
|
-
}),
|
|
744
|
-
expires_at: import_data2.Field.datetime({
|
|
745
|
-
label: "Expires At",
|
|
746
|
-
required: true
|
|
747
|
-
}),
|
|
748
|
-
token: import_data2.Field.text({
|
|
749
|
-
label: "Session Token",
|
|
750
|
-
required: true
|
|
751
|
-
}),
|
|
752
|
-
ip_address: import_data2.Field.text({
|
|
753
|
-
label: "IP Address",
|
|
754
|
-
required: false,
|
|
755
|
-
maxLength: 45
|
|
756
|
-
// Support IPv6
|
|
757
|
-
}),
|
|
758
|
-
user_agent: import_data2.Field.textarea({
|
|
759
|
-
label: "User Agent",
|
|
760
|
-
required: false
|
|
761
|
-
})
|
|
762
|
-
},
|
|
763
|
-
indexes: [
|
|
764
|
-
{ fields: ["token"], unique: true },
|
|
765
|
-
{ fields: ["user_id"], unique: false },
|
|
766
|
-
{ fields: ["expires_at"], unique: false }
|
|
767
|
-
],
|
|
768
|
-
enable: {
|
|
769
|
-
trackHistory: false,
|
|
770
|
-
searchable: false,
|
|
771
|
-
apiEnabled: true,
|
|
772
|
-
apiMethods: ["get", "list", "create", "delete"],
|
|
773
|
-
trash: false,
|
|
774
|
-
mru: false
|
|
775
|
-
}
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
// src/objects/sys-account.object.ts
|
|
779
|
-
var import_data3 = require("@objectstack/spec/data");
|
|
780
|
-
var SysAccount = import_data3.ObjectSchema.create({
|
|
781
|
-
namespace: "sys",
|
|
782
|
-
name: "account",
|
|
783
|
-
label: "Account",
|
|
784
|
-
pluralLabel: "Accounts",
|
|
785
|
-
icon: "link",
|
|
786
|
-
isSystem: true,
|
|
787
|
-
description: "OAuth and authentication provider accounts",
|
|
788
|
-
titleFormat: "{provider_id} - {account_id}",
|
|
789
|
-
compactLayout: ["provider_id", "user_id", "account_id"],
|
|
790
|
-
fields: {
|
|
791
|
-
id: import_data3.Field.text({
|
|
792
|
-
label: "Account ID",
|
|
793
|
-
required: true,
|
|
794
|
-
readonly: true
|
|
795
|
-
}),
|
|
796
|
-
created_at: import_data3.Field.datetime({
|
|
797
|
-
label: "Created At",
|
|
798
|
-
defaultValue: "NOW()",
|
|
799
|
-
readonly: true
|
|
800
|
-
}),
|
|
801
|
-
updated_at: import_data3.Field.datetime({
|
|
802
|
-
label: "Updated At",
|
|
803
|
-
defaultValue: "NOW()",
|
|
804
|
-
readonly: true
|
|
805
|
-
}),
|
|
806
|
-
provider_id: import_data3.Field.text({
|
|
807
|
-
label: "Provider ID",
|
|
808
|
-
required: true,
|
|
809
|
-
description: "OAuth provider identifier (google, github, etc.)"
|
|
810
|
-
}),
|
|
811
|
-
account_id: import_data3.Field.text({
|
|
812
|
-
label: "Provider Account ID",
|
|
813
|
-
required: true,
|
|
814
|
-
description: "User's ID in the provider's system"
|
|
815
|
-
}),
|
|
816
|
-
user_id: import_data3.Field.text({
|
|
817
|
-
label: "User ID",
|
|
818
|
-
required: true,
|
|
819
|
-
description: "Link to user table"
|
|
820
|
-
}),
|
|
821
|
-
access_token: import_data3.Field.textarea({
|
|
822
|
-
label: "Access Token",
|
|
823
|
-
required: false
|
|
824
|
-
}),
|
|
825
|
-
refresh_token: import_data3.Field.textarea({
|
|
826
|
-
label: "Refresh Token",
|
|
827
|
-
required: false
|
|
828
|
-
}),
|
|
829
|
-
id_token: import_data3.Field.textarea({
|
|
830
|
-
label: "ID Token",
|
|
831
|
-
required: false
|
|
832
|
-
}),
|
|
833
|
-
access_token_expires_at: import_data3.Field.datetime({
|
|
834
|
-
label: "Access Token Expires At",
|
|
835
|
-
required: false
|
|
836
|
-
}),
|
|
837
|
-
refresh_token_expires_at: import_data3.Field.datetime({
|
|
838
|
-
label: "Refresh Token Expires At",
|
|
839
|
-
required: false
|
|
840
|
-
}),
|
|
841
|
-
scope: import_data3.Field.text({
|
|
842
|
-
label: "OAuth Scope",
|
|
843
|
-
required: false
|
|
844
|
-
}),
|
|
845
|
-
password: import_data3.Field.text({
|
|
846
|
-
label: "Password Hash",
|
|
847
|
-
required: false,
|
|
848
|
-
description: "Hashed password for email/password provider"
|
|
849
|
-
})
|
|
850
|
-
},
|
|
851
|
-
indexes: [
|
|
852
|
-
{ fields: ["user_id"], unique: false },
|
|
853
|
-
{ fields: ["provider_id", "account_id"], unique: true }
|
|
854
|
-
],
|
|
855
|
-
enable: {
|
|
856
|
-
trackHistory: false,
|
|
857
|
-
searchable: false,
|
|
858
|
-
apiEnabled: true,
|
|
859
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
860
|
-
trash: true,
|
|
861
|
-
mru: false
|
|
862
|
-
}
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
// src/objects/sys-verification.object.ts
|
|
866
|
-
var import_data4 = require("@objectstack/spec/data");
|
|
867
|
-
var SysVerification = import_data4.ObjectSchema.create({
|
|
868
|
-
namespace: "sys",
|
|
869
|
-
name: "verification",
|
|
870
|
-
label: "Verification",
|
|
871
|
-
pluralLabel: "Verifications",
|
|
872
|
-
icon: "shield-check",
|
|
873
|
-
isSystem: true,
|
|
874
|
-
description: "Email and phone verification tokens",
|
|
875
|
-
titleFormat: "Verification for {identifier}",
|
|
876
|
-
compactLayout: ["identifier", "expires_at", "created_at"],
|
|
877
|
-
fields: {
|
|
878
|
-
id: import_data4.Field.text({
|
|
879
|
-
label: "Verification ID",
|
|
880
|
-
required: true,
|
|
881
|
-
readonly: true
|
|
882
|
-
}),
|
|
883
|
-
created_at: import_data4.Field.datetime({
|
|
884
|
-
label: "Created At",
|
|
885
|
-
defaultValue: "NOW()",
|
|
886
|
-
readonly: true
|
|
887
|
-
}),
|
|
888
|
-
updated_at: import_data4.Field.datetime({
|
|
889
|
-
label: "Updated At",
|
|
890
|
-
defaultValue: "NOW()",
|
|
891
|
-
readonly: true
|
|
892
|
-
}),
|
|
893
|
-
value: import_data4.Field.text({
|
|
894
|
-
label: "Verification Token",
|
|
895
|
-
required: true,
|
|
896
|
-
description: "Token or code for verification"
|
|
897
|
-
}),
|
|
898
|
-
expires_at: import_data4.Field.datetime({
|
|
899
|
-
label: "Expires At",
|
|
900
|
-
required: true
|
|
901
|
-
}),
|
|
902
|
-
identifier: import_data4.Field.text({
|
|
903
|
-
label: "Identifier",
|
|
904
|
-
required: true,
|
|
905
|
-
description: "Email address or phone number"
|
|
906
|
-
})
|
|
907
|
-
},
|
|
908
|
-
indexes: [
|
|
909
|
-
{ fields: ["value"], unique: true },
|
|
910
|
-
{ fields: ["identifier"], unique: false },
|
|
911
|
-
{ fields: ["expires_at"], unique: false }
|
|
912
|
-
],
|
|
913
|
-
enable: {
|
|
914
|
-
trackHistory: false,
|
|
915
|
-
searchable: false,
|
|
916
|
-
apiEnabled: true,
|
|
917
|
-
apiMethods: ["get", "create", "delete"],
|
|
918
|
-
trash: false,
|
|
919
|
-
mru: false
|
|
920
|
-
}
|
|
921
|
-
});
|
|
922
|
-
|
|
923
|
-
// src/objects/sys-organization.object.ts
|
|
924
|
-
var import_data5 = require("@objectstack/spec/data");
|
|
925
|
-
var SysOrganization = import_data5.ObjectSchema.create({
|
|
926
|
-
namespace: "sys",
|
|
927
|
-
name: "organization",
|
|
928
|
-
label: "Organization",
|
|
929
|
-
pluralLabel: "Organizations",
|
|
930
|
-
icon: "building-2",
|
|
931
|
-
isSystem: true,
|
|
932
|
-
description: "Organizations for multi-tenant grouping",
|
|
933
|
-
titleFormat: "{name}",
|
|
934
|
-
compactLayout: ["name", "slug", "created_at"],
|
|
935
|
-
fields: {
|
|
936
|
-
id: import_data5.Field.text({
|
|
937
|
-
label: "Organization ID",
|
|
938
|
-
required: true,
|
|
939
|
-
readonly: true
|
|
940
|
-
}),
|
|
941
|
-
created_at: import_data5.Field.datetime({
|
|
942
|
-
label: "Created At",
|
|
943
|
-
defaultValue: "NOW()",
|
|
944
|
-
readonly: true
|
|
945
|
-
}),
|
|
946
|
-
updated_at: import_data5.Field.datetime({
|
|
947
|
-
label: "Updated At",
|
|
948
|
-
defaultValue: "NOW()",
|
|
949
|
-
readonly: true
|
|
950
|
-
}),
|
|
951
|
-
name: import_data5.Field.text({
|
|
952
|
-
label: "Name",
|
|
953
|
-
required: true,
|
|
954
|
-
searchable: true,
|
|
955
|
-
maxLength: 255
|
|
956
|
-
}),
|
|
957
|
-
slug: import_data5.Field.text({
|
|
958
|
-
label: "Slug",
|
|
959
|
-
required: false,
|
|
960
|
-
maxLength: 255,
|
|
961
|
-
description: "URL-friendly identifier"
|
|
962
|
-
}),
|
|
963
|
-
logo: import_data5.Field.url({
|
|
964
|
-
label: "Logo",
|
|
965
|
-
required: false
|
|
966
|
-
}),
|
|
967
|
-
metadata: import_data5.Field.textarea({
|
|
968
|
-
label: "Metadata",
|
|
969
|
-
required: false,
|
|
970
|
-
description: "JSON-serialized organization metadata"
|
|
971
|
-
})
|
|
972
|
-
},
|
|
973
|
-
indexes: [
|
|
974
|
-
{ fields: ["slug"], unique: true },
|
|
975
|
-
{ fields: ["name"] }
|
|
976
|
-
],
|
|
977
|
-
enable: {
|
|
978
|
-
trackHistory: true,
|
|
979
|
-
searchable: true,
|
|
980
|
-
apiEnabled: true,
|
|
981
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
982
|
-
trash: true,
|
|
983
|
-
mru: true
|
|
984
|
-
}
|
|
985
|
-
});
|
|
986
|
-
|
|
987
|
-
// src/objects/sys-member.object.ts
|
|
988
|
-
var import_data6 = require("@objectstack/spec/data");
|
|
989
|
-
var SysMember = import_data6.ObjectSchema.create({
|
|
990
|
-
namespace: "sys",
|
|
991
|
-
name: "member",
|
|
992
|
-
label: "Member",
|
|
993
|
-
pluralLabel: "Members",
|
|
994
|
-
icon: "user-check",
|
|
995
|
-
isSystem: true,
|
|
996
|
-
description: "Organization membership records",
|
|
997
|
-
titleFormat: "{user_id} in {organization_id}",
|
|
998
|
-
compactLayout: ["user_id", "organization_id", "role"],
|
|
999
|
-
fields: {
|
|
1000
|
-
id: import_data6.Field.text({
|
|
1001
|
-
label: "Member ID",
|
|
1002
|
-
required: true,
|
|
1003
|
-
readonly: true
|
|
1004
|
-
}),
|
|
1005
|
-
created_at: import_data6.Field.datetime({
|
|
1006
|
-
label: "Created At",
|
|
1007
|
-
defaultValue: "NOW()",
|
|
1008
|
-
readonly: true
|
|
1009
|
-
}),
|
|
1010
|
-
organization_id: import_data6.Field.text({
|
|
1011
|
-
label: "Organization ID",
|
|
1012
|
-
required: true
|
|
1013
|
-
}),
|
|
1014
|
-
user_id: import_data6.Field.text({
|
|
1015
|
-
label: "User ID",
|
|
1016
|
-
required: true
|
|
1017
|
-
}),
|
|
1018
|
-
role: import_data6.Field.text({
|
|
1019
|
-
label: "Role",
|
|
1020
|
-
required: false,
|
|
1021
|
-
description: "Member role within the organization (e.g. admin, member)",
|
|
1022
|
-
maxLength: 100
|
|
1023
|
-
})
|
|
1024
|
-
},
|
|
1025
|
-
indexes: [
|
|
1026
|
-
{ fields: ["organization_id", "user_id"], unique: true },
|
|
1027
|
-
{ fields: ["user_id"] }
|
|
1028
|
-
],
|
|
1029
|
-
enable: {
|
|
1030
|
-
trackHistory: true,
|
|
1031
|
-
searchable: false,
|
|
1032
|
-
apiEnabled: true,
|
|
1033
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
1034
|
-
trash: false,
|
|
1035
|
-
mru: false
|
|
1036
|
-
}
|
|
1037
|
-
});
|
|
1038
|
-
|
|
1039
|
-
// src/objects/sys-invitation.object.ts
|
|
1040
|
-
var import_data7 = require("@objectstack/spec/data");
|
|
1041
|
-
var SysInvitation = import_data7.ObjectSchema.create({
|
|
1042
|
-
namespace: "sys",
|
|
1043
|
-
name: "invitation",
|
|
1044
|
-
label: "Invitation",
|
|
1045
|
-
pluralLabel: "Invitations",
|
|
1046
|
-
icon: "mail",
|
|
1047
|
-
isSystem: true,
|
|
1048
|
-
description: "Organization invitations for user onboarding",
|
|
1049
|
-
titleFormat: "Invitation to {organization_id}",
|
|
1050
|
-
compactLayout: ["email", "organization_id", "status"],
|
|
1051
|
-
fields: {
|
|
1052
|
-
id: import_data7.Field.text({
|
|
1053
|
-
label: "Invitation ID",
|
|
1054
|
-
required: true,
|
|
1055
|
-
readonly: true
|
|
1056
|
-
}),
|
|
1057
|
-
created_at: import_data7.Field.datetime({
|
|
1058
|
-
label: "Created At",
|
|
1059
|
-
defaultValue: "NOW()",
|
|
1060
|
-
readonly: true
|
|
1061
|
-
}),
|
|
1062
|
-
organization_id: import_data7.Field.text({
|
|
1063
|
-
label: "Organization ID",
|
|
1064
|
-
required: true
|
|
1065
|
-
}),
|
|
1066
|
-
email: import_data7.Field.email({
|
|
1067
|
-
label: "Email",
|
|
1068
|
-
required: true,
|
|
1069
|
-
description: "Email address of the invited user"
|
|
1070
|
-
}),
|
|
1071
|
-
role: import_data7.Field.text({
|
|
1072
|
-
label: "Role",
|
|
1073
|
-
required: false,
|
|
1074
|
-
maxLength: 100,
|
|
1075
|
-
description: "Role to assign upon acceptance"
|
|
1076
|
-
}),
|
|
1077
|
-
status: import_data7.Field.select(["pending", "accepted", "rejected", "expired", "canceled"], {
|
|
1078
|
-
label: "Status",
|
|
1079
|
-
required: true,
|
|
1080
|
-
defaultValue: "pending"
|
|
1081
|
-
}),
|
|
1082
|
-
inviter_id: import_data7.Field.text({
|
|
1083
|
-
label: "Inviter ID",
|
|
1084
|
-
required: true,
|
|
1085
|
-
description: "User ID of the person who sent the invitation"
|
|
1086
|
-
}),
|
|
1087
|
-
expires_at: import_data7.Field.datetime({
|
|
1088
|
-
label: "Expires At",
|
|
1089
|
-
required: true
|
|
1090
|
-
}),
|
|
1091
|
-
team_id: import_data7.Field.text({
|
|
1092
|
-
label: "Team ID",
|
|
1093
|
-
required: false,
|
|
1094
|
-
description: "Optional team to assign upon acceptance"
|
|
1095
|
-
})
|
|
1096
|
-
},
|
|
1097
|
-
indexes: [
|
|
1098
|
-
{ fields: ["organization_id"] },
|
|
1099
|
-
{ fields: ["email"] },
|
|
1100
|
-
{ fields: ["expires_at"] }
|
|
1101
|
-
],
|
|
1102
|
-
enable: {
|
|
1103
|
-
trackHistory: true,
|
|
1104
|
-
searchable: false,
|
|
1105
|
-
apiEnabled: true,
|
|
1106
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
1107
|
-
trash: false,
|
|
1108
|
-
mru: false
|
|
1109
|
-
}
|
|
1110
|
-
});
|
|
1111
|
-
|
|
1112
|
-
// src/objects/sys-team.object.ts
|
|
1113
|
-
var import_data8 = require("@objectstack/spec/data");
|
|
1114
|
-
var SysTeam = import_data8.ObjectSchema.create({
|
|
1115
|
-
namespace: "sys",
|
|
1116
|
-
name: "team",
|
|
1117
|
-
label: "Team",
|
|
1118
|
-
pluralLabel: "Teams",
|
|
1119
|
-
icon: "users",
|
|
1120
|
-
isSystem: true,
|
|
1121
|
-
description: "Teams within organizations for fine-grained grouping",
|
|
1122
|
-
titleFormat: "{name}",
|
|
1123
|
-
compactLayout: ["name", "organization_id", "created_at"],
|
|
1124
|
-
fields: {
|
|
1125
|
-
id: import_data8.Field.text({
|
|
1126
|
-
label: "Team ID",
|
|
1127
|
-
required: true,
|
|
1128
|
-
readonly: true
|
|
1129
|
-
}),
|
|
1130
|
-
created_at: import_data8.Field.datetime({
|
|
1131
|
-
label: "Created At",
|
|
1132
|
-
defaultValue: "NOW()",
|
|
1133
|
-
readonly: true
|
|
1134
|
-
}),
|
|
1135
|
-
updated_at: import_data8.Field.datetime({
|
|
1136
|
-
label: "Updated At",
|
|
1137
|
-
defaultValue: "NOW()",
|
|
1138
|
-
readonly: true
|
|
1139
|
-
}),
|
|
1140
|
-
name: import_data8.Field.text({
|
|
1141
|
-
label: "Name",
|
|
1142
|
-
required: true,
|
|
1143
|
-
searchable: true,
|
|
1144
|
-
maxLength: 255
|
|
1145
|
-
}),
|
|
1146
|
-
organization_id: import_data8.Field.text({
|
|
1147
|
-
label: "Organization ID",
|
|
1148
|
-
required: true
|
|
1149
|
-
})
|
|
1150
|
-
},
|
|
1151
|
-
indexes: [
|
|
1152
|
-
{ fields: ["organization_id"] },
|
|
1153
|
-
{ fields: ["name", "organization_id"], unique: true }
|
|
1154
|
-
],
|
|
1155
|
-
enable: {
|
|
1156
|
-
trackHistory: true,
|
|
1157
|
-
searchable: true,
|
|
1158
|
-
apiEnabled: true,
|
|
1159
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
1160
|
-
trash: true,
|
|
1161
|
-
mru: false
|
|
1162
|
-
}
|
|
1163
|
-
});
|
|
1164
|
-
|
|
1165
|
-
// src/objects/sys-team-member.object.ts
|
|
1166
|
-
var import_data9 = require("@objectstack/spec/data");
|
|
1167
|
-
var SysTeamMember = import_data9.ObjectSchema.create({
|
|
1168
|
-
namespace: "sys",
|
|
1169
|
-
name: "team_member",
|
|
1170
|
-
label: "Team Member",
|
|
1171
|
-
pluralLabel: "Team Members",
|
|
1172
|
-
icon: "user-plus",
|
|
1173
|
-
isSystem: true,
|
|
1174
|
-
description: "Team membership records linking users to teams",
|
|
1175
|
-
titleFormat: "{user_id} in {team_id}",
|
|
1176
|
-
compactLayout: ["user_id", "team_id", "created_at"],
|
|
1177
|
-
fields: {
|
|
1178
|
-
id: import_data9.Field.text({
|
|
1179
|
-
label: "Team Member ID",
|
|
1180
|
-
required: true,
|
|
1181
|
-
readonly: true
|
|
1182
|
-
}),
|
|
1183
|
-
created_at: import_data9.Field.datetime({
|
|
1184
|
-
label: "Created At",
|
|
1185
|
-
defaultValue: "NOW()",
|
|
1186
|
-
readonly: true
|
|
1187
|
-
}),
|
|
1188
|
-
team_id: import_data9.Field.text({
|
|
1189
|
-
label: "Team ID",
|
|
1190
|
-
required: true
|
|
1191
|
-
}),
|
|
1192
|
-
user_id: import_data9.Field.text({
|
|
1193
|
-
label: "User ID",
|
|
1194
|
-
required: true
|
|
1195
|
-
})
|
|
1196
|
-
},
|
|
1197
|
-
indexes: [
|
|
1198
|
-
{ fields: ["team_id", "user_id"], unique: true },
|
|
1199
|
-
{ fields: ["user_id"] }
|
|
1200
|
-
],
|
|
1201
|
-
enable: {
|
|
1202
|
-
trackHistory: true,
|
|
1203
|
-
searchable: false,
|
|
1204
|
-
apiEnabled: true,
|
|
1205
|
-
apiMethods: ["get", "list", "create", "delete"],
|
|
1206
|
-
trash: false,
|
|
1207
|
-
mru: false
|
|
1208
|
-
}
|
|
1209
|
-
});
|
|
1210
|
-
|
|
1211
|
-
// src/objects/sys-api-key.object.ts
|
|
1212
|
-
var import_data10 = require("@objectstack/spec/data");
|
|
1213
|
-
var SysApiKey = import_data10.ObjectSchema.create({
|
|
1214
|
-
namespace: "sys",
|
|
1215
|
-
name: "api_key",
|
|
1216
|
-
label: "API Key",
|
|
1217
|
-
pluralLabel: "API Keys",
|
|
1218
|
-
icon: "key-round",
|
|
1219
|
-
isSystem: true,
|
|
1220
|
-
description: "API keys for programmatic access",
|
|
1221
|
-
titleFormat: "{name}",
|
|
1222
|
-
compactLayout: ["name", "user_id", "expires_at"],
|
|
1223
|
-
fields: {
|
|
1224
|
-
id: import_data10.Field.text({
|
|
1225
|
-
label: "API Key ID",
|
|
1226
|
-
required: true,
|
|
1227
|
-
readonly: true
|
|
1228
|
-
}),
|
|
1229
|
-
created_at: import_data10.Field.datetime({
|
|
1230
|
-
label: "Created At",
|
|
1231
|
-
defaultValue: "NOW()",
|
|
1232
|
-
readonly: true
|
|
1233
|
-
}),
|
|
1234
|
-
updated_at: import_data10.Field.datetime({
|
|
1235
|
-
label: "Updated At",
|
|
1236
|
-
defaultValue: "NOW()",
|
|
1237
|
-
readonly: true
|
|
1238
|
-
}),
|
|
1239
|
-
name: import_data10.Field.text({
|
|
1240
|
-
label: "Name",
|
|
1241
|
-
required: true,
|
|
1242
|
-
maxLength: 255,
|
|
1243
|
-
description: "Human-readable label for the API key"
|
|
1244
|
-
}),
|
|
1245
|
-
key: import_data10.Field.text({
|
|
1246
|
-
label: "Key",
|
|
1247
|
-
required: true,
|
|
1248
|
-
description: "Hashed API key value"
|
|
1249
|
-
}),
|
|
1250
|
-
prefix: import_data10.Field.text({
|
|
1251
|
-
label: "Prefix",
|
|
1252
|
-
required: false,
|
|
1253
|
-
maxLength: 16,
|
|
1254
|
-
description: 'Visible prefix for identifying the key (e.g., "osk_")'
|
|
1255
|
-
}),
|
|
1256
|
-
user_id: import_data10.Field.text({
|
|
1257
|
-
label: "User ID",
|
|
1258
|
-
required: true,
|
|
1259
|
-
description: "Owner user of this API key"
|
|
1260
|
-
}),
|
|
1261
|
-
scopes: import_data10.Field.textarea({
|
|
1262
|
-
label: "Scopes",
|
|
1263
|
-
required: false,
|
|
1264
|
-
description: "JSON array of permission scopes"
|
|
1265
|
-
}),
|
|
1266
|
-
expires_at: import_data10.Field.datetime({
|
|
1267
|
-
label: "Expires At",
|
|
1268
|
-
required: false
|
|
1269
|
-
}),
|
|
1270
|
-
last_used_at: import_data10.Field.datetime({
|
|
1271
|
-
label: "Last Used At",
|
|
1272
|
-
required: false
|
|
1273
|
-
}),
|
|
1274
|
-
revoked: import_data10.Field.boolean({
|
|
1275
|
-
label: "Revoked",
|
|
1276
|
-
defaultValue: false
|
|
1277
|
-
})
|
|
1278
|
-
},
|
|
1279
|
-
indexes: [
|
|
1280
|
-
{ fields: ["key"], unique: true },
|
|
1281
|
-
{ fields: ["user_id"] },
|
|
1282
|
-
{ fields: ["prefix"] }
|
|
1283
|
-
],
|
|
1284
|
-
enable: {
|
|
1285
|
-
trackHistory: true,
|
|
1286
|
-
searchable: false,
|
|
1287
|
-
apiEnabled: true,
|
|
1288
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
1289
|
-
trash: false,
|
|
1290
|
-
mru: false
|
|
1291
|
-
}
|
|
1292
|
-
});
|
|
1293
|
-
|
|
1294
|
-
// src/objects/sys-two-factor.object.ts
|
|
1295
|
-
var import_data11 = require("@objectstack/spec/data");
|
|
1296
|
-
var SysTwoFactor = import_data11.ObjectSchema.create({
|
|
1297
|
-
namespace: "sys",
|
|
1298
|
-
name: "two_factor",
|
|
1299
|
-
label: "Two Factor",
|
|
1300
|
-
pluralLabel: "Two Factor Credentials",
|
|
1301
|
-
icon: "smartphone",
|
|
1302
|
-
isSystem: true,
|
|
1303
|
-
description: "Two-factor authentication credentials",
|
|
1304
|
-
titleFormat: "Two-factor for {user_id}",
|
|
1305
|
-
compactLayout: ["user_id", "created_at"],
|
|
1306
|
-
fields: {
|
|
1307
|
-
id: import_data11.Field.text({
|
|
1308
|
-
label: "Two Factor ID",
|
|
1309
|
-
required: true,
|
|
1310
|
-
readonly: true
|
|
1311
|
-
}),
|
|
1312
|
-
created_at: import_data11.Field.datetime({
|
|
1313
|
-
label: "Created At",
|
|
1314
|
-
defaultValue: "NOW()",
|
|
1315
|
-
readonly: true
|
|
1316
|
-
}),
|
|
1317
|
-
updated_at: import_data11.Field.datetime({
|
|
1318
|
-
label: "Updated At",
|
|
1319
|
-
defaultValue: "NOW()",
|
|
1320
|
-
readonly: true
|
|
1321
|
-
}),
|
|
1322
|
-
user_id: import_data11.Field.text({
|
|
1323
|
-
label: "User ID",
|
|
1324
|
-
required: true
|
|
1325
|
-
}),
|
|
1326
|
-
secret: import_data11.Field.text({
|
|
1327
|
-
label: "Secret",
|
|
1328
|
-
required: true,
|
|
1329
|
-
description: "TOTP secret key"
|
|
1330
|
-
}),
|
|
1331
|
-
backup_codes: import_data11.Field.textarea({
|
|
1332
|
-
label: "Backup Codes",
|
|
1333
|
-
required: false,
|
|
1334
|
-
description: "JSON-serialized backup recovery codes"
|
|
1335
|
-
})
|
|
1336
|
-
},
|
|
1337
|
-
indexes: [
|
|
1338
|
-
{ fields: ["user_id"], unique: true }
|
|
1339
|
-
],
|
|
1340
|
-
enable: {
|
|
1341
|
-
trackHistory: false,
|
|
1342
|
-
searchable: false,
|
|
1343
|
-
apiEnabled: true,
|
|
1344
|
-
apiMethods: ["get", "create", "update", "delete"],
|
|
1345
|
-
trash: false,
|
|
1346
|
-
mru: false
|
|
828
|
+
/**
|
|
829
|
+
* Returns the data engine wired into this auth manager. Used by route
|
|
830
|
+
* handlers (e.g. bootstrap-status) that need to query identity tables
|
|
831
|
+
* directly without going through better-auth.
|
|
832
|
+
*/
|
|
833
|
+
getDataEngine() {
|
|
834
|
+
return this.config.dataEngine;
|
|
1347
835
|
}
|
|
1348
|
-
}
|
|
836
|
+
};
|
|
1349
837
|
|
|
1350
|
-
// src/
|
|
1351
|
-
var
|
|
1352
|
-
var
|
|
838
|
+
// src/manifest.ts
|
|
839
|
+
var import_identity = require("@objectstack/platform-objects/identity");
|
|
840
|
+
var AUTH_PLUGIN_ID = "com.objectstack.plugin-auth";
|
|
841
|
+
var AUTH_PLUGIN_VERSION = "3.0.1";
|
|
842
|
+
var authIdentityObjects = [
|
|
843
|
+
import_identity.SysUser,
|
|
844
|
+
import_identity.SysSession,
|
|
845
|
+
import_identity.SysAccount,
|
|
846
|
+
import_identity.SysVerification,
|
|
847
|
+
import_identity.SysOrganization,
|
|
848
|
+
import_identity.SysMember,
|
|
849
|
+
import_identity.SysInvitation,
|
|
850
|
+
import_identity.SysTeam,
|
|
851
|
+
import_identity.SysTeamMember,
|
|
852
|
+
import_identity.SysApiKey,
|
|
853
|
+
import_identity.SysTwoFactor,
|
|
854
|
+
import_identity.SysUserPreference,
|
|
855
|
+
import_identity.SysOauthApplication,
|
|
856
|
+
import_identity.SysOauthAccessToken,
|
|
857
|
+
import_identity.SysOauthRefreshToken,
|
|
858
|
+
import_identity.SysOauthConsent,
|
|
859
|
+
import_identity.SysJwks,
|
|
860
|
+
import_identity.SysDeviceCode
|
|
861
|
+
];
|
|
862
|
+
var authPluginManifestHeader = {
|
|
863
|
+
id: AUTH_PLUGIN_ID,
|
|
1353
864
|
namespace: "sys",
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
description: "
|
|
1360
|
-
|
|
1361
|
-
compactLayout: ["user_id", "key"],
|
|
1362
|
-
fields: {
|
|
1363
|
-
id: import_data12.Field.text({
|
|
1364
|
-
label: "Preference ID",
|
|
1365
|
-
required: true,
|
|
1366
|
-
readonly: true
|
|
1367
|
-
}),
|
|
1368
|
-
created_at: import_data12.Field.datetime({
|
|
1369
|
-
label: "Created At",
|
|
1370
|
-
defaultValue: "NOW()",
|
|
1371
|
-
readonly: true
|
|
1372
|
-
}),
|
|
1373
|
-
updated_at: import_data12.Field.datetime({
|
|
1374
|
-
label: "Updated At",
|
|
1375
|
-
defaultValue: "NOW()",
|
|
1376
|
-
readonly: true
|
|
1377
|
-
}),
|
|
1378
|
-
user_id: import_data12.Field.text({
|
|
1379
|
-
label: "User ID",
|
|
1380
|
-
required: true,
|
|
1381
|
-
maxLength: 255,
|
|
1382
|
-
description: "Owner user of this preference"
|
|
1383
|
-
}),
|
|
1384
|
-
key: import_data12.Field.text({
|
|
1385
|
-
label: "Key",
|
|
1386
|
-
required: true,
|
|
1387
|
-
maxLength: 255,
|
|
1388
|
-
description: "Preference key (e.g., theme, locale, plugin.ai.auto_save)"
|
|
1389
|
-
}),
|
|
1390
|
-
value: import_data12.Field.json({
|
|
1391
|
-
label: "Value",
|
|
1392
|
-
description: "Preference value (any JSON-serializable type)"
|
|
1393
|
-
})
|
|
1394
|
-
},
|
|
1395
|
-
indexes: [
|
|
1396
|
-
{ fields: ["user_id", "key"], unique: true },
|
|
1397
|
-
{ fields: ["user_id"], unique: false }
|
|
1398
|
-
],
|
|
1399
|
-
enable: {
|
|
1400
|
-
trackHistory: false,
|
|
1401
|
-
searchable: false,
|
|
1402
|
-
apiEnabled: true,
|
|
1403
|
-
apiMethods: ["get", "list", "create", "update", "delete"],
|
|
1404
|
-
trash: false,
|
|
1405
|
-
mru: false
|
|
1406
|
-
}
|
|
1407
|
-
});
|
|
865
|
+
version: AUTH_PLUGIN_VERSION,
|
|
866
|
+
type: "plugin",
|
|
867
|
+
scope: "system",
|
|
868
|
+
defaultDatasource: "cloud",
|
|
869
|
+
name: "Authentication & Identity Plugin",
|
|
870
|
+
description: "Core authentication objects for ObjectStack (User, Session, Account, Verification)"
|
|
871
|
+
};
|
|
1408
872
|
|
|
1409
873
|
// src/auth-plugin.ts
|
|
1410
874
|
var AuthPlugin = class {
|
|
@@ -1435,43 +899,27 @@ var AuthPlugin = class {
|
|
|
1435
899
|
});
|
|
1436
900
|
ctx.registerService("auth", this.authManager);
|
|
1437
901
|
ctx.getService("manifest").register({
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
objects
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
]
|
|
902
|
+
...authPluginManifestHeader,
|
|
903
|
+
objects: authIdentityObjects,
|
|
904
|
+
// The platform Setup App is a static metadata artifact (lives in
|
|
905
|
+
// @objectstack/platform-objects/apps). plugin-auth is the natural
|
|
906
|
+
// owner of its registration since it loads first among the trio
|
|
907
|
+
// (auth + security + audit) that supplies the underlying objects.
|
|
908
|
+
apps: [import_apps.SETUP_APP],
|
|
909
|
+
// Curated list views and dashboards consumed by the Setup App's
|
|
910
|
+
// navigation entries. The manifest service does NOT auto-discover
|
|
911
|
+
// these from the app definition — they must be registered as
|
|
912
|
+
// explicit top-level arrays per ObjectStackDefinitionSchema.
|
|
913
|
+
views: [
|
|
914
|
+
import_apps.UsersView,
|
|
915
|
+
import_apps.OrganizationsView,
|
|
916
|
+
import_apps.RolesView,
|
|
917
|
+
import_apps.SessionsView,
|
|
918
|
+
import_apps.AuditLogsView,
|
|
919
|
+
import_apps.PackageInstallationsView
|
|
920
|
+
],
|
|
921
|
+
dashboards: [import_apps.SystemOverviewDashboard, import_apps.SecurityOverviewDashboard]
|
|
1457
922
|
});
|
|
1458
|
-
try {
|
|
1459
|
-
const setupNav = ctx.getService("setupNav");
|
|
1460
|
-
if (setupNav) {
|
|
1461
|
-
setupNav.contribute({
|
|
1462
|
-
areaId: "area_administration",
|
|
1463
|
-
items: [
|
|
1464
|
-
{ id: "nav_users", type: "object", label: "Users", objectName: "user", icon: "users", order: 10 },
|
|
1465
|
-
{ id: "nav_organizations", type: "object", label: "Organizations", objectName: "organization", icon: "building-2", order: 20 },
|
|
1466
|
-
{ id: "nav_teams", type: "object", label: "Teams", objectName: "team", icon: "users-round", order: 30 },
|
|
1467
|
-
{ id: "nav_api_keys", type: "object", label: "API Keys", objectName: "api_key", icon: "key", order: 40 },
|
|
1468
|
-
{ id: "nav_sessions", type: "object", label: "Sessions", objectName: "session", icon: "monitor", order: 50 }
|
|
1469
|
-
]
|
|
1470
|
-
});
|
|
1471
|
-
ctx.logger.info("Auth navigation items contributed to Setup App");
|
|
1472
|
-
}
|
|
1473
|
-
} catch {
|
|
1474
|
-
}
|
|
1475
923
|
ctx.logger.info("Auth Plugin initialized successfully");
|
|
1476
924
|
}
|
|
1477
925
|
async start(ctx) {
|
|
@@ -1494,7 +942,8 @@ var AuthPlugin = class {
|
|
|
1494
942
|
const configuredUrl = this.options.baseUrl || "http://localhost:3000";
|
|
1495
943
|
const configuredOrigin = new URL(configuredUrl).origin;
|
|
1496
944
|
const actualUrl = `http://localhost:${actualPort}`;
|
|
1497
|
-
|
|
945
|
+
const configuredIsLocalhost = configuredOrigin.startsWith("http://localhost");
|
|
946
|
+
if (configuredIsLocalhost && configuredOrigin !== actualUrl) {
|
|
1498
947
|
this.authManager.setRuntimeBaseUrl(actualUrl);
|
|
1499
948
|
ctx.logger.info(
|
|
1500
949
|
`Auth baseUrl auto-updated to ${actualUrl} (configured: ${configuredUrl})`
|
|
@@ -1552,23 +1001,26 @@ var AuthPlugin = class {
|
|
|
1552
1001
|
);
|
|
1553
1002
|
}
|
|
1554
1003
|
const rawApp = httpServer.getRawApp();
|
|
1555
|
-
rawApp.get(`${basePath}/config`,
|
|
1004
|
+
rawApp.get(`${basePath}/config`, (c) => {
|
|
1556
1005
|
try {
|
|
1557
1006
|
const config = this.authManager.getPublicConfig();
|
|
1558
|
-
return c.json({
|
|
1559
|
-
success: true,
|
|
1560
|
-
data: config
|
|
1561
|
-
});
|
|
1007
|
+
return c.json({ success: true, data: config });
|
|
1562
1008
|
} catch (error) {
|
|
1563
1009
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1010
|
+
return c.json({ success: false, error: { code: "auth_config_error", message: err.message } }, 500);
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
rawApp.get(`${basePath}/bootstrap-status`, async (c) => {
|
|
1014
|
+
try {
|
|
1015
|
+
const dataEngine = this.authManager.getDataEngine();
|
|
1016
|
+
if (!dataEngine) {
|
|
1017
|
+
return c.json({ hasOwner: true });
|
|
1018
|
+
}
|
|
1019
|
+
const count = await dataEngine.count("sys_user", {});
|
|
1020
|
+
return c.json({ hasOwner: (count ?? 0) > 0 });
|
|
1021
|
+
} catch (error) {
|
|
1022
|
+
ctx.logger.warn("[AuthPlugin] bootstrap-status check failed; assuming bootstrapped", error);
|
|
1023
|
+
return c.json({ hasOwner: true });
|
|
1572
1024
|
}
|
|
1573
1025
|
});
|
|
1574
1026
|
rawApp.all(`${basePath}/*`, async (c) => {
|
|
@@ -1598,15 +1050,44 @@ var AuthPlugin = class {
|
|
|
1598
1050
|
);
|
|
1599
1051
|
}
|
|
1600
1052
|
});
|
|
1053
|
+
if (this.options.plugins?.oidcProvider) {
|
|
1054
|
+
void this.registerOidcDiscoveryRoutes(rawApp, ctx).catch((error) => {
|
|
1055
|
+
ctx.logger.error("Failed to register OIDC discovery routes", error);
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1601
1058
|
ctx.logger.info(`Auth routes registered: All requests under ${basePath}/* forwarded to better-auth`);
|
|
1602
1059
|
}
|
|
1060
|
+
/**
|
|
1061
|
+
* Mount the OIDC / OAuth 2.0 well-known discovery documents at the root
|
|
1062
|
+
* URL. Required by RFC 8414 §3 and OpenID Connect Discovery 1.0 §4 — the
|
|
1063
|
+
* documents must live at `/.well-known/{oauth-authorization-server,openid-configuration}`
|
|
1064
|
+
* relative to the issuer, not under the auth basePath.
|
|
1065
|
+
*/
|
|
1066
|
+
async registerOidcDiscoveryRoutes(rawApp, ctx) {
|
|
1067
|
+
const auth = await this.authManager.getAuthInstance();
|
|
1068
|
+
const { oauthProviderAuthServerMetadata, oauthProviderOpenIdConfigMetadata } = await import("@better-auth/oauth-provider");
|
|
1069
|
+
const authServerHandler = oauthProviderAuthServerMetadata(auth);
|
|
1070
|
+
const openidConfigHandler = oauthProviderOpenIdConfigMetadata(auth);
|
|
1071
|
+
rawApp.get("/.well-known/oauth-authorization-server", (c) => authServerHandler(c.req.raw));
|
|
1072
|
+
rawApp.get("/.well-known/openid-configuration", (c) => openidConfigHandler(c.req.raw));
|
|
1073
|
+
ctx.logger.info(
|
|
1074
|
+
"OIDC discovery endpoints mounted at /.well-known/{oauth-authorization-server,openid-configuration}"
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
1603
1077
|
};
|
|
1604
1078
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1605
1079
|
0 && (module.exports = {
|
|
1606
1080
|
AUTH_ACCOUNT_CONFIG,
|
|
1081
|
+
AUTH_DEVICE_CODE_SCHEMA,
|
|
1607
1082
|
AUTH_INVITATION_SCHEMA,
|
|
1083
|
+
AUTH_JWKS_SCHEMA,
|
|
1608
1084
|
AUTH_MEMBER_SCHEMA,
|
|
1609
1085
|
AUTH_MODEL_TO_PROTOCOL,
|
|
1086
|
+
AUTH_OAUTH_ACCESS_TOKEN_SCHEMA,
|
|
1087
|
+
AUTH_OAUTH_APPLICATION_SCHEMA,
|
|
1088
|
+
AUTH_OAUTH_CLIENT_SCHEMA,
|
|
1089
|
+
AUTH_OAUTH_CONSENT_SCHEMA,
|
|
1090
|
+
AUTH_OAUTH_REFRESH_TOKEN_SCHEMA,
|
|
1610
1091
|
AUTH_ORGANIZATION_SCHEMA,
|
|
1611
1092
|
AUTH_ORG_SESSION_FIELDS,
|
|
1612
1093
|
AUTH_SESSION_CONFIG,
|
|
@@ -1616,24 +1097,12 @@ var AuthPlugin = class {
|
|
|
1616
1097
|
AUTH_TWO_FACTOR_USER_FIELDS,
|
|
1617
1098
|
AUTH_USER_CONFIG,
|
|
1618
1099
|
AUTH_VERIFICATION_CONFIG,
|
|
1619
|
-
AuthAccount,
|
|
1620
1100
|
AuthManager,
|
|
1621
1101
|
AuthPlugin,
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
SysApiKey,
|
|
1627
|
-
SysInvitation,
|
|
1628
|
-
SysMember,
|
|
1629
|
-
SysOrganization,
|
|
1630
|
-
SysSession,
|
|
1631
|
-
SysTeam,
|
|
1632
|
-
SysTeamMember,
|
|
1633
|
-
SysTwoFactor,
|
|
1634
|
-
SysUser,
|
|
1635
|
-
SysUserPreference,
|
|
1636
|
-
SysVerification,
|
|
1102
|
+
buildDeviceAuthorizationPluginSchema,
|
|
1103
|
+
buildJwtPluginSchema,
|
|
1104
|
+
buildOauthProviderPluginSchema,
|
|
1105
|
+
buildOidcProviderPluginSchema,
|
|
1637
1106
|
buildOrganizationPluginSchema,
|
|
1638
1107
|
buildTwoFactorPluginSchema,
|
|
1639
1108
|
createObjectQLAdapter,
|