@owox/idp-owox-better-auth 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/README.md +119 -0
  2. package/dist/client/IdentityOwoxClient.d.ts +41 -0
  3. package/dist/client/IdentityOwoxClient.d.ts.map +1 -0
  4. package/dist/client/IdentityOwoxClient.js +128 -0
  5. package/dist/client/dto/authFlowDto.d.ts +27 -0
  6. package/dist/client/dto/authFlowDto.d.ts.map +1 -0
  7. package/dist/client/dto/authFlowDto.js +5 -0
  8. package/dist/client/dto/idpOwoxPayloadDto.d.ts +29 -0
  9. package/dist/client/dto/idpOwoxPayloadDto.d.ts.map +1 -0
  10. package/dist/client/dto/idpOwoxPayloadDto.js +28 -0
  11. package/dist/client/dto/index.d.ts +11 -0
  12. package/dist/client/dto/index.d.ts.map +1 -0
  13. package/dist/client/dto/index.js +10 -0
  14. package/dist/client/dto/introspectionDto.d.ts +70 -0
  15. package/dist/client/dto/introspectionDto.d.ts.map +1 -0
  16. package/dist/client/dto/introspectionDto.js +15 -0
  17. package/dist/client/dto/jwksDto.d.ts +102 -0
  18. package/dist/client/dto/jwksDto.d.ts.map +1 -0
  19. package/dist/client/dto/jwksDto.js +18 -0
  20. package/dist/client/dto/revocationDto.d.ts +11 -0
  21. package/dist/client/dto/revocationDto.d.ts.map +1 -0
  22. package/dist/client/dto/revocationDto.js +1 -0
  23. package/dist/client/dto/tokenDto.d.ts +33 -0
  24. package/dist/client/dto/tokenDto.d.ts.map +1 -0
  25. package/dist/client/dto/tokenDto.js +9 -0
  26. package/dist/client/dto/tokenType.d.ts +5 -0
  27. package/dist/client/dto/tokenType.d.ts.map +1 -0
  28. package/dist/client/dto/tokenType.js +1 -0
  29. package/dist/client/index.d.ts +6 -0
  30. package/dist/client/index.d.ts.map +1 -0
  31. package/dist/client/index.js +5 -0
  32. package/dist/config/idp-better-auth-config.d.ts +9 -0
  33. package/dist/config/idp-better-auth-config.d.ts.map +1 -0
  34. package/dist/config/idp-better-auth-config.js +101 -0
  35. package/dist/config/idp-owox-config.d.ts +195 -0
  36. package/dist/config/idp-owox-config.d.ts.map +1 -0
  37. package/dist/config/idp-owox-config.js +252 -0
  38. package/dist/config/index.d.ts +6 -0
  39. package/dist/config/index.d.ts.map +1 -0
  40. package/dist/config/index.js +5 -0
  41. package/dist/core/constants.d.ts +14 -0
  42. package/dist/core/constants.d.ts.map +1 -0
  43. package/dist/core/constants.js +13 -0
  44. package/dist/core/exceptions.d.ts +27 -0
  45. package/dist/core/exceptions.d.ts.map +1 -0
  46. package/dist/core/exceptions.js +36 -0
  47. package/dist/core/logger.d.ts +17 -0
  48. package/dist/core/logger.d.ts.map +1 -0
  49. package/dist/core/logger.js +66 -0
  50. package/dist/core/pkce.d.ts +21 -0
  51. package/dist/core/pkce.d.ts.map +1 -0
  52. package/dist/core/pkce.js +27 -0
  53. package/dist/facades/owox-token-facade.d.ts +27 -0
  54. package/dist/facades/owox-token-facade.d.ts.map +1 -0
  55. package/dist/facades/owox-token-facade.js +117 -0
  56. package/dist/index.d.ts +13 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.js +13 -0
  59. package/dist/jwt/jwksCache.d.ts +24 -0
  60. package/dist/jwt/jwksCache.d.ts.map +1 -0
  61. package/dist/jwt/jwksCache.js +41 -0
  62. package/dist/jwt/parseToken.d.ts +15 -0
  63. package/dist/jwt/parseToken.d.ts.map +1 -0
  64. package/dist/jwt/parseToken.js +26 -0
  65. package/dist/jwt/verifyJwt.d.ts +13 -0
  66. package/dist/jwt/verifyJwt.d.ts.map +1 -0
  67. package/dist/jwt/verifyJwt.js +23 -0
  68. package/dist/mappers/client-payload-mapper.d.ts +6 -0
  69. package/dist/mappers/client-payload-mapper.d.ts.map +1 -0
  70. package/dist/mappers/client-payload-mapper.js +17 -0
  71. package/dist/mappers/user-info-payload-builder.d.ts +11 -0
  72. package/dist/mappers/user-info-payload-builder.d.ts.map +1 -0
  73. package/dist/mappers/user-info-payload-builder.js +28 -0
  74. package/dist/owox-better-auth-idp.d.ts +40 -0
  75. package/dist/owox-better-auth-idp.d.ts.map +1 -0
  76. package/dist/owox-better-auth-idp.js +239 -0
  77. package/dist/resources/templates/layouts/auth.ejs +29 -0
  78. package/dist/resources/templates/pages/sign-in.ejs +66 -0
  79. package/dist/resources/templates/pages/sign-up.ejs +65 -0
  80. package/dist/resources/templates/partials/brand-panel.ejs +24 -0
  81. package/dist/resources/templates/partials/footer.ejs +10 -0
  82. package/dist/resources/templates/partials/head.ejs +64 -0
  83. package/dist/resources/templates/partials/header.ejs +7 -0
  84. package/dist/services/auth/better-auth-session-service.d.ts +28 -0
  85. package/dist/services/auth/better-auth-session-service.d.ts.map +1 -0
  86. package/dist/services/auth/better-auth-session-service.js +121 -0
  87. package/dist/services/auth/pkce-flow-orchestrator.d.ts +33 -0
  88. package/dist/services/auth/pkce-flow-orchestrator.d.ts.map +1 -0
  89. package/dist/services/auth/pkce-flow-orchestrator.js +134 -0
  90. package/dist/services/auth/platform-auth-flow-client.d.ts +16 -0
  91. package/dist/services/auth/platform-auth-flow-client.d.ts.map +1 -0
  92. package/dist/services/auth/platform-auth-flow-client.js +32 -0
  93. package/dist/services/core/token-service.d.ts +25 -0
  94. package/dist/services/core/token-service.d.ts.map +1 -0
  95. package/dist/services/core/token-service.js +56 -0
  96. package/dist/services/core/user-context-service.d.ts +23 -0
  97. package/dist/services/core/user-context-service.d.ts.map +1 -0
  98. package/dist/services/core/user-context-service.js +54 -0
  99. package/dist/services/middleware/middleware-service.d.ts +19 -0
  100. package/dist/services/middleware/middleware-service.d.ts.map +1 -0
  101. package/dist/services/middleware/middleware-service.js +62 -0
  102. package/dist/services/middleware/request-handler-service.d.ts +18 -0
  103. package/dist/services/middleware/request-handler-service.d.ts.map +1 -0
  104. package/dist/services/middleware/request-handler-service.js +131 -0
  105. package/dist/services/rendering/page-service.d.ts +11 -0
  106. package/dist/services/rendering/page-service.d.ts.map +1 -0
  107. package/dist/services/rendering/page-service.js +26 -0
  108. package/dist/services/rendering/template-service.d.ts +17 -0
  109. package/dist/services/rendering/template-service.d.ts.map +1 -0
  110. package/dist/services/rendering/template-service.js +52 -0
  111. package/dist/social/google-provider.d.ts +35 -0
  112. package/dist/social/google-provider.d.ts.map +1 -0
  113. package/dist/social/google-provider.js +55 -0
  114. package/dist/social/social-provider.d.ts +23 -0
  115. package/dist/social/social-provider.d.ts.map +1 -0
  116. package/dist/social/social-provider.js +1 -0
  117. package/dist/store/database-store-factory.d.ts +8 -0
  118. package/dist/store/database-store-factory.d.ts.map +1 -0
  119. package/dist/store/database-store-factory.js +38 -0
  120. package/dist/store/database-store.d.ts +20 -0
  121. package/dist/store/database-store.d.ts.map +1 -0
  122. package/dist/store/database-store.js +1 -0
  123. package/dist/store/mysql-database-store.d.ts +40 -0
  124. package/dist/store/mysql-database-store.d.ts.map +1 -0
  125. package/dist/store/mysql-database-store.js +213 -0
  126. package/dist/store/sqlite-database-store.d.ts +32 -0
  127. package/dist/store/sqlite-database-store.d.ts.map +1 -0
  128. package/dist/store/sqlite-database-store.js +205 -0
  129. package/dist/store/store-result.d.ts +16 -0
  130. package/dist/store/store-result.d.ts.map +1 -0
  131. package/dist/store/store-result.js +25 -0
  132. package/dist/types/auth-request-context.d.ts +15 -0
  133. package/dist/types/auth-request-context.d.ts.map +1 -0
  134. package/dist/types/auth-request-context.js +12 -0
  135. package/dist/types/auth-session.d.ts +25 -0
  136. package/dist/types/auth-session.d.ts.map +1 -0
  137. package/dist/types/auth-session.js +1 -0
  138. package/dist/types/database-models.d.ts +39 -0
  139. package/dist/types/database-models.d.ts.map +1 -0
  140. package/dist/types/database-models.js +1 -0
  141. package/dist/types/index.d.ts +45 -0
  142. package/dist/types/index.d.ts.map +1 -0
  143. package/dist/types/index.js +2 -0
  144. package/dist/utils/cookie-policy.d.ts +16 -0
  145. package/dist/utils/cookie-policy.d.ts.map +1 -0
  146. package/dist/utils/cookie-policy.js +27 -0
  147. package/dist/utils/platform-redirect-builder.d.ts +29 -0
  148. package/dist/utils/platform-redirect-builder.d.ts.map +1 -0
  149. package/dist/utils/platform-redirect-builder.js +86 -0
  150. package/dist/utils/request-utils.d.ts +87 -0
  151. package/dist/utils/request-utils.d.ts.map +1 -0
  152. package/dist/utils/request-utils.js +171 -0
  153. package/dist/utils/string-utils.d.ts +13 -0
  154. package/dist/utils/string-utils.d.ts.map +1 -0
  155. package/dist/utils/string-utils.js +21 -0
  156. package/package.json +71 -0
