@stacksjs/auth 0.70.256 → 0.70.258

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.
@@ -1,5 +1,5 @@
1
1
  import { config } from "@stacksjs/config";
2
- import { db } from "@stacksjs/database";
2
+ import { db, parseSqlDateTime } from "@stacksjs/database";
3
3
  import { HttpError } from "@stacksjs/error-handling";
4
4
  import { formatDate, User } from "@stacksjs/orm";
5
5
  import { getCurrentRequest, request } from "@stacksjs/router";
@@ -110,7 +110,7 @@ export class Auth {
110
110
  name: token.name || "auth-token",
111
111
  scopes: parseScopes(token.scopes),
112
112
  abilities: parseScopes(token.scopes),
113
- expiresAt: token.expires_at ? new Date(String(token.expires_at)) : null,
113
+ expiresAt: parseSqlDateTime(token.expires_at),
114
114
  createdAt: token.created_at ? new Date(String(token.created_at)) : new Date,
115
115
  updatedAt: token.updated_at ? new Date(String(token.updated_at)) : new Date,
116
116
  revoked: !!token.revoked
@@ -243,7 +243,7 @@ export class Auth {
243
243
  if (!accessToken)
244
244
  return !1;
245
245
  log.debug(`[auth] Token validated for token#${accessToken.id}`);
246
- if (accessToken.expires_at && new Date(String(accessToken.expires_at)) < new Date) {
246
+ if (accessToken.expires_at && (parseSqlDateTime(accessToken.expires_at) ?? new Date(0)) < new Date) {
247
247
  await db.deleteFrom("oauth_access_tokens").where("id", "=", accessToken.id).execute();
248
248
  return !1;
249
249
  }
@@ -258,7 +258,7 @@ export class Auth {
258
258
  const hashedPlainToken = hashToken(token), accessToken = await db.selectFrom("oauth_access_tokens").where("token", "=", hashedPlainToken).selectAll().executeTakeFirst();
259
259
  if (!accessToken)
260
260
  return;
261
- if (accessToken.expires_at && new Date(String(accessToken.expires_at)) < new Date) {
261
+ if (accessToken.expires_at && (parseSqlDateTime(accessToken.expires_at) ?? new Date(0)) < new Date) {
262
262
  await db.deleteFrom("oauth_access_tokens").where("id", "=", accessToken.id).execute();
263
263
  return;
264
264
  }
@@ -331,7 +331,7 @@ export class Auth {
331
331
  name: String(token.name || "auth-token"),
332
332
  scopes: parseScopes(String(token.scopes ?? "")),
333
333
  abilities: parseScopes(String(token.scopes ?? "")),
334
- expiresAt: token.expires_at ? new Date(String(token.expires_at)) : null,
334
+ expiresAt: parseSqlDateTime(token.expires_at),
335
335
  createdAt: token.created_at ? new Date(String(token.created_at)) : new Date,
336
336
  updatedAt: token.updated_at ? new Date(String(token.updated_at)) : new Date,
337
337
  revoked: !!token.revoked
@@ -1,7 +1,7 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
3
3
  import { config } from "@stacksjs/config";
4
- import { db } from "@stacksjs/database";
4
+ import { db, sqlDateTime, parseSqlDateTime } from "@stacksjs/database";
5
5
  import { mail, template } from "@stacksjs/email";
6
6
  import { log } from "@stacksjs/logging";
7
7
  function getVerificationKey() {
@@ -42,7 +42,7 @@ export async function sendVerificationEmail(user) {
42
42
  await db.insertInto("email_verifications").values({
43
43
  user_id: user.id,
44
44
  token: hash,
45
- expires_at: expiresAt.toISOString()
45
+ expires_at: sqlDateTime(expiresAt)
46
46
  }).executeTakeFirst();
47
47
  const verificationUrl = getVerificationUrl(user.id, token), appName = config.app.name || "Stacks";
48
48
  try {
@@ -78,14 +78,14 @@ export async function verifyEmail(userId, token) {
78
78
  const record = await db.selectFrom("email_verifications").where("user_id", "=", userId).selectAll().executeTakeFirst();
79
79
  if (!record)
80
80
  return { success: !1, message: "No verification request found. Please request a new verification email." };
81
- const expiresAt = new Date(record.expires_at);
81
+ const expiresAt = parseSqlDateTime(record.expires_at) ?? new Date(Number.NaN);
82
82
  if (Number.isNaN(expiresAt.getTime()) || new Date > expiresAt) {
83
83
  await db.deleteFrom("email_verifications").where("user_id", "=", userId).execute();
84
84
  return { success: !1, message: "Verification link has expired. Please request a new one." };
85
85
  }
86
86
  if (!verifyToken(userId, token, record.token))
87
87
  return { success: !1, message: "Invalid verification link." };
88
- await db.updateTable("users").set({ email_verified_at: new Date().toISOString() }).where("id", "=", userId).executeTakeFirst();
88
+ await db.updateTable("users").set({ email_verified_at: sqlDateTime() }).where("id", "=", userId).executeTakeFirst();
89
89
  await db.deleteFrom("email_verifications").where("user_id", "=", userId).execute();
90
90
  return { success: !0, message: "Email verified successfully." };
91
91
  }
package/dist/passkey.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { db } from "@stacksjs/database";
2
+ import { db, sqlDateTime, parseSqlDateTime } from "@stacksjs/database";
3
3
  import { User } from "@stacksjs/orm";
4
4
  export {
5
5
  generateRegistrationOptions,
@@ -55,7 +55,7 @@ function formatDateTime() {
55
55
  }
56
56
  const DEFAULT_CHALLENGE_TTL_SECONDS = 300;
57
57
  export async function storeWebAuthnChallenge(userId, challenge, purpose, ttlSeconds = DEFAULT_CHALLENGE_TTL_SECONDS) {
58
- const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString(), encodedChallenge = typeof challenge === "string" ? challenge : Buffer.from(challenge).toString("base64url");
58
+ const expiresAt = sqlDateTime(new Date(Date.now() + ttlSeconds * 1000)), encodedChallenge = typeof challenge === "string" ? challenge : Buffer.from(challenge).toString("base64url");
59
59
  await db.deleteFrom("webauthn_challenges").where("user_id", "=", userId).where("purpose", "=", purpose).execute();
60
60
  await db.insertInto("webauthn_challenges").values({
61
61
  user_id: userId,
@@ -69,7 +69,7 @@ export async function consumeWebAuthnChallenge(userId, purpose) {
69
69
  if (!row)
70
70
  return null;
71
71
  await db.deleteFrom("webauthn_challenges").where("user_id", "=", userId).where("purpose", "=", purpose).execute();
72
- const expiresAt = row.expires_at ? new Date(String(row.expires_at)).getTime() : 0;
72
+ const expiresAt = parseSqlDateTime(row.expires_at)?.getTime() ?? 0;
73
73
  if (Date.now() > expiresAt)
74
74
  return null;
75
75
  return Buffer.from(String(row.challenge), "base64url");
@@ -1,6 +1,6 @@
1
1
  import { randomBytes } from "node:crypto";
2
2
  import { config } from "@stacksjs/config";
3
- import { db } from "@stacksjs/database";
3
+ import { db, sqlDateTime } from "@stacksjs/database";
4
4
  import { mail, template } from "@stacksjs/email";
5
5
  import { log } from "@stacksjs/logging";
6
6
  import { formatDate } from "@stacksjs/orm";
@@ -57,7 +57,7 @@ export function passwordResets(email) {
57
57
  return randomBytes(32).toString("hex");
58
58
  }
59
59
  async function createResetToken() {
60
- const token = generateResetToken(), hashedToken = await makeHash(token, { algorithm: "bcrypt" }), expireMinutes = getTokenExpireMinutes(), expiresAt = new Date(Date.now() + expireMinutes * 60000).toISOString();
60
+ const token = generateResetToken(), hashedToken = await makeHash(token, { algorithm: "bcrypt" }), expireMinutes = getTokenExpireMinutes(), expiresAt = sqlDateTime(new Date(Date.now() + expireMinutes * 60000));
61
61
  await db.deleteFrom("password_resets").where("email", "=", email).execute();
62
62
  await db.insertInto("password_resets").values({
63
63
  email,
@@ -3,7 +3,7 @@ import { log } from "@stacksjs/logging";
3
3
  import { User } from "@stacksjs/orm";
4
4
  import { verifyHash } from "@stacksjs/security";
5
5
  import { config } from "@stacksjs/config";
6
- import { db } from "@stacksjs/database";
6
+ import { db, sqlDateTime, parseSqlDateTime } from "@stacksjs/database";
7
7
  import { getCurrentRequest } from "@stacksjs/router";
8
8
  import { DUMMY_BCRYPT_HASH } from "./internal-constants";
9
9
  import { RateLimiter } from "./rate-limiter";
@@ -57,7 +57,7 @@ export async function sessionLogin(email, password, fingerprint) {
57
57
  user_agent: userAgent,
58
58
  payload: "{}",
59
59
  last_activity: Math.floor(Date.now() / 1000),
60
- expires_at: expiresAt.toISOString()
60
+ expires_at: sqlDateTime(expiresAt)
61
61
  }).execute();
62
62
  } catch (err) {
63
63
  log.error(`[auth] Session persistence failed for user#${user.id}: ${err.message}`);
@@ -88,7 +88,7 @@ export async function sessionUser(sessionId) {
88
88
  const session = await db.selectFrom("sessions").where("id", "=", sessionId).selectAll().executeTakeFirst();
89
89
  if (!session)
90
90
  return;
91
- const expiresAt = session.expires_at ? new Date(String(session.expires_at)).getTime() : 0;
91
+ const expiresAt = parseSqlDateTime(session.expires_at)?.getTime() ?? 0;
92
92
  if (Date.now() > expiresAt) {
93
93
  await db.deleteFrom("sessions").where("id", "=", sessionId).execute();
94
94
  return;
@@ -107,7 +107,7 @@ export async function sessionCheck(sessionId) {
107
107
  const session = await db.selectFrom("sessions").where("id", "=", sessionId).selectAll().executeTakeFirst();
108
108
  if (!session)
109
109
  return !1;
110
- const expiresAt = session.expires_at ? new Date(String(session.expires_at)).getTime() : 0;
110
+ const expiresAt = parseSqlDateTime(session.expires_at)?.getTime() ?? 0;
111
111
  if (Date.now() > expiresAt) {
112
112
  await db.deleteFrom("sessions").where("id", "=", sessionId).execute();
113
113
  return !1;
@@ -124,7 +124,7 @@ export async function sessionRefresh(sessionId, ttlMs = 86400000) {
124
124
  const session = await db.selectFrom("sessions").where("id", "=", sessionId).selectAll().executeTakeFirst();
125
125
  if (!session)
126
126
  return !1;
127
- const expiresAt = session.expires_at ? new Date(String(session.expires_at)).getTime() : 0;
127
+ const expiresAt = parseSqlDateTime(session.expires_at)?.getTime() ?? 0;
128
128
  if (Date.now() > expiresAt) {
129
129
  await db.deleteFrom("sessions").where("id", "=", sessionId).execute();
130
130
  return !1;
@@ -133,7 +133,7 @@ export async function sessionRefresh(sessionId, ttlMs = 86400000) {
133
133
  return !1;
134
134
  const newExpiry = new Date(Date.now() + ttlMs);
135
135
  await db.updateTable("sessions").set({
136
- expires_at: newExpiry.toISOString(),
136
+ expires_at: sqlDateTime(newExpiry),
137
137
  last_activity: Math.floor(Date.now() / 1000)
138
138
  }).where("id", "=", sessionId).execute();
139
139
  return !0;
package/dist/tokens.d.ts CHANGED
@@ -273,7 +273,7 @@ export declare function parseScopes(scopes: string | string[] | null | undefined
273
273
  /** Current database driver */
274
274
  declare const dbDriver: DatabaseDriver;
275
275
  /** Cross-database SQL helpers */
276
- declare const sql: unknown;
276
+ declare const sql: ReturnType<typeof sqlHelpers>;
277
277
  /**
278
278
  * Alias for currentAccessToken
279
279
  *
package/dist/tokens.js CHANGED
@@ -4,8 +4,11 @@ import { db } from "@stacksjs/database";
4
4
  import { HttpError } from "@stacksjs/error-handling";
5
5
  import { getCurrentRequest } from "@stacksjs/router";
6
6
  import { env } from "@stacksjs/env";
7
- import { sqlHelpers } from "@stacksjs/database";
7
+ import { parseSqlDateTime, sqlDateTime, sqlDateTimeLiteral, sqlHelpers } from "@stacksjs/database";
8
8
  const dbDriver = env.DB_CONNECTION || "sqlite", sql = sqlHelpers(dbDriver), { isPostgres, isMysql, now, boolTrue, boolFalse } = sql;
9
+ function appNow() {
10
+ return sqlDateTimeLiteral();
11
+ }
9
12
  function param(index) {
10
13
  return sql.param(index);
11
14
  }
@@ -28,8 +31,7 @@ export async function getPasswordChangedAt(userId, q = db) {
28
31
  `, [userId]))[0]?.password_changed_at;
29
32
  if (value === null || value === void 0)
30
33
  return null;
31
- const parsed = new Date(String(value));
32
- return Number.isNaN(parsed.getTime()) ? null : parsed;
34
+ return parseSqlDateTime(value);
33
35
  } catch {
34
36
  return null;
35
37
  }
@@ -39,8 +41,8 @@ export function isIssuedBeforePasswordChange(createdAt, changedAt) {
39
41
  return !1;
40
42
  if (createdAt === null || createdAt === void 0)
41
43
  return !1;
42
- const created = new Date(String(createdAt));
43
- if (Number.isNaN(created.getTime()))
44
+ const created = parseSqlDateTime(createdAt);
45
+ if (!created)
44
46
  return !1;
45
47
  return created.getTime() < changedAt.getTime();
46
48
  }
@@ -69,7 +71,7 @@ export async function findToken(plainTextToken) {
69
71
  SELECT * FROM oauth_access_tokens
70
72
  WHERE token = ${param(1)}
71
73
  AND revoked = ${boolFalse}
72
- AND (expires_at IS NULL OR expires_at > ${now})
74
+ AND (expires_at IS NULL OR expires_at > ${appNow()})
73
75
  LIMIT 1
74
76
  `, [hashedToken]))[0];
75
77
  if (!row)
@@ -149,13 +151,13 @@ export async function createToken(userId, name = "access-token", scopes = ["*"],
149
151
  if (isPostgres)
150
152
  await db.unsafe(`
151
153
  INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
152
- VALUES ($1, $2, $3, $4, $5, false, $6, NOW(), NOW())
153
- `, [userId, client.id, hashedToken, name, JSON.stringify(scopes), expiresAt.toISOString()]);
154
+ VALUES ($1, $2, $3, $4, $5, false, $6, ${appNow()}, ${appNow()})
155
+ `, [userId, client.id, hashedToken, name, JSON.stringify(scopes), sqlDateTime(expiresAt)]);
154
156
  else
155
157
  await db.unsafe(`
156
158
  INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
157
- VALUES (?, ?, ?, ?, ?, 0, ?, ${now}, ${now})
158
- `, [userId, client.id, hashedToken, name, JSON.stringify(scopes), expiresAt.toISOString()]);
159
+ VALUES (?, ?, ?, ?, ?, 0, ?, ${appNow()}, ${appNow()})
160
+ `, [userId, client.id, hashedToken, name, JSON.stringify(scopes), sqlDateTime(expiresAt)]);
159
161
  const row = (await db.unsafe(`
160
162
  SELECT * FROM oauth_access_tokens WHERE token = ${param(1)} LIMIT 1
161
163
  `, [hashedToken]))[0];
@@ -180,13 +182,13 @@ export async function createToken(userId, name = "access-token", scopes = ["*"],
180
182
  if (isPostgres)
181
183
  await db.unsafe(`
182
184
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
183
- VALUES ($1, $2, false, $3, NOW())
184
- `, [accessToken.id, hashedRefreshToken, refreshExpiresAt.toISOString()]);
185
+ VALUES ($1, $2, false, $3, ${appNow()})
186
+ `, [accessToken.id, hashedRefreshToken, sqlDateTime(refreshExpiresAt)]);
185
187
  else
186
188
  await db.unsafe(`
187
189
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
188
- VALUES (?, ?, 0, ?, ${now})
189
- `, [accessToken.id, hashedRefreshToken, refreshExpiresAt.toISOString()]);
190
+ VALUES (?, ?, 0, ?, ${appNow()})
191
+ `, [accessToken.id, hashedRefreshToken, sqlDateTime(refreshExpiresAt)]);
190
192
  }
191
193
  return {
192
194
  accessToken,
@@ -207,7 +209,7 @@ export async function refreshToken(refreshTokenPlain, options = {}) {
207
209
  JOIN oauth_access_tokens t ON r.access_token_id = t.id
208
210
  WHERE r.token = ${param(1)}
209
211
  AND r.revoked = ${boolFalse}
210
- AND (r.expires_at IS NULL OR r.expires_at > ${now})
212
+ AND (r.expires_at IS NULL OR r.expires_at > ${appNow()})
211
213
  LIMIT 1${forUpdate}
212
214
  `, [hashedRefreshToken]))[0];
213
215
  if (!refreshRow)
@@ -229,13 +231,13 @@ export async function refreshToken(refreshTokenPlain, options = {}) {
229
231
  if (isPostgres)
230
232
  await trx.unsafe(`
231
233
  INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
232
- VALUES ($1, $2, $3, $4, $5, false, $6, NOW(), NOW())
233
- `, [refreshRow.user_id, refreshRow.oauth_client_id, hashedToken, refreshRow.name, refreshRow.scopes, expiresAt.toISOString()]);
234
+ VALUES ($1, $2, $3, $4, $5, false, $6, ${appNow()}, ${appNow()})
235
+ `, [refreshRow.user_id, refreshRow.oauth_client_id, hashedToken, refreshRow.name, refreshRow.scopes, sqlDateTime(expiresAt)]);
234
236
  else
235
237
  await trx.unsafe(`
236
238
  INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
237
- VALUES (?, ?, ?, ?, ?, 0, ?, ${now}, ${now})
238
- `, [refreshRow.user_id, refreshRow.oauth_client_id, hashedToken, refreshRow.name, refreshRow.scopes, expiresAt.toISOString()]);
239
+ VALUES (?, ?, ?, ?, ?, 0, ?, ${appNow()}, ${appNow()})
240
+ `, [refreshRow.user_id, refreshRow.oauth_client_id, hashedToken, refreshRow.name, refreshRow.scopes, sqlDateTime(expiresAt)]);
239
241
  const row = (await trx.unsafe(`
240
242
  SELECT * FROM oauth_access_tokens WHERE token = ${param(1)} LIMIT 1
241
243
  `, [hashedToken]))[0], accessToken = {
@@ -256,13 +258,13 @@ export async function refreshToken(refreshTokenPlain, options = {}) {
256
258
  if (isPostgres)
257
259
  await trx.unsafe(`
258
260
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
259
- VALUES ($1, $2, false, $3, NOW())
260
- `, [accessToken.id, newHashedRefreshToken, refreshExpiresAt.toISOString()]);
261
+ VALUES ($1, $2, false, $3, ${appNow()})
262
+ `, [accessToken.id, newHashedRefreshToken, sqlDateTime(refreshExpiresAt)]);
261
263
  else
262
264
  await trx.unsafe(`
263
265
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
264
- VALUES (?, ?, 0, ?, ${now})
265
- `, [accessToken.id, newHashedRefreshToken, refreshExpiresAt.toISOString()]);
266
+ VALUES (?, ?, 0, ?, ${appNow()})
267
+ `, [accessToken.id, newHashedRefreshToken, sqlDateTime(refreshExpiresAt)]);
266
268
  return {
267
269
  accessToken,
268
270
  plainTextToken,
@@ -277,7 +279,7 @@ export async function validateRefreshToken(refreshTokenPlain) {
277
279
  SELECT id FROM oauth_refresh_tokens
278
280
  WHERE token = ${param(1)}
279
281
  AND revoked = ${boolFalse}
280
- AND (expires_at IS NULL OR expires_at > ${now})
282
+ AND (expires_at IS NULL OR expires_at > ${appNow()})
281
283
  LIMIT 1
282
284
  `, [hashedRefreshToken])).length > 0;
283
285
  }
@@ -301,7 +303,7 @@ export async function revokeAllRefreshTokens(userId) {
301
303
  export async function deleteExpiredRefreshTokens() {
302
304
  const result = await db.unsafe(`
303
305
  DELETE FROM oauth_refresh_tokens
304
- WHERE expires_at < ${now}
306
+ WHERE expires_at < ${appNow()}
305
307
  `);
306
308
  return result?.changes || result?.rowCount || 0;
307
309
  }
@@ -311,7 +313,7 @@ export async function deleteRevokedRefreshTokens(daysOld = 7) {
311
313
  const result = await db.unsafe(`
312
314
  DELETE FROM oauth_refresh_tokens
313
315
  WHERE revoked = ${boolTrue} AND created_at < ${param(1)}
314
- `, [cutoffDate.toISOString()]);
316
+ `, [sqlDateTime(cutoffDate)]);
315
317
  return result?.changes || result?.rowCount || 0;
316
318
  }
317
319
  export async function revokeToken(plainTextToken) {
@@ -325,7 +327,7 @@ export async function revokeToken(plainTextToken) {
325
327
  `, [hashedToken]);
326
328
  await db.unsafe(`
327
329
  UPDATE oauth_access_tokens
328
- SET revoked = ${boolTrue}, updated_at = ${now}
330
+ SET revoked = ${boolTrue}, updated_at = ${appNow()}
329
331
  WHERE token = ${param(1)}
330
332
  `, [hashedToken]);
331
333
  }
@@ -337,7 +339,7 @@ export async function revokeTokenById(tokenId) {
337
339
  `, [tokenId]);
338
340
  await db.unsafe(`
339
341
  UPDATE oauth_access_tokens
340
- SET revoked = ${boolTrue}, updated_at = ${now}
342
+ SET revoked = ${boolTrue}, updated_at = ${appNow()}
341
343
  WHERE id = ${param(1)}
342
344
  `, [tokenId]);
343
345
  }
@@ -345,7 +347,7 @@ export async function revokeAllTokens(userId) {
345
347
  await revokeAllRefreshTokens(userId);
346
348
  await db.unsafe(`
347
349
  UPDATE oauth_access_tokens
348
- SET revoked = ${boolTrue}, updated_at = ${now}
350
+ SET revoked = ${boolTrue}, updated_at = ${appNow()}
349
351
  WHERE user_id = ${param(1)}
350
352
  `, [userId]);
351
353
  }
@@ -363,7 +365,7 @@ export async function revokeOtherTokens(userId) {
363
365
  `, [userId, current.id]);
364
366
  await db.unsafe(`
365
367
  UPDATE oauth_access_tokens
366
- SET revoked = true, updated_at = NOW()
368
+ SET revoked = true, updated_at = ${appNow()}
367
369
  WHERE user_id = $1 AND id != $2
368
370
  `, [userId, current.id]);
369
371
  } else {
@@ -376,7 +378,7 @@ export async function revokeOtherTokens(userId) {
376
378
  `, [userId, current.id]);
377
379
  await db.unsafe(`
378
380
  UPDATE oauth_access_tokens
379
- SET revoked = 1, updated_at = ${now}
381
+ SET revoked = 1, updated_at = ${appNow()}
380
382
  WHERE user_id = ? AND id != ?
381
383
  `, [userId, current.id]);
382
384
  }
@@ -385,12 +387,12 @@ export async function deleteExpiredTokens() {
385
387
  await db.unsafe(`
386
388
  DELETE FROM oauth_refresh_tokens
387
389
  WHERE access_token_id IN (
388
- SELECT id FROM oauth_access_tokens WHERE expires_at < ${now}
390
+ SELECT id FROM oauth_access_tokens WHERE expires_at < ${appNow()}
389
391
  )
390
392
  `);
391
393
  const result = await db.unsafe(`
392
394
  DELETE FROM oauth_access_tokens
393
- WHERE expires_at < ${now}
395
+ WHERE expires_at < ${appNow()}
394
396
  `);
395
397
  return result?.changes || result?.rowCount || 0;
396
398
  }
@@ -402,11 +404,11 @@ export async function deleteRevokedTokens(daysOld = 7) {
402
404
  WHERE access_token_id IN (
403
405
  SELECT id FROM oauth_access_tokens WHERE revoked = ${boolTrue} AND updated_at < ${param(1)}
404
406
  )
405
- `, [cutoffDate.toISOString()]);
407
+ `, [sqlDateTime(cutoffDate)]);
406
408
  const result = await db.unsafe(`
407
409
  DELETE FROM oauth_access_tokens
408
410
  WHERE revoked = ${boolTrue} AND updated_at < ${param(1)}
409
- `, [cutoffDate.toISOString()]);
411
+ `, [sqlDateTime(cutoffDate)]);
410
412
  return result?.changes || result?.rowCount || 0;
411
413
  }
412
414
  export async function clients(userId) {
@@ -427,7 +429,7 @@ export async function createClient(options) {
427
429
  if (isPostgres)
428
430
  await db.unsafe(`
429
431
  INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
430
- VALUES ($1, $2, 'local', $3, $4, $5, false, NOW())
432
+ VALUES ($1, $2, 'local', $3, $4, $5, false, ${appNow()})
431
433
  `, [
432
434
  options.name,
433
435
  secret,
@@ -438,7 +440,7 @@ export async function createClient(options) {
438
440
  else
439
441
  await db.unsafe(`
440
442
  INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
441
- VALUES (?, ?, 'local', ?, ?, ?, 0, ${now})
443
+ VALUES (?, ?, 'local', ?, ?, ?, 0, ${appNow()})
442
444
  `, [
443
445
  options.name,
444
446
  secret,
@@ -457,7 +459,7 @@ export async function createClient(options) {
457
459
  export async function revokeClient(clientId) {
458
460
  await db.unsafe(`
459
461
  UPDATE oauth_clients
460
- SET revoked = ${boolTrue}, updated_at = ${now}
462
+ SET revoked = ${boolTrue}, updated_at = ${appNow()}
461
463
  WHERE id = ${param(1)}
462
464
  `, [clientId]);
463
465
  }
@@ -1,5 +1,5 @@
1
1
  import { randomBytes } from "node:crypto";
2
- import { db } from "@stacksjs/database";
2
+ import { db, sqlDateTime, parseSqlDateTime } from "@stacksjs/database";
3
3
  import { generateTwoFactorSecret, generateTwoFactorUri, verifyTwoFactorCode } from "./authenticator";
4
4
  import { RateLimiter } from "./rate-limiter";
5
5
  const DEFAULT_CHALLENGE_TTL_SECONDS = 300, TWO_FACTOR_RATE_LIMIT_PREFIX = "2fa:", TWO_FACTOR_STEP_SECONDS = 30;
@@ -35,7 +35,7 @@ export function generateTwoFactorSetup(email, serviceName) {
35
35
  }
36
36
  const PENDING_SECRET_TTL_SECONDS = 600;
37
37
  export async function stashPendingTwoFactorSecret(userId, secret, ttlSeconds = PENDING_SECRET_TTL_SECONDS) {
38
- const expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
38
+ const expiresAt = sqlDateTime(new Date(Date.now() + ttlSeconds * 1000));
39
39
  await db.deleteFrom("two_factor_pending_secrets").where("user_id", "=", userId).execute();
40
40
  await db.insertInto("two_factor_pending_secrets").values({
41
41
  user_id: userId,
@@ -48,7 +48,7 @@ export async function consumePendingTwoFactorSecret(userId) {
48
48
  if (!row)
49
49
  return null;
50
50
  await db.deleteFrom("two_factor_pending_secrets").where("user_id", "=", userId).execute();
51
- const expiresAt = row.expires_at ? new Date(String(row.expires_at)).getTime() : 0;
51
+ const expiresAt = parseSqlDateTime(row.expires_at)?.getTime() ?? 0;
52
52
  if (Date.now() > expiresAt)
53
53
  return null;
54
54
  return String(row.secret);
@@ -80,7 +80,7 @@ export async function verifyTwoFactorLoginCode(userId, code) {
80
80
  return !0;
81
81
  }
82
82
  export async function createTwoFactorChallenge(userId, ttlSeconds = DEFAULT_CHALLENGE_TTL_SECONDS) {
83
- const id = randomBytes(32).toString("hex"), expiresAt = new Date(Date.now() + ttlSeconds * 1000).toISOString();
83
+ const id = randomBytes(32).toString("hex"), expiresAt = sqlDateTime(new Date(Date.now() + ttlSeconds * 1000));
84
84
  await db.deleteFrom("two_factor_challenges").where("user_id", "=", userId).execute();
85
85
  await db.insertInto("two_factor_challenges").values({
86
86
  id,
@@ -94,7 +94,7 @@ export async function consumeTwoFactorChallenge(challengeToken) {
94
94
  if (!row)
95
95
  return null;
96
96
  await db.deleteFrom("two_factor_challenges").where("id", "=", challengeToken).execute();
97
- const expiresAt = row.expires_at ? new Date(String(row.expires_at)).getTime() : 0;
97
+ const expiresAt = parseSqlDateTime(row.expires_at)?.getTime() ?? 0;
98
98
  if (Date.now() > expiresAt)
99
99
  return null;
100
100
  return Number(row.user_id);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/auth",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.70.256",
5
+ "version": "0.70.258",
6
6
  "description": "A more simplistic way to authenticate.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -54,7 +54,7 @@
54
54
  },
55
55
  "devDependencies": {
56
56
  "better-dx": "^0.2.17",
57
- "@stacksjs/error-handling": "0.70.256",
58
- "@stacksjs/router": "0.70.256"
57
+ "@stacksjs/error-handling": "0.70.258",
58
+ "@stacksjs/router": "0.70.258"
59
59
  }
60
60
  }