@stacksjs/auth 0.72.103 → 0.73.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.
@@ -1,3 +1,4 @@
1
+ export declare function requestToken(request: TokenBearingRequest | null | undefined): string | null;
1
2
  /**
2
3
  * The access token a request carries, from the Authorization header or the
3
4
  * auth cookie.
@@ -13,4 +14,16 @@
13
14
  *
14
15
  * The header is checked first, so an API client behaves exactly as before.
15
16
  */
16
- export declare function requestToken(request: any): string | null;
17
+ /**
18
+ * What this needs off a request, which is deliberately little.
19
+ *
20
+ * Both members are optional because the function is handed two different
21
+ * shapes: a Stacks request, which answers `bearerToken()`, and a plain
22
+ * `Request`, which only has headers. Written out rather than left as `any` so
23
+ * the optional chaining below is checked against something - as `any` it was
24
+ * indistinguishable from probing for members that do not exist on either.
25
+ */
26
+ export declare interface TokenBearingRequest {
27
+ bearerToken?: () => string | undefined | null
28
+ headers?: Headers
29
+ }
@@ -1 +1 @@
1
- import{authCookieToken}from"./cookie";export function requestToken(request){let token=request?.bearerToken?.();if(!token){const header=request?.headers?.get?.("authorization")||request?.headers?.get?.("Authorization");if(typeof header==="string"&&header.startsWith("Bearer "))token=header.substring(7)}if(!token&&request?.headers)token=authCookieToken(request);return token||null}
1
+ import{authCookieToken}from"./cookie";export function requestToken(request){let token=request?.bearerToken?.();if(!token){const header=request?.headers?.get?.("authorization")||request?.headers?.get?.("Authorization");if(typeof header==="string"&&header.startsWith("Bearer "))token=header.substring(7)}if(!token&&request?.headers)token=authCookieToken({headers:request.headers});return token||null}
package/dist/tokens.d.ts CHANGED
@@ -1,19 +1,4 @@
1
1
  import type { AccessToken, CreateClientOptions, CreateClientResult, DatabaseDriver, OAuthClient, PersonalAccessTokenResult, RefreshTokenResult, TokenScopes } from '@stacksjs/types';
2
- /**
3
- * Read `users.password_changed_at` for a user.
4
- *
5
- * Binds a token's validity to the account's credential state: a token
6
- * issued before the user last changed their password is no longer
7
- * trusted, regardless of its own `revoked`/`expires_at` flags. This is
8
- * the durable, use-time backstop behind the post-reset revocation sweep
9
- * (#1947) — even a freshly minted pair that the sweep never saw is
10
- * rejected on first use.
11
- *
12
- * Returns `null` on ANY error (missing column / missing table) so a
13
- * not-yet-migrated database degrades to legacy-allow rather than locking
14
- * everyone out. Accepts an optional query runner so the refresh exchange
15
- * can read the stamp inside its own transaction.
16
- */
17
2
  export declare function getPasswordChangedAt(userId: unknown, q?: { unsafe: (sql: string, params?: any[]) => any }): Promise<Date | null>;