@@ -0,0 +1,40 @@
1
+ import type { DatabaseAccount, DatabaseOperationResult, DatabaseUser } from '../types/index.js';
2
+ import type { DatabaseStore } from './database-store.js';
3
+ import { StoreResult } from './store-result.js';
4
+ export interface MysqlConnectionConfig {
5
+ host: string;
6
+ user: string;
7
+ password: string;
8
+ database: string;
9
+ port?: number;
10
+ ssl?: unknown;
11
+ }
12
+ /**
13
+ * MySQL-backed DatabaseStore implementation.
14
+ */
15
+ export declare class MysqlDatabaseStore implements DatabaseStore {
16
+ private readonly config;
17
+ private pool?;
18
+ private readonly logger;
19
+ private authTableReady;
20
+ constructor(config: MysqlConnectionConfig);
21
+ private getPool;
22
+ private toIso;
23
+ private toBoolean;
24
+ private mapUser;
25
+ private mapAccount;
26
+ initialize(): Promise<void>;
27
+ isHealthy(): Promise<boolean>;
28
+ cleanupExpiredSessions(): Promise<DatabaseOperationResult>;
29
+ getUserById(userId: string): Promise<DatabaseUser | null>;
30
+ getUserByEmail(email: string): Promise<DatabaseUser | null>;
31
+ getAccountByUserId(userId: string): Promise<DatabaseAccount | null>;
32
+ saveAuthState(state: string, codeVerifier: string, expiresAt?: Date | null): Promise<void>;
33
+ getAuthState(state: string): Promise<StoreResult>;
34
+ deleteAuthState(state: string): Promise<void>;
35
+ purgeExpiredAuthStates(): Promise<number>;
36
+ shutdown(): Promise<void>;
37
+ getAdapter(): Promise<unknown>;
38
+ private ensureAuthStatesTable;
39
+ }
40
+ //# sourceMappingURL=mysql-database-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql-database-store.d.ts","sourceRoot":"","sources":["../../src/store/mysql-database-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAYhD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IAK1C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,IAAI,CAAC,CAAY;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,cAAc,CAAS;gBAEF,MAAM,EAAE,qBAAqB;YAI5C,OAAO;IA2BrB,OAAO,CAAC,KAAK;IAYb,OAAO,CAAC,SAAS;IAqBjB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,UAAU;IAUZ,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAW7B,sBAAsB,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAS1D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAUzD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAS3D,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAUnE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAe1F,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAwBjD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;IASzC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAYzB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;YAMtB,qBAAqB;CAkBpC"}
@@ -0,0 +1,213 @@
1
+ import { LoggerFactory } from '@owox/internal-helpers';
2
+ import { StoreResult } from './store-result.js';
3
+ /**
4
+ * MySQL-backed DatabaseStore implementation.
5
+ */
6
+ export class MysqlDatabaseStore {
7
+ config;
8
+ pool;
9
+ logger;
10
+ authTableReady = false;
11
+ constructor(config) {
12
+ this.config = config;
13
+ this.logger = LoggerFactory.createNamedLogger('BetterAuthMysqlDatabaseStore');
14
+ }
15
+ async getPool() {
16
+ if (this.pool)
17
+ return this.pool;
18
+ try {
19
+ const mysql = await import('mysql2/promise');
20
+ this.pool = mysql.default.createPool({
21
+ host: this.config.host,
22
+ user: this.config.user,
23
+ password: this.config.password,
24
+ database: this.config.database,
25
+ port: this.config.port || 3306,
26
+ waitForConnections: true,
27
+ connectionLimit: 10,
28
+ queueLimit: 0,
29
+ ssl: this.config.ssl,
30
+ });
31
+ }
32
+ catch (error) {
33
+ this.logger.error('Failed to initialize MySQL pool', { error });
34
+ throw new Error('mysql2 is required for MySQL support. Install it with: npm install mysql2');
35
+ }
36
+ return this.pool;
37
+ }
38
+ toIso(val) {
39
+ if (val == null)
40
+ return undefined;
41
+ if (val instanceof Date)
42
+ return val.toISOString();
43
+ try {
44
+ const parsed = new Date(String(val));
45
+ if (!Number.isNaN(parsed.getTime()))
46
+ return parsed.toISOString();
47
+ }
48
+ catch {
49
+ // ignore parse errors
50
+ }
51
+ return String(val);
52
+ }
53
+ toBoolean(val) {
54
+ if (val == null)
55
+ return undefined;
56
+ if (typeof val === 'boolean')
57
+ return val;
58
+ if (typeof val === 'number')
59
+ return val !== 0;
60
+ if (val instanceof Date)
61
+ return true;
62
+ if (val instanceof Uint8Array) {
63
+ return val.length > 0 ? val[0] !== 0 : undefined;
64
+ }
65
+ if (typeof val === 'string') {
66
+ const normalized = val.trim().toLowerCase();
67
+ if (!normalized)
68
+ return undefined;
69
+ if (['1', 'true', 't', 'yes', 'y'].includes(normalized))
70
+ return true;
71
+ if (['0', 'false', 'f', 'no', 'n'].includes(normalized))
72
+ return false;
73
+ if (!Number.isNaN(Date.parse(normalized)))
74
+ return true;
75
+ }
76
+ return undefined;
77
+ }
78
+ mapUser(row) {
79
+ return {
80
+ id: String(row.id),
81
+ email: String(row.email),
82
+ emailVerified: this.toBoolean(row.emailVerified),
83
+ name: row.name != null ? String(row.name) : undefined,
84
+ image: row.image != null ? String(row.image) : null,
85
+ createdAt: this.toIso(row.createdAt),
86
+ };
87
+ }
88
+ mapAccount(row) {
89
+ return {
90
+ id: String(row.id),
91
+ accountId: String(row.accountId),
92
+ providerId: String(row.providerId),
93
+ userId: String(row.userId),
94
+ createdAt: this.toIso(row.createdAt),
95
+ };
96
+ }
97
+ async initialize() {
98
+ const pool = await this.getPool();
99
+ await this.ensureAuthStatesTable(pool);
100
+ }
101
+ async isHealthy() {
102
+ try {
103
+ const pool = await this.getPool();
104
+ await pool.query('SELECT 1');
105
+ await this.ensureAuthStatesTable(pool);
106
+ return true;
107
+ }
108
+ catch {
109
+ return false;
110
+ }
111
+ }
112
+ async cleanupExpiredSessions() {
113
+ const pool = await this.getPool();
114
+ const [result] = (await pool.execute('DELETE FROM session WHERE expiresAt < NOW()'));
115
+ return { changes: Number(result?.affectedRows ?? 0) };
116
+ }
117
+ async getUserById(userId) {
118
+ const pool = await this.getPool();
119
+ const [rows] = (await pool.execute('SELECT * FROM user WHERE id = ? LIMIT 1', [userId]));
120
+ const row = rows[0];
121
+ return row ? this.mapUser(row) : null;
122
+ }
123
+ async getUserByEmail(email) {
124
+ const pool = await this.getPool();
125
+ const [rows] = (await pool.execute('SELECT * FROM user WHERE LOWER(email) = LOWER(?) LIMIT 1', [
126
+ email,
127
+ ]));
128
+ const row = rows[0];
129
+ return row ? this.mapUser(row) : null;
130
+ }
131
+ async getAccountByUserId(userId) {
132
+ const pool = await this.getPool();
133
+ const [rows] = (await pool.execute('SELECT id, accountId, providerId, userId, createdAt FROM account WHERE userId = ? ORDER BY createdAt DESC LIMIT 1', [userId]));
134
+ const row = rows[0];
135
+ return row ? this.mapAccount(row) : null;
136
+ }
137
+ async saveAuthState(state, codeVerifier, expiresAt) {
138
+ const pool = await this.getPool();
139
+ await this.ensureAuthStatesTable(pool);
140
+ const exp = expiresAt ?? null;
141
+ await pool.execute(`INSERT INTO auth_states (state, code_verifier, expires_at)
142
+ VALUES (?, ?, ?)
143
+ ON DUPLICATE KEY UPDATE
144
+ code_verifier = VALUES(code_verifier),
145
+ expires_at = VALUES(expires_at),
146
+ created_at = CURRENT_TIMESTAMP`, [state, codeVerifier, exp]);
147
+ }
148
+ async getAuthState(state) {
149
+ const pool = await this.getPool();
150
+ await this.ensureAuthStatesTable(pool);
151
+ const [rows] = await pool.execute(`SELECT code_verifier, expires_at FROM auth_states WHERE state = ? LIMIT 1`, [state]);
152
+ const row = Array.isArray(rows) && rows.length > 0
153
+ ? rows[0]
154
+ : null;
155
+ if (!row)
156
+ return StoreResult.notFound();
157
+ const exp = row.expires_at ? new Date(row.expires_at) : null;
158
+ if (exp && exp.getTime() <= Date.now()) {
159
+ await this.deleteAuthState(state);
160
+ return StoreResult.expired();
161
+ }
162
+ await this.deleteAuthState(state);
163
+ return StoreResult.withCode(row.code_verifier);
164
+ }
165
+ async deleteAuthState(state) {
166
+ const pool = await this.getPool();
167
+ await this.ensureAuthStatesTable(pool);
168
+ await pool.execute(`DELETE FROM auth_states WHERE state = ?`, [state]);
169
+ }
170
+ async purgeExpiredAuthStates() {
171
+ const pool = await this.getPool();
172
+ await this.ensureAuthStatesTable(pool);
173
+ const [result] = await pool.execute(`DELETE FROM auth_states WHERE expires_at IS NOT NULL AND expires_at <= CURRENT_TIMESTAMP`);
174
+ return result.affectedRows ?? 0;
175
+ }
176
+ async shutdown() {
177
+ if (this.pool && typeof this.pool.end === 'function') {
178
+ try {
179
+ await this.pool.end();
180
+ }
181
+ catch (error) {
182
+ this.logger.error('Failed to close MySQL pool', { error });
183
+ }
184
+ finally {
185
+ this.pool = undefined;
186
+ }
187
+ }
188
+ }
189
+ async getAdapter() {
190
+ const pool = await this.getPool();
191
+ await this.ensureAuthStatesTable(pool);
192
+ return pool;
193
+ }
194
+ async ensureAuthStatesTable(pool) {
195
+ if (this.authTableReady)
196
+ return;
197
+ await pool.query(`
198
+ CREATE TABLE IF NOT EXISTS auth_states (
199
+ state VARCHAR(255) NOT NULL PRIMARY KEY,
200
+ code_verifier VARCHAR(255) NOT NULL,
201
+ created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
202
+ expires_at TIMESTAMP NULL
203
+ )
204
+ `);
205
+ try {
206
+ await pool.query(`CREATE INDEX idx_auth_states_expires_at ON auth_states (expires_at)`);
207
+ }
208
+ catch {
209
+ // index already exists
210
+ }
211
+ this.authTableReady = true;
212
+ }
213
+ }
@@ -0,0 +1,32 @@
1
+ import type { DatabaseAccount, DatabaseOperationResult, DatabaseUser } from '../types/index.js';
2
+ import type { DatabaseStore } from './database-store.js';
3
+ import { StoreResult } from './store-result.js';
4
+ /**
5
+ * SQLite-backed DatabaseStore implementation.
6
+ */
7
+ export declare class SqliteDatabaseStore implements DatabaseStore {
8
+ private readonly dbPath;
9
+ private db?;
10
+ private authTableReady;
11
+ constructor(dbPath: string);
12
+ connect(): Promise<void>;
13
+ private getDb;
14
+ private toIso;
15
+ private toBoolean;
16
+ private mapUser;
17
+ private mapAccount;
18
+ private ensureAuthStatesTable;
19
+ shutdown(): Promise<void>;
20
+ getAdapter(): Promise<unknown>;
21
+ isHealthy(): Promise<boolean>;
22
+ initialize(): Promise<void>;
23
+ cleanupExpiredSessions(): Promise<DatabaseOperationResult>;
24
+ getUserById(userId: string): Promise<DatabaseUser | null>;
25
+ getUserByEmail(email: string): Promise<DatabaseUser | null>;
26
+ getAccountByUserId(userId: string): Promise<DatabaseAccount | null>;
27
+ saveAuthState(state: string, codeVerifier: string, expiresAt?: Date | null): Promise<void>;
28
+ getAuthState(state: string): Promise<StoreResult>;
29
+ deleteAuthState(state: string): Promise<void>;
30
+ purgeExpiredAuthStates(): Promise<number>;
31
+ }
32
+ //# sourceMappingURL=sqlite-database-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-database-store.d.ts","sourceRoot":"","sources":["../../src/store/sqlite-database-store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAchD;;GAEG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IAI3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,EAAE,CAAC,CAAW;IACtB,OAAO,CAAC,cAAc,CAAS;gBAEF,MAAM,EAAE,MAAM;IAErC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B,OAAO,CAAC,KAAK;IAKb,OAAO,CAAC,KAAK;IAYb,OAAO,CAAC,SAAS;IAqBjB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,UAAU;IAUlB,OAAO,CAAC,qBAAqB;IAmBvB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAUzB,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAM9B,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAW7B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,sBAAsB,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAO1D,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAOzD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAO3D,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IASnE,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB1F,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAmBjD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,sBAAsB,IAAI,OAAO,CAAC,MAAM,CAAC;CAUhD"}
@@ -0,0 +1,205 @@
1
+ import { logger } from '../core/logger.js';
2
+ import { StoreResult } from './store-result.js';
3
+ /**
4
+ * SQLite-backed DatabaseStore implementation.
5
+ */
6
+ export class SqliteDatabaseStore {
7
+ dbPath;
8
+ db;
9
+ authTableReady = false;
10
+ constructor(dbPath) {
11
+ this.dbPath = dbPath;
12
+ }
13
+ async connect() {
14
+ if (this.db)
15
+ return;
16
+ const { default: DatabaseCtor } = await import('better-sqlite3');
17
+ this.db = new DatabaseCtor(this.dbPath, { fileMustExist: false });
18
+ // Ensure sane defaults
19
+ try {
20
+ this.db.pragma?.('journal_mode = WAL');
21
+ }
22
+ catch {
23
+ // noop
24
+ }
25
+ }
26
+ getDb() {
27
+ if (!this.db)
28
+ throw new Error('SqliteDatabaseStore is not connected');
29
+ return this.db;
30
+ }
31
+ toIso(val) {
32
+ if (val == null)
33
+ return undefined;
34
+ if (val instanceof Date)
35
+ return val.toISOString();
36
+ try {
37
+ const parsed = new Date(String(val));
38
+ if (!Number.isNaN(parsed.getTime()))
39
+ return parsed.toISOString();
40
+ }
41
+ catch {
42
+ // ignore parse errors
43
+ }
44
+ return String(val);
45
+ }
46
+ toBoolean(val) {
47
+ if (val == null)
48
+ return undefined;
49
+ if (typeof val === 'boolean')
50
+ return val;
51
+ if (typeof val === 'number')
52
+ return val !== 0;
53
+ if (val instanceof Date)
54
+ return true;
55
+ if (val instanceof Uint8Array) {
56
+ return val.length > 0 ? val[0] !== 0 : undefined;
57
+ }
58
+ if (typeof val === 'string') {
59
+ const normalized = val.trim().toLowerCase();
60
+ if (!normalized)
61
+ return undefined;
62
+ if (['1', 'true', 't', 'yes', 'y'].includes(normalized))
63
+ return true;
64
+ if (['0', 'false', 'f', 'no', 'n'].includes(normalized))
65
+ return false;
66
+ if (!Number.isNaN(Date.parse(normalized)))
67
+ return true;
68
+ }
69
+ return undefined;
70
+ }
71
+ mapUser(row) {
72
+ return {
73
+ id: String(row.id),
74
+ email: String(row.email),
75
+ emailVerified: this.toBoolean(row.emailVerified),
76
+ name: row.name != null ? String(row.name) : undefined,
77
+ image: row.image != null ? String(row.image) : null,
78
+ createdAt: this.toIso(row.createdAt),
79
+ };
80
+ }
81
+ mapAccount(row) {
82
+ return {
83
+ id: String(row.id),
84
+ accountId: String(row.accountId),
85
+ providerId: String(row.providerId),
86
+ userId: String(row.userId),
87
+ createdAt: this.toIso(row.createdAt),
88
+ };
89
+ }
90
+ ensureAuthStatesTable() {
91
+ if (this.authTableReady)
92
+ return;
93
+ const db = this.getDb();
94
+ db.prepare(`CREATE TABLE IF NOT EXISTS auth_states (
95
+ state TEXT NOT NULL PRIMARY KEY,
96
+ code_verifier TEXT NOT NULL,
97
+ created_at TEXT NOT NULL DEFAULT (CURRENT_TIMESTAMP),
98
+ expires_at TEXT NULL
99
+ )`).run();
100
+ try {
101
+ db.prepare(`CREATE INDEX idx_auth_states_expires_at ON auth_states (expires_at)`).run();
102
+ }
103
+ catch {
104
+ // index already exists
105
+ }
106
+ this.authTableReady = true;
107
+ }
108
+ async shutdown() {
109
+ try {
110
+ this.db?.close?.();
111
+ }
112
+ catch (error) {
113
+ logger.error('Failed to close SQLite database', {}, error);
114
+ }
115
+ finally {
116
+ this.db = undefined;
117
+ }
118
+ }
119
+ async getAdapter() {
120
+ await this.connect();
121
+ this.ensureAuthStatesTable();
122
+ return this.getDb();
123
+ }
124
+ async isHealthy() {
125
+ try {
126
+ await this.connect();
127
+ this.ensureAuthStatesTable();
128
+ this.getDb().prepare('SELECT 1').get();
129
+ return true;
130
+ }
131
+ catch {
132
+ return false;
133
+ }
134
+ }
135
+ async initialize() {
136
+ await this.connect();
137
+ this.ensureAuthStatesTable();
138
+ }
139
+ async cleanupExpiredSessions() {
140
+ await this.connect();
141
+ const stmt = this.getDb().prepare('DELETE FROM session WHERE expiresAt < datetime("now")');
142
+ const result = stmt.run();
143
+ return { changes: Number(result.changes ?? 0) };
144
+ }
145
+ async getUserById(userId) {
146
+ await this.connect();
147
+ const stmt = this.getDb().prepare('SELECT * FROM user WHERE id = ?');
148
+ const row = stmt.get(userId);
149
+ return row ? this.mapUser(row) : null;
150
+ }
151
+ async getUserByEmail(email) {
152
+ await this.connect();
153
+ const stmt = this.getDb().prepare('SELECT * FROM user WHERE lower(email) = lower(?)');
154
+ const row = stmt.get(email);
155
+ return row ? this.mapUser(row) : null;
156
+ }
157
+ async getAccountByUserId(userId) {
158
+ await this.connect();
159
+ const stmt = this.getDb().prepare('SELECT id, accountId, providerId, userId, createdAt FROM account WHERE userId = ? ORDER BY createdAt DESC LIMIT 1');
160
+ const row = stmt.get(userId);
161
+ return row ? this.mapAccount(row) : null;
162
+ }
163
+ async saveAuthState(state, codeVerifier, expiresAt) {
164
+ await this.connect();
165
+ this.ensureAuthStatesTable();
166
+ const exp = expiresAt ? expiresAt.toISOString() : null;
167
+ this.getDb()
168
+ .prepare(`INSERT INTO auth_states (state, code_verifier, expires_at)
169
+ VALUES (?, ?, ?)
170
+ ON CONFLICT(state) DO UPDATE SET
171
+ code_verifier = excluded.code_verifier,
172
+ expires_at = excluded.expires_at,
173
+ created_at = CURRENT_TIMESTAMP`)
174
+ .run(state, codeVerifier, exp);
175
+ }
176
+ async getAuthState(state) {
177
+ await this.connect();
178
+ this.ensureAuthStatesTable();
179
+ const row = this.getDb()
180
+ .prepare('SELECT code_verifier, expires_at FROM auth_states WHERE state = ? LIMIT 1')
181
+ .get(state);
182
+ if (!row || !row.code_verifier)
183
+ return StoreResult.notFound();
184
+ const exp = row.expires_at ? new Date(row.expires_at) : null;
185
+ if (exp && exp.getTime() <= Date.now()) {
186
+ await this.deleteAuthState(state);
187
+ return StoreResult.expired();
188
+ }
189
+ await this.deleteAuthState(state);
190
+ return StoreResult.withCode(row.code_verifier);
191
+ }
192
+ async deleteAuthState(state) {
193
+ await this.connect();
194
+ this.ensureAuthStatesTable();
195
+ this.getDb().prepare('DELETE FROM auth_states WHERE state = ?').run(state);
196
+ }
197
+ async purgeExpiredAuthStates() {
198
+ await this.connect();
199
+ this.ensureAuthStatesTable();
200
+ const res = this.getDb()
201
+ .prepare('DELETE FROM auth_states WHERE expires_at IS NOT NULL AND expires_at <= CURRENT_TIMESTAMP')
202
+ .run();
203
+ return Number(res.changes ?? 0);
204
+ }
205
+ }
@@ -0,0 +1,16 @@
1
+ export declare enum StoreReason {
2
+ NOT_FOUND = "not_found",
3
+ EXPIRED = "expired"
4
+ }
5
+ /**
6
+ * Wraps PKCE state lookup results with reason metadata.
7
+ */
8
+ export declare class StoreResult {
9
+ readonly code: string | null;
10
+ readonly reason: StoreReason | null;
11
+ private constructor();
12
+ static withCode(code: string): StoreResult;
13
+ static notFound(): StoreResult;
14
+ static expired(): StoreResult;
15
+ }
16
+ //# sourceMappingURL=store-result.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-result.d.ts","sourceRoot":"","sources":["../../src/store/store-result.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,SAAS,cAAc;IACvB,OAAO,YAAY;CACpB;AAED;;GAEG;AACH,qBAAa,WAAW;aAEJ,IAAI,EAAE,MAAM,GAAG,IAAI;aACnB,MAAM,EAAE,WAAW,GAAG,IAAI;IAF5C,OAAO;IAKP,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAG1C,MAAM,CAAC,QAAQ,IAAI,WAAW;IAG9B,MAAM,CAAC,OAAO,IAAI,WAAW;CAG9B"}
@@ -0,0 +1,25 @@
1
+ export var StoreReason;
2
+ (function (StoreReason) {
3
+ StoreReason["NOT_FOUND"] = "not_found";
4
+ StoreReason["EXPIRED"] = "expired";
5
+ })(StoreReason || (StoreReason = {}));
6
+ /**
7
+ * Wraps PKCE state lookup results with reason metadata.
8
+ */
9
+ export class StoreResult {
10
+ code;
11
+ reason;
12
+ constructor(code, reason) {
13
+ this.code = code;
14
+ this.reason = reason;
15
+ }
16
+ static withCode(code) {
17
+ return new StoreResult(code, null);
18
+ }
19
+ static notFound() {
20
+ return new StoreResult(null, StoreReason.NOT_FOUND);
21
+ }
22
+ static expired() {
23
+ return new StoreResult(null, StoreReason.EXPIRED);
24
+ }
25
+ }
@@ -0,0 +1,15 @@
1
+ import { type Request } from 'express';
2
+ import { type PlatformParams } from '../utils/request-utils.js';
3
+ /**
4
+ * Extracted request context used for auth flow decisions.
5
+ */
6
+ export interface AuthRequestContext {
7
+ state?: string;
8
+ platformParams: PlatformParams;
9
+ refreshToken?: string;
10
+ }
11
+ /**
12
+ * Builds auth request context from query and cookies.
13
+ */
14
+ export declare function buildAuthRequestContext(req: Request): AuthRequestContext;
15
+ //# sourceMappingURL=auth-request-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-request-context.d.ts","sourceRoot":"","sources":["../../src/types/auth-request-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAIL,KAAK,cAAc,EACpB,MAAM,2BAA2B,CAAC;AAEnC;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAOxE"}
@@ -0,0 +1,12 @@
1
+ import { extractPlatformParams, extractRefreshToken, getStateManager, } from '../utils/request-utils.js';
2
+ /**
3
+ * Builds auth request context from query and cookies.
4
+ */
5
+ export function buildAuthRequestContext(req) {
6
+ const stateManager = getStateManager(req);
7
+ return {
8
+ state: stateManager.extract() || undefined,
9
+ platformParams: extractPlatformParams(req),
10
+ refreshToken: extractRefreshToken(req),
11
+ };
12
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Better Auth session data
3
+ */
4
+ export interface AuthSession {
5
+ user: {
6
+ id: string;
7
+ email: string;
8
+ name?: string;
9
+ };
10
+ session: {
11
+ id: string;
12
+ userId: string;
13
+ token: string;
14
+ expiresAt: Date;
15
+ };
16
+ }
17
+ /**
18
+ * Session validation result
19
+ */
20
+ export interface SessionValidationResult {
21
+ isValid: boolean;
22
+ session?: AuthSession;
23
+ error?: string;
24
+ }
25
+ //# sourceMappingURL=auth-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-session.d.ts","sourceRoot":"","sources":["../../src/types/auth-session.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE;QACP,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,IAAI,CAAC;KACjB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Database user model
3
+ */
4
+ export interface DatabaseUser {
5
+ id: string;
6
+ email: string;
7
+ emailVerified?: boolean;
8
+ name?: string;
9
+ image?: string | null;
10
+ createdAt?: string;
11
+ }
12
+ /**
13
+ * Database session model
14
+ */
15
+ export interface DatabaseSession {
16
+ id: string;
17
+ userId: string;
18
+ token: string;
19
+ expiresAt: string;
20
+ createdAt?: string;
21
+ }
22
+ /**
23
+ * Database account model
24
+ */
25
+ export interface DatabaseAccount {
26
+ id: string;
27
+ userId: string;
28
+ providerId: string;
29
+ accountId: string;
30
+ createdAt?: string;
31
+ }
32
+ /**
33
+ * Database operation result
34
+ */
35
+ export interface DatabaseOperationResult {
36
+ changes: number;
37
+ lastInsertRowid?: number;
38
+ }
39
+ //# sourceMappingURL=database-models.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database-models.d.ts","sourceRoot":"","sources":["../../src/types/database-models.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B"}
@@ -0,0 +1 @@
1
+ export {};