@stacksjs/auth 0.75.408 → 0.75.417
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 +13 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/password/reset-token.d.ts +15 -0
- package/dist/password/reset-token.js +1 -0
- package/package.json +14 -14
package/README.md
CHANGED
|
@@ -99,3 +99,16 @@ For casual chit-chat with others using this package:
|
|
|
99
99
|
The MIT License (MIT). Please see [LICENSE](https://github.com/stacksjs/stacks/tree/main/LICENSE.md) for more information.
|
|
100
100
|
|
|
101
101
|
Made with 💙
|
|
102
|
+
|
|
103
|
+
### Indexed recovery credentials
|
|
104
|
+
|
|
105
|
+
`mintPasswordResetToken()` returns a random credential and its SHA-256 digest. Store only
|
|
106
|
+
`digest` in `password_resets.token`. Token-only recovery endpoints use
|
|
107
|
+
`consumePasswordResetToken(token, async claim => { ... })` to change the account and
|
|
108
|
+
revoke its credentials in one transaction. `claim` contains `userId`, `email` and
|
|
109
|
+
`hasExplicitExpiry`. The result is `{ consumed: false }` or
|
|
110
|
+
`{ consumed: true, value }`, with the callback's value. Unknown, expired, replaced
|
|
111
|
+
and already consumed tokens never invoke the callback. A failed callback rolls
|
|
112
|
+
everything back, leaving recovery retryable. Notifications belong after commit.
|
|
113
|
+
This API is for high-entropy digest tokens; `passwordResets(email)` continues to
|
|
114
|
+
serve existing email-based bcrypt recovery credentials.
|
package/dist/index.d.ts
CHANGED
|
@@ -33,6 +33,7 @@ export * from './magic-link';
|
|
|
33
33
|
export * from './passkey';
|
|
34
34
|
export * from './passkeys';
|
|
35
35
|
export * from './password/reset';
|
|
36
|
+
export * from './password/reset-token';
|
|
36
37
|
export * from './register';
|
|
37
38
|
export * from './user';
|
|
38
39
|
// Token management (Laravel Passport-style)
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./authentication";export*from"./impersonation";export{requestImpersonation}from"./impersonation-auth";export*from"./impersonation-store";export*from"./authenticator";export*from"./client";export*from"./credential-user";export*from"./middleware";export*from"./oauth-authorization";export*from"./oauth-authorization-endpoint";export*from"./oauth-authorization-requests";export*from"./oauth-authorization-response";export*from"./oauth-authorization-codes";export*from"./oauth-browser-session";export*from"./oauth-client-registration";export*from"./oauth-consent";export*from"./oauth-delegated-access";export*from"./oauth-grants";export*from"./oauth-introspection";export*from"./oauth-metadata";export*from"./oauth-provider";export*from"./oauth-pruning";export*from"./oauth-pkce";export*from"./oauth-revocation";export*from"./oauth-token-exchange";export*from"./oauth-token-endpoint";export*from"./oauth-token-request";export*from"./oauth-token-response";export*from"./rate-limiter";export*from"./magic-link";export*from"./passkey";export*from"./passkeys";export*from"./password/reset";export*from"./register";export*from"./user";export*from"./tokens";export*from"./gate";export*from"./policy";export*from"./authorizable";export*from"./permissions";export*from"./rbac";export{createBqbRbacStore}from"./rbac-store-bqb";export{DEFAULT_ROLE_PACKS,seedDefaultRoles}from"./rbac-seed";export*from"./email-verification";export*from"./session-auth";export*from"./cookie-auth";export*from"./browser-session";export*from"./request-token";export*from"./page-gate";export*from"./socials";export*from"./session-handoff";export{generateTOTP,verifyTOTP,generateTOTPSecret,totpKeyUri}from"@stacksjs/ts-auth";export*from"./two-factor";export*from"./team";export*from"./referrals";export*from"./oauth-dynamic-registration";export*from"./oauth-protected-resource";export*from"./oauth-server";
|
|
1
|
+
export*from"./authentication";export*from"./impersonation";export{requestImpersonation}from"./impersonation-auth";export*from"./impersonation-store";export*from"./authenticator";export*from"./client";export*from"./credential-user";export*from"./middleware";export*from"./oauth-authorization";export*from"./oauth-authorization-endpoint";export*from"./oauth-authorization-requests";export*from"./oauth-authorization-response";export*from"./oauth-authorization-codes";export*from"./oauth-browser-session";export*from"./oauth-client-registration";export*from"./oauth-consent";export*from"./oauth-delegated-access";export*from"./oauth-grants";export*from"./oauth-introspection";export*from"./oauth-metadata";export*from"./oauth-provider";export*from"./oauth-pruning";export*from"./oauth-pkce";export*from"./oauth-revocation";export*from"./oauth-token-exchange";export*from"./oauth-token-endpoint";export*from"./oauth-token-request";export*from"./oauth-token-response";export*from"./rate-limiter";export*from"./magic-link";export*from"./passkey";export*from"./passkeys";export*from"./password/reset";export*from"./password/reset-token";export*from"./register";export*from"./user";export*from"./tokens";export*from"./gate";export*from"./policy";export*from"./authorizable";export*from"./permissions";export*from"./rbac";export{createBqbRbacStore}from"./rbac-store-bqb";export{DEFAULT_ROLE_PACKS,seedDefaultRoles}from"./rbac-seed";export*from"./email-verification";export*from"./session-auth";export*from"./cookie-auth";export*from"./browser-session";export*from"./request-token";export*from"./page-gate";export*from"./socials";export*from"./session-handoff";export{generateTOTP,verifyTOTP,generateTOTPSecret,totpKeyUri}from"@stacksjs/ts-auth";export*from"./two-factor";export*from"./team";export*from"./referrals";export*from"./oauth-dynamic-registration";export*from"./oauth-protected-resource";export*from"./oauth-server";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** High-entropy reset credentials support indexed lookup without storing a usable token. */
|
|
2
|
+
export declare function passwordResetTokenDigest(token: string): string;
|
|
3
|
+
export declare function mintPasswordResetToken(): { token: string, digest: string };
|
|
4
|
+
/**
|
|
5
|
+
* Redeem a SHA-256 reset credential and apply account changes in one transaction.
|
|
6
|
+
* Issuance stores mintPasswordResetToken().digest in password_resets.token.
|
|
7
|
+
* The callback must contain database work only; notify the person after commit.
|
|
8
|
+
* A callback failure restores the token, and competing redeemers cannot both win.
|
|
9
|
+
*/
|
|
10
|
+
export declare function consumePasswordResetToken<T>(token: string, apply: (claim: PasswordResetClaim) => Promise<T>, options?: { ttlMs?: number }): Promise<{ consumed: false } | { consumed: true, value: T }>;
|
|
11
|
+
export declare interface PasswordResetClaim {
|
|
12
|
+
userId: number
|
|
13
|
+
email: string
|
|
14
|
+
hasExplicitExpiry: boolean
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createHash,randomBytes}from"node:crypto";import{config}from"@stacksjs/config";import{db,lockRow,mutationCount,parseSqlDateTime}from"@stacksjs/database/runtime";export function passwordResetTokenDigest(token){return createHash("sha256").update(token).digest("hex")}export function mintPasswordResetToken(){const token=randomBytes(32).toString("hex");return{token,digest:passwordResetTokenDigest(token)}}export async function consumePasswordResetToken(token,apply,options={}){if(!token||token.length>1024)return{consumed:!1};const digest=passwordResetTokenDigest(token),observed=await db.primary.selectFrom("password_resets").select(["email"]).where("token","=",digest).executeTakeFirst();if(!observed)return{consumed:!1};const email=String(observed.email),owner=await db.primary.selectFrom("users").select("id").where("email","=",email).executeTakeFirst();if(!owner)return{consumed:!1};const ttlMs=options.ttlMs??Number(config.auth.passwordReset?.expire??60)*60000;if(!Number.isFinite(ttlMs)||ttlMs<=0)throw RangeError("Reset lifetime must be positive");return db.transaction(async(tx)=>{const user=await lockRow(tx,"users",{id:Number(owner.id)});if(!user||String(user.email)!==email)return{consumed:!1};const reset=await lockRow(tx,"password_resets",{email});if(!reset||reset.token!==digest)return{consumed:!1};if(!((reset.expires_at==null?(parseSqlDateTime(reset.created_at)?.getTime()??0)+ttlMs:parseSqlDateTime(reset.expires_at)?.getTime()??0)>Date.now())){await db.deleteFrom("password_resets").where("email","=",email).where("token","=",digest).execute();return{consumed:!1}}const value=await apply({userId:Number(owner.id),email,hasExplicitExpiry:reset.expires_at!=null}),deleted=await db.deleteFrom("password_resets").where("email","=",email).where("token","=",digest).execute();if(mutationCount(deleted)!==1)throw Error("Password reset token could not be consumed");return{consumed:!0,value}})}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/auth",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.417",
|
|
6
6
|
"description": "A more simplistic way to authenticate.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -59,19 +59,19 @@
|
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@stacksjs/bun-router": "^0.1.24",
|
|
62
|
-
"@stacksjs/cache": "0.75.
|
|
63
|
-
"@stacksjs/config": "0.75.
|
|
64
|
-
"@stacksjs/database": "0.75.
|
|
65
|
-
"@stacksjs/email": "0.75.
|
|
66
|
-
"@stacksjs/env": "0.75.
|
|
67
|
-
"@stacksjs/error-handling": "0.75.
|
|
68
|
-
"@stacksjs/logging": "0.75.
|
|
69
|
-
"@stacksjs/orm": "0.75.
|
|
70
|
-
"@stacksjs/path": "0.75.
|
|
71
|
-
"@stacksjs/router": "0.75.
|
|
72
|
-
"@stacksjs/security": "0.75.
|
|
73
|
-
"@stacksjs/socials": "0.75.
|
|
74
|
-
"@stacksjs/storage": "0.75.
|
|
62
|
+
"@stacksjs/cache": "0.75.417",
|
|
63
|
+
"@stacksjs/config": "0.75.417",
|
|
64
|
+
"@stacksjs/database": "0.75.417",
|
|
65
|
+
"@stacksjs/email": "0.75.417",
|
|
66
|
+
"@stacksjs/env": "0.75.417",
|
|
67
|
+
"@stacksjs/error-handling": "0.75.417",
|
|
68
|
+
"@stacksjs/logging": "0.75.417",
|
|
69
|
+
"@stacksjs/orm": "0.75.417",
|
|
70
|
+
"@stacksjs/path": "0.75.417",
|
|
71
|
+
"@stacksjs/router": "0.75.417",
|
|
72
|
+
"@stacksjs/security": "0.75.417",
|
|
73
|
+
"@stacksjs/socials": "0.75.417",
|
|
74
|
+
"@stacksjs/storage": "0.75.417",
|
|
75
75
|
"@stacksjs/ts-auth": "^0.4.7",
|
|
76
76
|
"ts-qr-codes": "^0.1.8"
|
|
77
77
|
},
|