18
3
  /**
19
4
  * True when a credential issued at `createdAt` predates the user's last
package/dist/tokens.js CHANGED
@@ -23,7 +23,7 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
23
23
  VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?, ${appNow()}, ${appNow()})
24
24
  `,[userId,client.id,hashedToken,name,JSON.stringify(scopes),sqlDateTime(expiresAt),agent,address]);const row=(await db.unsafe(`
25
25
  SELECT * FROM oauth_access_tokens WHERE token = ${param(1)} LIMIT 1
26
- `,[hashedToken]))[0];if(!row)throw new HttpError(500,"Failed to create access token - inserted row not found");const accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name,scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:new Date(row.updated_at)};let refreshTokenPlain;if(withRefreshToken){refreshTokenPlain=generateSecureToken(40);const hashedRefreshToken=hashToken(refreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await db.unsafe(`
26
+ `,[hashedToken]))[0];if(!row)throw new HttpError(500,"Failed to create access token - inserted row not found");const accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name,scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:row.updated_at?new Date(row.updated_at):new Date(row.created_at)};let refreshTokenPlain;if(withRefreshToken){refreshTokenPlain=generateSecureToken(40);const hashedRefreshToken=hashToken(refreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await db.unsafe(`
27
27
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
28
28
  VALUES ($1, $2, false, $3, ${appNow()})
29
29
  `,[accessToken.id,hashedRefreshToken,sqlDateTime(refreshExpiresAt)]);else await db.unsafe(`
@@ -53,7 +53,7 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
53
53
  VALUES (?, ?, ?, ?, ?, 0, ?, ${appNow()}, ${appNow()})
54
54
  `,[refreshRow.user_id,refreshRow.oauth_client_id,hashedToken,refreshRow.name,refreshRow.scopes,sqlDateTime(expiresAt)]);const row=(await trx.unsafe(`
55
55
  SELECT * FROM oauth_access_tokens WHERE token = ${param(1)} LIMIT 1
56
- `,[hashedToken]))[0],accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name,scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:new Date(row.updated_at)};if(isIssuedBeforePasswordChange(row.created_at,await getPasswordChangedAt(refreshRow.user_id,trx)))throw new HttpError(401,"Invalid or expired refresh token");const newRefreshTokenPlain=generateSecureToken(40),newHashedRefreshToken=hashToken(newRefreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await trx.unsafe(`
56
+ `,[hashedToken]))[0];if(!row)throw new HttpError(500,"Failed to read back the access token that was just created.");const accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name||"access-token",scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:row.updated_at?new Date(row.updated_at):new Date(row.created_at)};if(isIssuedBeforePasswordChange(row.created_at,await getPasswordChangedAt(refreshRow.user_id,trx)))throw new HttpError(401,"Invalid or expired refresh token");const newRefreshTokenPlain=generateSecureToken(40),newHashedRefreshToken=hashToken(newRefreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await trx.unsafe(`
57
57
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
58
58
  VALUES ($1, $2, false, $3, ${appNow()})
59
59
  `,[accessToken.id,newHashedRefreshToken,sqlDateTime(refreshExpiresAt)]);else await trx.unsafe(`
@@ -75,13 +75,13 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
75
75
  WHERE access_token_id IN (
76
76
  SELECT id FROM oauth_access_tokens WHERE user_id = ${param(1)}
77
77
  )
78
- `,[userId])}export async function deleteExpiredRefreshTokens(){const result=await db.unsafe(`
78
+ `,[userId])}export async function deleteExpiredRefreshTokens(){const written=await db.unsafe(`
79
79
  DELETE FROM oauth_refresh_tokens
80
80
  WHERE expires_at < ${appNow()}
81
- `);return result?.changes||result?.rowCount||0}export async function deleteRevokedRefreshTokens(daysOld=7){const cutoffDate=new Date;cutoffDate.setDate(cutoffDate.getDate()-daysOld);const result=await db.unsafe(`
81
+ `);return Number(written?.changes??written?.rowCount??0)}export async function deleteRevokedRefreshTokens(daysOld=7){const cutoffDate=new Date;cutoffDate.setDate(cutoffDate.getDate()-daysOld);const written=await db.unsafe(`
82
82
  DELETE FROM oauth_refresh_tokens
83
83
  WHERE revoked = ${boolTrue} AND created_at < ${param(1)}
84
- `,[sqlDateTime(cutoffDate)]);return result?.changes||result?.rowCount||0}export async function revokeToken(plainTextToken){const hashedToken=bearerLookupHash(plainTextToken);await db.unsafe(`
84
+ `,[sqlDateTime(cutoffDate)]);return Number(written?.changes??written?.rowCount??0)}export async function revokeToken(plainTextToken){const hashedToken=bearerLookupHash(plainTextToken);await db.unsafe(`
85
85
  UPDATE oauth_refresh_tokens
86
86
  SET revoked = ${boolTrue}
87
87
  WHERE access_token_id IN (
@@ -128,18 +128,18 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
128
128
  WHERE access_token_id IN (
129
129
  SELECT id FROM oauth_access_tokens WHERE expires_at < ${appNow()}
130
130
  )
131
- `);const result=await db.unsafe(`
131
+ `);const written=await db.unsafe(`
132
132
  DELETE FROM oauth_access_tokens
133
133
  WHERE expires_at < ${appNow()}
134
- `);return result?.changes||result?.rowCount||0}export async function deleteRevokedTokens(daysOld=7){const cutoffDate=new Date;cutoffDate.setDate(cutoffDate.getDate()-daysOld);await db.unsafe(`
134
+ `);return Number(written?.changes??written?.rowCount??0)}export async function deleteRevokedTokens(daysOld=7){const cutoffDate=new Date;cutoffDate.setDate(cutoffDate.getDate()-daysOld);await db.unsafe(`
135
135
  DELETE FROM oauth_refresh_tokens
136
136
  WHERE access_token_id IN (
137
137
  SELECT id FROM oauth_access_tokens WHERE revoked = ${boolTrue} AND updated_at < ${param(1)}
138
138
  )
139
- `,[sqlDateTime(cutoffDate)]);const result=await db.unsafe(`
139
+ `,[sqlDateTime(cutoffDate)]);const written=await db.unsafe(`
140
140
  DELETE FROM oauth_access_tokens
141
141
  WHERE revoked = ${boolTrue} AND updated_at < ${param(1)}
142
- `,[sqlDateTime(cutoffDate)]);return result?.changes||result?.rowCount||0}export async function clients(userId){return(await db.unsafe(`
142
+ `,[sqlDateTime(cutoffDate)]);return Number(written?.changes??written?.rowCount??0)}export async function clients(userId){return(await db.unsafe(`
143
143
  SELECT * FROM oauth_clients
144
144
  WHERE user_id = ${param(1)} AND revoked = ${boolFalse}
145
145
  ORDER BY created_at DESC
@@ -151,9 +151,9 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
151
151
  `,[options.name,secret,options.redirect,options.personalAccessClient||!1,options.passwordClient||!1]);else await db.unsafe(`
152
152
  INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
153
153
  VALUES (?, ?, 'local', ?, ?, ?, 0, ${appNow()})
154
- `,[options.name,secret,options.redirect,options.personalAccessClient?1:0,options.passwordClient?1:0]);const inserted=await db.unsafe(`
154
+ `,[options.name,secret,options.redirect,options.personalAccessClient?1:0,options.passwordClient?1:0]);const createdClient=(await db.unsafe(`
155
155
  SELECT * FROM oauth_clients WHERE secret = ${param(1)} LIMIT 1
156
- `,[secret]);return{client:mapToOAuthClient(inserted[0]),plainTextSecret:secret}}export async function revokeClient(clientId){await db.unsafe(`
156
+ `,[secret]))[0];if(!createdClient)throw new HttpError(500,"Failed to read back the OAuth client that was just created.");return{client:mapToOAuthClient(createdClient),plainTextSecret:secret}}export async function revokeClient(clientId){await db.unsafe(`
157
157
  UPDATE oauth_clients
158
158
  SET revoked = ${boolTrue}, updated_at = ${appNow()}
159
159
  WHERE id = ${param(1)}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@stacksjs/auth",
3
3
  "type": "module",
4
4
  "sideEffects": false,
5
- "version": "0.72.103",
5
+ "version": "0.73.0",
6
6
  "description": "A more simplistic way to authenticate.",
7
7
  "author": "Chris Breuer",
8
8
  "contributors": [
@@ -62,7 +62,7 @@
62
62
  },
63
63
  "devDependencies": {
64
64
  "better-dx": "^0.2.24",
65
- "@stacksjs/error-handling": "0.72.103",
66
- "@stacksjs/router": "0.72.103"
65
+ "@stacksjs/error-handling": "0.73.0",
66
+ "@stacksjs/router": "0.73.0"
67
67
  }
68
68
  }