@stacksjs/auth 0.70.87 → 0.70.88
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/package.json +3 -3
- package/dist/authentication.d.ts +0 -43
- package/dist/authenticator.d.ts +0 -17
- package/dist/authorizable.d.ts +0 -77
- package/dist/client.d.ts +0 -22
- package/dist/email-verification.d.ts +0 -29
- package/dist/gate.d.ts +0 -180
- package/dist/index.d.ts +0 -36
- package/dist/index.js +0 -165
- package/dist/internal-constants.d.ts +0 -19
- package/dist/middleware.d.ts +0 -14
- package/dist/passkey.d.ts +0 -97
- package/dist/password/reset.d.ts +0 -10
- package/dist/policy.d.ts +0 -33
- package/dist/rate-limiter.d.ts +0 -44
- package/dist/rbac-seed.d.ts +0 -24
- package/dist/rbac-store-bqb.d.ts +0 -18
- package/dist/rbac.d.ts +0 -265
- package/dist/register.d.ts +0 -3
- package/dist/session-auth.d.ts +0 -49
- package/dist/team.d.ts +0 -121
- package/dist/token.d.ts +0 -16
- package/dist/tokens.d.ts +0 -284
- package/dist/two-factor.d.ts +0 -67
- package/dist/user.d.ts +0 -34
package/dist/index.js
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
var r1=Object.defineProperty;var s1=($)=>$;function e1($,W){this[$]=s1.bind(null,W)}var d0=($,W)=>{for(var Z in W)r1($,Z,{get:W[Z],enumerable:!0,configurable:!0,set:e1.bind(W,Z)})};var n0=($,W)=>()=>($&&(W=$($=0)),W);var _0=import.meta.require;var $1={};d0($1,{validateRefreshToken:()=>j$,tokens:()=>V$,tokenCant:()=>B$,tokenCanAny:()=>_$,tokenCanAll:()=>O$,tokenCan:()=>s0,tokenAbilities:()=>z$,token:()=>F$,revokeTokenById:()=>C$,revokeToken:()=>q$,revokeRefreshToken:()=>H$,revokeOtherTokens:()=>P$,revokeClient:()=>A$,revokeAllTokens:()=>z0,revokeAllRefreshTokens:()=>e0,refreshToken:()=>D$,parseScopes:()=>w,isIssuedBeforePasswordChange:()=>u,getPasswordChangedAt:()=>a,findToken:()=>r0,findClient:()=>x$,deleteRevokedTokens:()=>E$,deleteRevokedRefreshTokens:()=>U$,deleteExpiredTokens:()=>v$,deleteExpiredRefreshTokens:()=>M$,currentAccessToken:()=>l,createToken:()=>N0,createClient:()=>R$,clients:()=>S$});import{createHash as Q$,randomBytes as G$}from"crypto";import{db as B}from"@stacksjs/database";import{HttpError as Q0}from"@stacksjs/error-handling";import{getCurrentRequest as X$}from"@stacksjs/router";import{env as Y$}from"@stacksjs/env";import{sqlHelpers as K$}from"@stacksjs/database";function z($){return o0.param($)}function b($){return Q$("sha256").update($).digest("hex")}function a0($){let W=$.indexOf(":"),Z=W===-1?$:$.substring(0,W);return b(Z)}function G0($=40){return G$($).toString("hex")}async function a($,W=B){if($===null||$===void 0)return null;try{let J=(await W.unsafe(`
|
|
3
|
-
SELECT password_changed_at FROM users WHERE id = ${z(1)} LIMIT 1
|
|
4
|
-
`,[$]))[0]?.password_changed_at;if(J===null||J===void 0)return null;let Q=new Date(String(J));return Number.isNaN(Q.getTime())?null:Q}catch{return null}}function u($,W){if(!W)return!1;if($===null||$===void 0)return!1;let Z=new Date(String($));if(Number.isNaN(Z.getTime()))return!1;return Z.getTime()<W.getTime()}async function V$($){return(await B.unsafe(`
|
|
5
|
-
SELECT t.*, c.provider as client_provider
|
|
6
|
-
FROM oauth_access_tokens t
|
|
7
|
-
LEFT JOIN oauth_clients c ON t.oauth_client_id = c.id
|
|
8
|
-
WHERE t.user_id = ${z(1)}
|
|
9
|
-
AND t.revoked = ${X0}
|
|
10
|
-
ORDER BY t.created_at DESC
|
|
11
|
-
`,[$])).map((Z)=>({id:Z.id,userId:Z.user_id,clientId:Z.oauth_client_id,name:Z.name||"access-token",scopes:w(Z.scopes),revoked:!!Z.revoked,expiresAt:Z.expires_at?new Date(Z.expires_at):null,createdAt:new Date(Z.created_at),updatedAt:Z.updated_at?new Date(Z.updated_at):new Date}))}async function r0($){let W=a0($),J=(await B.unsafe(`
|
|
12
|
-
SELECT * FROM oauth_access_tokens
|
|
13
|
-
WHERE token = ${z(1)}
|
|
14
|
-
AND revoked = ${X0}
|
|
15
|
-
AND (expires_at IS NULL OR expires_at > ${M})
|
|
16
|
-
LIMIT 1
|
|
17
|
-
`,[W]))[0];if(!J)return null;if(u(J.created_at,await a(J.user_id)))return null;return{id:J.id,userId:J.user_id,clientId:J.oauth_client_id,name:J.name||"access-token",scopes:w(J.scopes),revoked:!!J.revoked,expiresAt:J.expires_at?new Date(J.expires_at):null,createdAt:new Date(J.created_at),updatedAt:J.updated_at?new Date(J.updated_at):new Date}}async function l(){let $=X$();if(!$)return null;let W=$._currentAccessToken;if(W)return W;let Z=$.bearerToken?.();if(!Z)return null;let J=await r0(Z);if(J)$._currentAccessToken=J;return J}async function s0($){let W=await l();if(!W)return!1;if(W.scopes.includes("*"))return!0;return W.scopes.includes($)}async function B$($){return!await s0($)}async function O$($){let W=await l();if(!W)return!1;if(W.scopes.includes("*"))return!0;return $.every((Z)=>W.scopes.includes(Z))}async function _$($){let W=await l();if(!W)return!1;if(W.scopes.includes("*"))return!0;return $.some((Z)=>W.scopes.includes(Z))}async function z$(){return(await l())?.scopes||[]}async function N0($,W="access-token",Z=["*"],J={}){let{expiresInMinutes:Q=60,withRefreshToken:G=!0,refreshExpiresInDays:X=30}=J,Y=(await B.unsafe(`
|
|
18
|
-
SELECT id FROM oauth_clients WHERE personal_access_client = ${P} LIMIT 1
|
|
19
|
-
`))[0];if(!Y)throw new Q0(500,"No personal access client found. Run ./buddy auth:setup first.");let L=G0(40),O=b(L),_=new Date;if(_.setMinutes(_.getMinutes()+Q),o)await B.unsafe(`
|
|
20
|
-
INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
|
|
21
|
-
VALUES ($1, $2, $3, $4, $5, false, $6, NOW(), NOW())
|
|
22
|
-
`,[$,Y.id,O,W,JSON.stringify(Z),_.toISOString()]);else await B.unsafe(`
|
|
23
|
-
INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
|
|
24
|
-
VALUES (?, ?, ?, ?, ?, 0, ?, ${M}, ${M})
|
|
25
|
-
`,[$,Y.id,O,W,JSON.stringify(Z),_.toISOString()]);let F=(await B.unsafe(`
|
|
26
|
-
SELECT * FROM oauth_access_tokens WHERE token = ${z(1)} LIMIT 1
|
|
27
|
-
`,[O]))[0];if(!F)throw new Q0(500,"Failed to create access token \u2014 inserted row not found");let j={id:F.id,userId:F.user_id,clientId:F.oauth_client_id,name:F.name,scopes:w(F.scopes),revoked:!1,expiresAt:_,createdAt:new Date(F.created_at),updatedAt:new Date(F.updated_at)},C;if(G){C=G0(40);let N=b(C),R=new Date;if(R.setDate(R.getDate()+X),o)await B.unsafe(`
|
|
28
|
-
INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
|
|
29
|
-
VALUES ($1, $2, false, $3, NOW())
|
|
30
|
-
`,[j.id,N,R.toISOString()]);else await B.unsafe(`
|
|
31
|
-
INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
|
|
32
|
-
VALUES (?, ?, 0, ?, ${M})
|
|
33
|
-
`,[j.id,N,R.toISOString()])}return{accessToken:j,plainTextToken:L,refreshToken:C,expiresIn:Q*60}}async function D$($,W={}){let{expiresInMinutes:Z=60,refreshExpiresInDays:J=30}=W,Q=b($);return await B.transaction(async(G)=>{let X=G,Y=(await X.unsafe(`
|
|
34
|
-
SELECT r.*, t.user_id, t.oauth_client_id, t.name, t.scopes
|
|
35
|
-
FROM oauth_refresh_tokens r
|
|
36
|
-
JOIN oauth_access_tokens t ON r.access_token_id = t.id
|
|
37
|
-
WHERE r.token = ${z(1)}
|
|
38
|
-
AND r.revoked = ${X0}
|
|
39
|
-
AND (r.expires_at IS NULL OR r.expires_at > ${M})
|
|
40
|
-
LIMIT 1
|
|
41
|
-
`,[Q]))[0];if(!Y)throw new Q0(401,"Invalid or expired refresh token");if(u(Y.created_at,await a(Y.user_id,X)))throw new Q0(401,"Invalid or expired refresh token");await X.unsafe(`
|
|
42
|
-
UPDATE oauth_refresh_tokens
|
|
43
|
-
SET revoked = ${P}
|
|
44
|
-
WHERE id = ${z(1)}
|
|
45
|
-
`,[Y.id]),await X.unsafe(`
|
|
46
|
-
UPDATE oauth_access_tokens
|
|
47
|
-
SET revoked = ${P}
|
|
48
|
-
WHERE id = ${z(1)}
|
|
49
|
-
`,[Y.access_token_id]);let L=G0(40),O=b(L),_=new Date;if(_.setMinutes(_.getMinutes()+Z),o)await X.unsafe(`
|
|
50
|
-
INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
|
|
51
|
-
VALUES ($1, $2, $3, $4, $5, false, $6, NOW(), NOW())
|
|
52
|
-
`,[Y.user_id,Y.oauth_client_id,O,Y.name,Y.scopes,_.toISOString()]);else await X.unsafe(`
|
|
53
|
-
INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
|
|
54
|
-
VALUES (?, ?, ?, ?, ?, 0, ?, ${M}, ${M})
|
|
55
|
-
`,[Y.user_id,Y.oauth_client_id,O,Y.name,Y.scopes,_.toISOString()]);let F=(await X.unsafe(`
|
|
56
|
-
SELECT * FROM oauth_access_tokens WHERE token = ${z(1)} LIMIT 1
|
|
57
|
-
`,[O]))[0],j={id:F.id,userId:F.user_id,clientId:F.oauth_client_id,name:F.name,scopes:w(F.scopes),revoked:!1,expiresAt:_,createdAt:new Date(F.created_at),updatedAt:new Date(F.updated_at)};if(u(F.created_at,await a(Y.user_id,X)))throw new Q0(401,"Invalid or expired refresh token");let C=G0(40),N=b(C),R=new Date;if(R.setDate(R.getDate()+J),o)await X.unsafe(`
|
|
58
|
-
INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
|
|
59
|
-
VALUES ($1, $2, false, $3, NOW())
|
|
60
|
-
`,[j.id,N,R.toISOString()]);else await X.unsafe(`
|
|
61
|
-
INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
|
|
62
|
-
VALUES (?, ?, 0, ?, ${M})
|
|
63
|
-
`,[j.id,N,R.toISOString()]);return{accessToken:j,plainTextToken:L,refreshToken:C,expiresIn:Z*60}})}async function j$($){let W=b($);return(await B.unsafe(`
|
|
64
|
-
SELECT id FROM oauth_refresh_tokens
|
|
65
|
-
WHERE token = ${z(1)}
|
|
66
|
-
AND revoked = ${X0}
|
|
67
|
-
AND (expires_at IS NULL OR expires_at > ${M})
|
|
68
|
-
LIMIT 1
|
|
69
|
-
`,[W])).length>0}async function H$($){let W=b($);await B.unsafe(`
|
|
70
|
-
UPDATE oauth_refresh_tokens
|
|
71
|
-
SET revoked = ${P}
|
|
72
|
-
WHERE token = ${z(1)}
|
|
73
|
-
`,[W])}async function e0($){await B.unsafe(`
|
|
74
|
-
UPDATE oauth_refresh_tokens
|
|
75
|
-
SET revoked = ${P}
|
|
76
|
-
WHERE access_token_id IN (
|
|
77
|
-
SELECT id FROM oauth_access_tokens WHERE user_id = ${z(1)}
|
|
78
|
-
)
|
|
79
|
-
`,[$])}async function M$(){let $=await B.unsafe(`
|
|
80
|
-
DELETE FROM oauth_refresh_tokens
|
|
81
|
-
WHERE expires_at < ${M}
|
|
82
|
-
`);return $?.changes||$?.rowCount||0}async function U$($=7){let W=new Date;W.setDate(W.getDate()-$);let Z=await B.unsafe(`
|
|
83
|
-
DELETE FROM oauth_refresh_tokens
|
|
84
|
-
WHERE revoked = ${P} AND created_at < ${z(1)}
|
|
85
|
-
`,[W.toISOString()]);return Z?.changes||Z?.rowCount||0}async function q$($){let W=a0($);await B.unsafe(`
|
|
86
|
-
UPDATE oauth_refresh_tokens
|
|
87
|
-
SET revoked = ${P}
|
|
88
|
-
WHERE access_token_id IN (
|
|
89
|
-
SELECT id FROM oauth_access_tokens WHERE token = ${z(1)}
|
|
90
|
-
)
|
|
91
|
-
`,[W]),await B.unsafe(`
|
|
92
|
-
UPDATE oauth_access_tokens
|
|
93
|
-
SET revoked = ${P}, updated_at = ${M}
|
|
94
|
-
WHERE token = ${z(1)}
|
|
95
|
-
`,[W])}async function C$($){await B.unsafe(`
|
|
96
|
-
UPDATE oauth_refresh_tokens
|
|
97
|
-
SET revoked = ${P}
|
|
98
|
-
WHERE access_token_id = ${z(1)}
|
|
99
|
-
`,[$]),await B.unsafe(`
|
|
100
|
-
UPDATE oauth_access_tokens
|
|
101
|
-
SET revoked = ${P}, updated_at = ${M}
|
|
102
|
-
WHERE id = ${z(1)}
|
|
103
|
-
`,[$])}async function z0($){await e0($),await B.unsafe(`
|
|
104
|
-
UPDATE oauth_access_tokens
|
|
105
|
-
SET revoked = ${P}, updated_at = ${M}
|
|
106
|
-
WHERE user_id = ${z(1)}
|
|
107
|
-
`,[$])}async function P$($){let W=await l();if(!W)return z0($);if(o)await B.unsafe(`
|
|
108
|
-
UPDATE oauth_refresh_tokens
|
|
109
|
-
SET revoked = true
|
|
110
|
-
WHERE access_token_id IN (
|
|
111
|
-
SELECT id FROM oauth_access_tokens WHERE user_id = $1 AND id != $2
|
|
112
|
-
)
|
|
113
|
-
`,[$,W.id]),await B.unsafe(`
|
|
114
|
-
UPDATE oauth_access_tokens
|
|
115
|
-
SET revoked = true, updated_at = NOW()
|
|
116
|
-
WHERE user_id = $1 AND id != $2
|
|
117
|
-
`,[$,W.id]);else await B.unsafe(`
|
|
118
|
-
UPDATE oauth_refresh_tokens
|
|
119
|
-
SET revoked = 1
|
|
120
|
-
WHERE access_token_id IN (
|
|
121
|
-
SELECT id FROM oauth_access_tokens WHERE user_id = ? AND id != ?
|
|
122
|
-
)
|
|
123
|
-
`,[$,W.id]),await B.unsafe(`
|
|
124
|
-
UPDATE oauth_access_tokens
|
|
125
|
-
SET revoked = 1, updated_at = ${M}
|
|
126
|
-
WHERE user_id = ? AND id != ?
|
|
127
|
-
`,[$,W.id])}async function v$(){await B.unsafe(`
|
|
128
|
-
DELETE FROM oauth_refresh_tokens
|
|
129
|
-
WHERE access_token_id IN (
|
|
130
|
-
SELECT id FROM oauth_access_tokens WHERE expires_at < ${M}
|
|
131
|
-
)
|
|
132
|
-
`);let $=await B.unsafe(`
|
|
133
|
-
DELETE FROM oauth_access_tokens
|
|
134
|
-
WHERE expires_at < ${M}
|
|
135
|
-
`);return $?.changes||$?.rowCount||0}async function E$($=7){let W=new Date;W.setDate(W.getDate()-$),await B.unsafe(`
|
|
136
|
-
DELETE FROM oauth_refresh_tokens
|
|
137
|
-
WHERE access_token_id IN (
|
|
138
|
-
SELECT id FROM oauth_access_tokens WHERE revoked = ${P} AND updated_at < ${z(1)}
|
|
139
|
-
)
|
|
140
|
-
`,[W.toISOString()]);let Z=await B.unsafe(`
|
|
141
|
-
DELETE FROM oauth_access_tokens
|
|
142
|
-
WHERE revoked = ${P} AND updated_at < ${z(1)}
|
|
143
|
-
`,[W.toISOString()]);return Z?.changes||Z?.rowCount||0}async function S$($){return(await B.unsafe(`
|
|
144
|
-
SELECT * FROM oauth_clients
|
|
145
|
-
WHERE user_id = ${z(1)} AND revoked = ${X0}
|
|
146
|
-
ORDER BY created_at DESC
|
|
147
|
-
`,[$])).map(w0)}async function x$($){let Z=(await B.unsafe(`
|
|
148
|
-
SELECT * FROM oauth_clients WHERE id = ${z(1)} LIMIT 1
|
|
149
|
-
`,[$]))[0];return Z?w0(Z):null}async function R$($){let W=G0(40);if(o)await B.unsafe(`
|
|
150
|
-
INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
|
|
151
|
-
VALUES ($1, $2, 'local', $3, $4, $5, false, NOW())
|
|
152
|
-
`,[$.name,W,$.redirect,$.personalAccessClient||!1,$.passwordClient||!1]);else await B.unsafe(`
|
|
153
|
-
INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
|
|
154
|
-
VALUES (?, ?, 'local', ?, ?, ?, 0, ${M})
|
|
155
|
-
`,[$.name,W,$.redirect,$.personalAccessClient?1:0,$.passwordClient?1:0]);let Z=await B.unsafe(`
|
|
156
|
-
SELECT * FROM oauth_clients WHERE secret = ${z(1)} LIMIT 1
|
|
157
|
-
`,[W]);return{client:w0(Z[0]),plainTextSecret:W}}async function A$($){await B.unsafe(`
|
|
158
|
-
UPDATE oauth_clients
|
|
159
|
-
SET revoked = ${P}, updated_at = ${M}
|
|
160
|
-
WHERE id = ${z(1)}
|
|
161
|
-
`,[$])}function w($){if(!$)return[];if(Array.isArray($))return $;try{let W=JSON.parse($);return Array.isArray(W)?W:[]}catch{return[]}}function w0($){return{id:$.id,name:$.name,secret:$.secret,provider:$.provider,redirect:$.redirect,personalAccessClient:Boolean($.personal_access_client),passwordClient:Boolean($.password_client),revoked:Boolean($.revoked),createdAt:$.created_at?new Date($.created_at):new Date,updatedAt:$.updated_at?new Date($.updated_at):null}}var L$,o0,o,FZ,M,P,X0,F$;var Y0=n0(()=>{L$=Y$.DB_CONNECTION||"sqlite",o0=K$(L$),{isPostgres:o,isMysql:FZ,now:M,boolTrue:P,boolFalse:X0}=o0;F$=l});var S1={};d0(S1,{policy:()=>F0,none:()=>M1,inspect:()=>e,hasPolicy:()=>C1,has:()=>q1,getPolicyFor:()=>U1,flush:()=>v1,denies:()=>H1,define:()=>_1,default:()=>LW,cannot:()=>P0,can:()=>C0,before:()=>z1,authorize:()=>E0,any:()=>B0,allows:()=>j1,all:()=>v0,after:()=>D1,abilities:()=>P1,Gate:()=>E1,AuthorizationResponse:()=>U,AuthorizationException:()=>V0});class U{isAllowed;message;code;constructor($,W,Z){this.isAllowed=$,this.message=W,this.code=Z}static allow($){return new U(!0,$)}static deny($,W){return new U(!1,$||"This action is unauthorized.",W)}allowed(){return this.isAllowed}denied(){return!this.isAllowed}authorize(){if(!this.isAllowed)throw new V0(this.message||"This action is unauthorized.",this.code)}}function _1($,W){q.gates.set($,W)}function F0($,W){let Z=typeof $==="string"?$:$.name;q.policies.set(Z,W)}function z1($){q.beforeCallbacks.push($)}function D1($){q.afterCallbacks.push($)}async function j1($,W,...Z){return $0($,W,...Z)}async function H1($,W,...Z){return!await $0($,W,...Z)}async function C0($,W,...Z){return $0($,W,...Z)}async function P0($,W,...Z){return!await $0($,W,...Z)}async function B0($,W,...Z){for(let J of $)if(await $0(J,W,...Z))return!0;return!1}async function v0($,W,...Z){for(let J of $)if(!await $0(J,W,...Z))return!1;return!0}async function M1($,W,...Z){return!await B0($,W,...Z)}async function E0($,W,...Z){let J=await e($,W,...Z);if(!J.isAllowed)throw new V0(J.message,J.code);return J}async function e($,W,...Z){for(let G of q.beforeCallbacks){let X=await G(W,$,Z);if(X===!0)return U.allow();if(X===!1)return U.deny()}let J=Z[0];if(J&&typeof J==="object"){let G=J.constructor?.name,X=q.policies.get(G);if(X){let K=new X;if(K.before){let L=await K.before(W,$);if(L===!0)return U.allow();if(L===!1)return U.deny()}let Y=K[$];if(Y){let L=await Y.call(K,W,...Z);return O1(L??!1)}}}let Q=q.gates.get($);if(Q){let G=await Q(W,...Z),X=O1(G);for(let K of q.afterCallbacks){let Y=await K(W,$,X.isAllowed,Z);if(typeof Y==="boolean")return Y?U.allow():U.deny()}return X}return U.deny(`No gate or policy defined for ability: ${$}`)}async function $0($,W,...Z){return(await e($,W,...Z)).isAllowed}function O1($){if($ instanceof U)return $;if(typeof $!=="boolean")throw TypeError(`[gate] Policy must return boolean or AuthorizationResponse; got ${typeof $}. If you returned a model/value by mistake, return \`true\`/\`false\` instead.`);return $?U.allow():U.deny()}function U1($){if(!$||typeof $!=="object")return null;let W=$.constructor?.name,Z=q.policies.get(W);if(Z)return new Z;return null}function q1($){return q.gates.has($)}function C1($){let W=typeof $==="string"?$:$.name;return q.policies.has(W)}function P1(){return Array.from(q.gates.keys())}function v1(){q.gates.clear(),q.policies.clear(),q.beforeCallbacks=[],q.afterCallbacks=[]}var V0,q,E1,LW;var W0=n0(()=>{V0=class V0 extends Error{code;status;constructor($="This action is unauthorized.",W,Z=403){super($);this.code=W;this.status=Z;this.name="AuthorizationException"}};q={gates:new Map,policies:new Map,beforeCallbacks:[],afterCallbacks:[]};E1={define:_1,policy:F0,before:z1,after:D1,allows:j1,denies:H1,can:C0,cannot:P0,any:B0,all:v0,none:M1,authorize:E0,inspect:e,has:q1,hasPolicy:C1,abilities:P1,getPolicyFor:U1,flush:v1,AuthorizationResponse:U,AuthorizationException:V0},LW=E1});import{config as v}from"@stacksjs/config";import{db as H}from"@stacksjs/database";import{HttpError as T0}from"@stacksjs/error-handling";import{formatDate as d,User as r}from"@stacksjs/orm";import{getCurrentRequest as y$,request as f0}from"@stacksjs/router";import{Buffer as D0}from"buffer";import{createHash as N$,timingSafeEqual as I0}from"crypto";import{decrypt as W1,encrypt as w$,verifyHash as j0}from"@stacksjs/security";import{log as Z1}from"@stacksjs/logging";var t="$2b$12$000000000000000000000uGByljkdFkOJRCRiYZGFOAstyLlSgTSW";import{HttpError as $$}from"@stacksjs/error-handling";var W$=5,i0=900000,Z$=1e4,J$=300000;class y0{store=new Map;lastEviction=Date.now();evict(){let $=Date.now(),W=$-this.lastEviction>=J$,Z=this.store.size>=Z$;if(!W&&!Z)return;this.lastEviction=$;for(let[J,Q]of this.store)if(Q.lockedUntil>0&&Q.lockedUntil<=$)this.store.delete(J);else if(Q.lockedUntil===0&&Q.attempts===0)this.store.delete(J)}get($){return this.evict(),this.store.get($)}set($,W){this.store.set($,W)}delete($){this.store.delete($)}}class t0{prefix="auth:ratelimit:";async get($){let{cache:W}=await import("@stacksjs/cache"),Z=await W.get(`${this.prefix}${$}`);if(Z==null)return;try{return typeof Z==="string"?JSON.parse(Z):Z}catch{return}}async set($,W,Z){let{cache:J}=await import("@stacksjs/cache");await J.set(`${this.prefix}${$}`,JSON.stringify(W),Math.ceil(Z/1000))}async delete($){let{cache:W}=await import("@stacksjs/cache");await W.remove(`${this.prefix}${$}`)}}var I=new y0;class f{static useStore($){I=$}static useSharedStore(){I=new t0}static useMemoryStore(){I=new y0}static async isRateLimited($){$=$.toLowerCase();let W=Date.now(),Z=await I.get($);if(!Z)return!1;if(Z.lockedUntil>0&&Z.lockedUntil<=W)return await I.delete($),!1;return Z.lockedUntil>0}static async recordFailedAttempt($){$=$.toLowerCase();let W=Date.now(),Z=await I.get($)||{attempts:0,lockedUntil:0};if(Z.attempts++,Z.attempts>=W$)Z.lockedUntil=W+i0,Z.attempts=0;await I.set($,Z,i0)}static async resetAttempts($){await I.delete($.toLowerCase())}static async validateAttempt($){if(await this.isRateLimited($))throw new $$(429,"Too many login attempts. Please try again later.")}}Y0();var J1=Symbol.for("stacks.requestAuthState");function S(){let $=y$();if(!$)return null;let W=$[J1];if(!W)W={},$[J1]=W;return W}function K0($){return N$("sha256").update($).digest("hex")}class x{static getBearerToken(){let $=f0.bearerToken?.();if(!$){let W=f0.headers?.get?.("authorization")||f0.headers?.get?.("Authorization");if(W&&W.startsWith("Bearer "))$=W.substring(7)}return $||null}static parseToken($){let W=$.indexOf(":");if(W===-1)return null;let Z=$.substring(0,W),J=$.substring(W+1);if(!Z||!J)return null;return{plainToken:Z,encryptedId:J}}static async getClientSecret(){let $=S();if($?.clientSecret)return $.clientSecret;let W=await this.getPersonalAccessClient();if($)$.clientSecret=W.secret;return W.secret}static async encryptTokenId($){return await w$(String($))}static async decryptTokenId($){try{return await W1($)}catch{try{let W=await this.getClientSecret();return await W1($,W)}catch{return null}}}static async getPersonalAccessClient(){try{let $=await H.selectFrom("oauth_clients").where("personal_access_client","=",!0).where("revoked","=",!1).selectAll().executeTakeFirst();if(!$)throw new T0(500,"No personal access client found. Please run `./buddy auth:setup` first.");return $}catch($){if($ instanceof Error&&$.message.includes("does not exist"))throw new T0(500,"OAuth tables not found. Please run `./buddy auth:setup` first.");throw $}}static async validateClient($,W){let Z=await H.selectFrom("oauth_clients").where("id","=",$).where("revoked","=",!1).selectAll().executeTakeFirst(),J=D0.from(W);if(!Z?.secret){let X=D0.alloc(Math.max(J.length,1)),K=J.length>0?J:D0.alloc(1);return I0(X,K),!1}let Q=String(Z.secret);if(Q.startsWith("$2"))return await j0(W,Q);let G=D0.from(Q);if(G.length!==J.length)return I0(G,G),!1;return I0(G,J)}static async getTokenFromId($){let W=await H.selectFrom("oauth_access_tokens").where("id","=",$).selectAll().executeTakeFirst();if(!W)return null;let Z=W;return{id:Z.id,userId:Z.user_id,clientId:Z.oauth_client_id,name:Z.name||"auth-token",scopes:w(Z.scopes),abilities:w(Z.scopes),expiresAt:Z.expires_at?new Date(String(Z.expires_at)):null,createdAt:Z.created_at?new Date(String(Z.created_at)):new Date,updatedAt:Z.updated_at?new Date(String(Z.updated_at)):new Date,revoked:!!Z.revoked}}static async attempt($){let W=v.auth.username||"email",Z=v.auth.password||"password",J=$[W];if(!J)return!1;let Q=await f.isRateLimited(J),G=await r.where("email","=",J).first(),X=$[Z]||"",K=G?.password||t,Y=await j0(X,K);if(Q)return!1;if(Y&&G){await f.resetAttempts(J);let L=S();if(L)L.authUser=G;return!0}return await f.recordFailedAttempt(J),!1}static async validate($){let W=v.auth.username||"email",Z=v.auth.password||"password",J=$[W];if(!J)return!1;let Q=await r.where("email","=",J).first(),G=$[Z]||"",X=Q?.password||t;return await j0(G,X)&&!!Q}static async login($,W){let Z=await this.attempt($),J=S()?.authUser;if(!Z||!J)return null;let{plainTextToken:Q,refreshToken:G,expiresIn:X}=await this.createTokenForUser(J,W);return{user:J,token:Q,refreshToken:G,expiresIn:X}}static async loginUsingId($,W){let Z=await r.find($);if(!Z)return null;let J=S();if(J)J.authUser=Z;let{plainTextToken:Q,refreshToken:G,expiresIn:X}=await this.createTokenForUser(Z,W);return{user:Z,token:Q,refreshToken:G,expiresIn:X}}static async logout(){let $=this.getBearerToken();if($){let Z=await H.selectFrom("oauth_access_tokens").where("token","=",K0($)).select(["id"]).executeTakeFirst();if(Z)await H.updateTable("oauth_refresh_tokens").set({revoked:!0}).where("access_token_id","=",Number(Z.id)).execute();await this.revokeToken($)}let W=S();if(W)W.authUser=void 0,W.currentToken=void 0}static async user(){let $=S();if($?.authUser)return $.authUser;let W=this.getBearerToken();if(!W)return;let Z=await this.getUserFromToken(W);if(Z&&$)$.authUser=Z;return Z}static async check(){return await this.user()!==void 0}static async guest(){return!await this.check()}static async id(){return(await this.user())?.id}static setUser($){let W=S();if(W)W.authUser=$}static async createTokenForUser($,W){let Z=W?.name??v.auth.defaultTokenName??"auth-token",J=W?.abilities??W?.scopes??v.auth.defaultAbilities??["*"],Q=W?.expiresInMinutes!==void 0?W.expiresInMinutes*60*1000:v.auth.tokenExpiry??3600000,X=W?.expiresAt??new Date(Date.now()+Q),K=Math.max(1,Math.floor((X.getTime()-Date.now())/60000)),Y=W?.refreshExpiresInDays??Math.max(1,Math.round((v.auth.refreshTokenExpiry??2592000000)/86400000));Z1.debug(`[auth] Creating token for user#${$.id}: ${Z}`);let L=await N0($.id,Z,J,{expiresInMinutes:K,withRefreshToken:W?.withRefreshToken!==!1,refreshExpiresInDays:Y}),O=L.plainTextToken;return{accessToken:{id:L.accessToken.id,userId:L.accessToken.userId,clientId:L.accessToken.clientId,name:L.accessToken.name,scopes:L.accessToken.scopes,abilities:J,expiresAt:L.accessToken.expiresAt??X,createdAt:L.accessToken.createdAt,updatedAt:L.accessToken.updatedAt,revoked:L.accessToken.revoked,plainTextToken:O},plainTextToken:O,refreshToken:L.refreshToken,expiresIn:L.expiresIn}}static async createToken($,W=v.auth.defaultTokenName||"auth-token",Z=v.auth.defaultAbilities||["*"]){let{plainTextToken:J}=await this.createTokenForUser($,{name:W,abilities:Z});return J}static async requestToken($,W,Z){if(!await this.validateClient(W,Z))throw new T0(401,"Invalid client credentials");let Q=await this.attempt($),G=S()?.authUser;if(!Q||!G)return null;return{token:await this.createToken(G,"user-auth-token")}}static async validateToken($){let W=K0($),Z=await H.selectFrom("oauth_access_tokens").where("token","=",W).selectAll().executeTakeFirst();if(!Z)return!1;if(Z1.debug(`[auth] Token validated for token#${Z.id}`),Z.expires_at&&new Date(String(Z.expires_at))<new Date)return await H.deleteFrom("oauth_access_tokens").where("id","=",Z.id).execute(),!1;if(Z.revoked)return!1;if(u(Z.created_at,await a(Z.user_id)))return!1;return await H.updateTable("oauth_access_tokens").set({updated_at:d(new Date)}).where("id","=",Z.id).execute(),!0}static async getUserFromToken($){let W=K0($),Z=await H.selectFrom("oauth_access_tokens").where("token","=",W).selectAll().executeTakeFirst();if(!Z)return;if(Z.expires_at&&new Date(String(Z.expires_at))<new Date){await H.deleteFrom("oauth_access_tokens").where("id","=",Z.id).execute();return}if(Z.revoked)return;let J=S();if(J)J.currentToken=await this.getTokenFromId(Z.id)??void 0;if(await H.updateTable("oauth_access_tokens").set({updated_at:d(new Date)}).where("id","=",Z.id).execute(),!Z?.user_id)return;let Q=await r.find(Z.user_id),G=Q?.password_changed_at,X=G?new Date(String(G)):null;if(u(Z.created_at,X))return;return Q}static async currentAccessToken(){let $=S();if($?.currentToken)return $.currentToken;let W=this.getBearerToken();if(!W)return;let Z=await H.selectFrom("oauth_access_tokens").where("token","=",K0(W)).select(["id"]).executeTakeFirst();if(!Z)return;let J=await this.getTokenFromId(Number(Z.id));if(J&&$)$.currentToken=J;return J??void 0}static async tokenCan($){let W=await this.currentAccessToken();if(!W)return!1;if(W.abilities.includes("*"))return!0;return W.abilities.includes($)}static async tokenCant($){return!await this.tokenCan($)}static async tokenAbilities(){return(await this.currentAccessToken())?.abilities??[]}static async tokenCanAll($){let W=await this.currentAccessToken();if(!W)return!1;if(W.abilities.includes("*"))return!0;return $.every((Z)=>W.abilities.includes(Z))}static async tokenCanAny($){let W=await this.currentAccessToken();if(!W)return!1;if(W.abilities.includes("*"))return!0;return $.some((Z)=>W.abilities.includes(Z))}static async tokens($){let W=$??await this.id();if(!W)return[];return(await H.selectFrom("oauth_access_tokens").where("user_id","=",W).where("revoked","=",!1).selectAll().execute()).map((J)=>({id:Number(J.id),userId:Number(J.user_id),clientId:Number(J.oauth_client_id),name:String(J.name||"auth-token"),scopes:w(String(J.scopes??"")),abilities:w(String(J.scopes??"")),expiresAt:J.expires_at?new Date(String(J.expires_at)):null,createdAt:J.created_at?new Date(String(J.created_at)):new Date,updatedAt:J.updated_at?new Date(String(J.updated_at)):new Date,revoked:!!J.revoked}))}static async revokeToken($){await H.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:d(new Date)}).where("token","=",K0($)).execute()}static async revokeTokenById($){await H.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:d(new Date)}).where("id","=",$).execute()}static async revokeAllTokens($){let W=$??await this.id();if(!W)return;await H.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:d(new Date)}).where("user_id","=",W).execute()}static async revokeOtherTokens($){let W=$??await this.id();if(!W)return;let Z=await this.currentAccessToken();if(!Z)return;await H.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:d(new Date)}).where("user_id","=",W).where("id","!=",Z.id).execute()}static async pruneExpiredTokens(){let $=await H.deleteFrom("oauth_access_tokens").where("expires_at","<",d(new Date)).executeTakeFirst();return Number($?.numDeletedRows)||0}static async pruneRevokedTokens(){let $=await H.deleteFrom("oauth_access_tokens").where("revoked","=",!0).executeTakeFirst();return Number($?.numDeletedRows)||0}static async rotateToken($){let{findToken:W,revokeToken:Z}=await Promise.resolve().then(() => (Y0(),$1)),J=await W($);if(!J)return null;let Q=J.expiresAt?J.expiresAt.getTime()-Date.now():v.auth.tokenExpiry??3600000,G=Math.max(1,Math.floor(Q/60000));await Z($);let X=await r.find(J.userId);if(!X)return null;return(await this.createTokenForUser(X,{name:J.name,abilities:J.scopes??["*"],expiresInMinutes:G,withRefreshToken:!1})).plainTextToken}static async findToken($){return this.getTokenFromId($)}static async once($){let W=v.auth.username||"email",Z=v.auth.password||"password",J=$[W];if(!J)return!1;let Q=await r.where("email","=",J).first(),G=$[Z]||"",X=Q?.password||t;if(await j0(G,X)&&Q){let Y=S();if(Y)Y.authUser=Q;return!0}return!1}static guard($){return this}static viaRemember(){return!1}static clearState(){let $=S();if($)$.authUser=void 0,$.currentToken=void 0,$.clientSecret=void 0}}import{generateTOTP as T$,generateTOTPSecret as f$,totpKeyUri as I$,verifyTOTP as b$}from"@stacksjs/ts-auth";function b0(){return f$()}async function xZ($){return T$({secret:$})}async function k0($,W){return b$($,{secret:W})}function Q1($,W,Z){let J=$||"johndoe@example.com",Q=W||"StacksJS 2fa",G=Z||b0();return I$(J,Q,G)}import{randomBytes as k$}from"crypto";import{db as H0}from"@stacksjs/database";import{formatDate as g$}from"@stacksjs/orm";import{err as h$,HttpError as p$,ok as m$}from"@stacksjs/error-handling";import{makeHash as c$}from"@stacksjs/security";async function fZ(){if((await H0.selectFrom("oauth_clients").where("personal_access_client","=",!0).where("revoked","=",!1).select(["id"]).executeTakeFirst())?.id)return h$({code:"already-exists",message:"A personal access client already exists. Revoke the existing client (`./buddy auth:revoke-client`) before creating a new one."});let W=k$(40).toString("hex"),Z=await c$(W,{algorithm:"bcrypt"});if(await H0.insertInto("oauth_clients").values({name:"Personal Access Client",secret:Z,provider:"local",redirect:"http://localhost",personal_access_client:!0,password_client:!1,revoked:!1,created_at:g$(new Date)}).execute(),!(await H0.selectFrom("oauth_clients").where("secret","=",Z).select(["id"]).executeTakeFirst())?.id)throw new p$(500,"Failed to create personal access client");return m$(W)}async function u$($){let W=$.bearerToken?.();if(!W){let Q=$.headers?.get?.("authorization")||$.headers?.get?.("Authorization");if(Q&&Q.startsWith("Bearer "))W=Q.substring(7)}if(!W){let Q=Error("No authentication token provided.");throw Q.statusCode=401,Q}let Z=await x.getUserFromToken(W);if(!Z){let Q=Error("Invalid or expired authentication token.");throw Q.statusCode=401,Q}x.setUser(Z),$._authenticatedUser=Z;let J=await x.currentAccessToken();$._currentAccessToken=J}var kZ={name:"auth",handle:u$};import{Buffer as G1}from"buffer";import{db as k}from"@stacksjs/database";import{generateRegistrationOptions as tZ,generateAuthenticationOptions as oZ,verifyRegistrationResponse as aZ,verifyAuthenticationResponse as rZ,startRegistration as sZ,startAuthentication as eZ,browserSupportsWebAuthn as $2,browserSupportsWebAuthnAutofill as W2,platformAuthenticatorIsAvailable as Z2}from"@stacksjs/ts-auth";async function mZ($){return await k.selectFrom("passkeys").selectAll().where("user_id","=",$).execute()}async function l$($,W){return await k.selectFrom("passkeys").selectAll().where("id","=",W).where("user_id","=",$).executeTakeFirst()}async function cZ($,W,Z){let J=await l$($,W);if(!J)return!1;let Q=Number(J.counter??0);if(Z!==0&&Z<=Q)return!1;return await k.updateTable("passkeys").set({counter:Z,last_used_at:X1()}).where("id","=",W).where("user_id","=",$).execute(),!0}async function uZ($,W){let Z=W.registrationInfo?.credential.id,J=W.registrationInfo?.credential.publicKey;if(!Z)throw Error("[auth/passkey] WebAuthn registration response is missing credential.id");if(!J)throw Error("[auth/passkey] WebAuthn registration response is missing credential.publicKey");let Q={id:Z,cred_public_key:JSON.stringify(J),user_id:$.id,webauthn_user_id:$.email||"",counter:W.registrationInfo?.credential.counter||0,credential_type:W.registrationInfo?.credentialType||"",device_type:W.registrationInfo?.credentialDeviceType||"",backup_eligible:!1,backup_status:W.registrationInfo?.credentialBackedUp||!1,transports:JSON.stringify(["internal"]),last_used_at:X1()};await k.insertInto("passkeys").values(Q).executeTakeFirstOrThrow()}function X1(){let $=new Date,W=(Y)=>String(Y).padStart(2,"0"),Z=$.getFullYear(),J=W($.getMonth()+1),Q=W($.getDate()),G=W($.getHours()),X=W($.getMinutes()),K=W($.getSeconds());return`${Z}-${J}-${Q} ${G}:${X}:${K}`}var d$=300;async function lZ($,W,Z,J=d$){let Q=new Date(Date.now()+J*1000).toISOString(),G=typeof W==="string"?W:G1.from(W).toString("base64url");await k.deleteFrom("webauthn_challenges").where("user_id","=",$).where("purpose","=",Z).execute(),await k.insertInto("webauthn_challenges").values({user_id:$,challenge:G,purpose:Z,expires_at:Q}).execute()}async function dZ($,W){let Z=await k.selectFrom("webauthn_challenges").where("user_id","=",$).where("purpose","=",W).selectAll().executeTakeFirst();if(!Z)return null;await k.deleteFrom("webauthn_challenges").where("user_id","=",$).where("purpose","=",W).execute();let J=Z.expires_at?new Date(String(Z.expires_at)).getTime():0;if(Date.now()>J)return null;return G1.from(String(Z.challenge),"base64url")}import{randomBytes as $W}from"crypto";import{config as g}from"@stacksjs/config";import{db as n}from"@stacksjs/database";import{mail as U0,template as F1}from"@stacksjs/email";import{log as WW}from"@stacksjs/logging";import{formatDate as ZW}from"@stacksjs/orm";import{makeHash as K1,verifyHash as L1}from"@stacksjs/security";import{HttpError as g0}from"@stacksjs/error-handling";import{log as M0}from"@stacksjs/logging";import{User as Y1}from"@stacksjs/orm";import{verifyHash as n$}from"@stacksjs/security";import{db as T}from"@stacksjs/database";import{getCurrentRequest as i$}from"@stacksjs/router";function t$(){let $=crypto.getRandomValues(new Uint8Array(32));return Array.from($,(W)=>W.toString(16).padStart(2,"0")).join("")}function o$($){if($)return{ip:$.ip??null,userAgent:$.userAgent??null};let W=i$();if(!W)return{ip:null,userAgent:null};let Z=W.headers,J=Z?.get?.("x-forwarded-for")||Z?.get?.("X-Forwarded-For")||"",Q=Z?.get?.("x-real-ip")||Z?.get?.("X-Real-IP")||"",G=(J?J.split(",")[0].trim():"")||Q||null,X=Z?.get?.("user-agent")||Z?.get?.("User-Agent")||null;return{ip:G,userAgent:X}}async function a$($,W,Z){let J=($||"").toLowerCase(),Q=await f.isRateLimited(J),G=await Y1.where("email",$).first(),X=G?.password||t,K=await n$(W,X);if(Q)throw new g0(429,"Too many login attempts. Please try again later.");if(!K||!G)throw await f.recordFailedAttempt(J),new g0(401,"Invalid credentials");await f.resetAttempts(J);let Y=t$(),L=new Date(Date.now()+86400000),{ip:O,userAgent:_}=o$(Z);M0.debug(`[auth] Session created for user#${G.id}`);try{await T.insertInto("sessions").values({id:Y,user_id:G.id,ip_address:O,user_agent:_,payload:"{}",last_activity:Math.floor(Date.now()/1000),expires_at:L.toISOString()}).execute()}catch(E){throw M0.error(`[auth] Session persistence failed for user#${G.id}: ${E.message}`),new g0(500,"Session could not be created. Ensure the `sessions` table exists (run `./buddy migrate`).")}return{user:G,sessionId:Y}}async function r$($){M0.debug("[auth] Session destroyed");try{await T.deleteFrom("sessions").where("id","=",$).execute()}catch(W){M0.debug(`[auth] Session destroy failed: ${W.message}`)}}async function h0($){try{await T.deleteFrom("sessions").where("user_id","=",$).execute()}catch(W){let Z=W instanceof Error?W.message:String(W);if(Z.includes("sessions")&&/no such table|does not exist|doesn't exist/i.test(Z))return;throw W}}async function p0($){try{let W=await T.selectFrom("sessions").where("id","=",$).selectAll().executeTakeFirst();if(!W)return;let Z=W.expires_at?new Date(String(W.expires_at)).getTime():0;if(Date.now()>Z){await T.deleteFrom("sessions").where("id","=",$).execute();return}return await Y1.find(W.user_id)}catch{return}}async function s$($){try{let W=await T.selectFrom("sessions").where("id","=",$).selectAll().executeTakeFirst();if(!W)return!1;let Z=W.expires_at?new Date(String(W.expires_at)).getTime():0;if(Date.now()>Z)return await T.deleteFrom("sessions").where("id","=",$).execute(),!1;return!0}catch{return!1}}async function e$($,W=86400000){try{let Z=await T.selectFrom("sessions").where("id","=",$).selectAll().executeTakeFirst();if(!Z)return!1;let J=Z.expires_at?new Date(String(Z.expires_at)).getTime():0;if(Date.now()>J)return await T.deleteFrom("sessions").where("id","=",$).execute(),!1;let Q=new Date(Date.now()+W);return await T.updateTable("sessions").set({expires_at:Q.toISOString(),last_activity:Math.floor(Date.now()/1000)}).where("id","=",$).execute(),!0}catch{return!1}}var F2={login:a$,logout:r$,destroyAll:h0,user:p0,check:s$,refresh:e$};Y0();function m0(){return g.auth.passwordReset?.expire??60}function V1($){let W=$.expires_at;if(typeof W==="string"||W instanceof Date)return new Date(W).getTime()>Date.now();let Z=$.created_at;if(typeof Z==="string"||Z instanceof Date){let J=m0();return new Date(Z).getTime()+J*60000>Date.now()}return!1}async function JW($){let W=g.app.name||"Stacks",Z=g.app.supportEmail||g.email?.from?.address||"",J=new Date().toLocaleString("en-US",{dateStyle:"full",timeStyle:"short"});try{let{html:Q,text:G}=await F1("password-changed",{subject:`Your ${W} password has been changed`,variables:{changedAt:J,supportEmail:Z}});if(!Q&&!G){await U0.send({to:$,subject:`Your ${W} password has been changed`,text:`Your ${W} password was changed on ${J}.${Z?` If this wasn't you, contact ${Z}.`:""}`});return}await U0.send({to:$,subject:`Your ${W} password has been changed`,text:G,html:Q})}catch(Q){console.error("[PasswordReset] Failed to send password changed notification:",Q)}}function C2($){function W(){return $W(32).toString("hex")}async function Z(){let X=W(),K=await K1(X,{algorithm:"bcrypt"}),Y=m0(),L=new Date(Date.now()+Y*60000).toISOString();return await n.deleteFrom("password_resets").where("email","=",$).execute(),await n.insertInto("password_resets").values({email:$,token:K,expires_at:L}).executeTakeFirst(),X}async function J(){if(!await n.selectFrom("users").where("email","=",$).selectAll().executeTakeFirst())return;let K=await Z(),Y=m0(),L=g.app.name||"Stacks",O=g.app.url?`https://${g.app.url}`:`http://localhost:${process.env.PORT||"3000"}`,E=(g.auth.passwordReset?.url??"/password/reset/{token}?email={email}").replace("{token}",K).replace("{email}",encodeURIComponent($)),F=/^https?:\/\//.test(E)?E:`${O}${E.startsWith("/")?"":"/"}${E}`;try{let{html:j,text:C}=await F1("password-reset",{subject:`Reset Your ${L} Password`,variables:{resetUrl:F,expireMinutes:Y}});if(!j&&!C)throw Error("password-reset template missing or rendered empty");await U0.send({to:$,subject:`Reset Your ${L} Password`,text:C,html:j})}catch(j){let C=j instanceof Error?j.message:String(j);console.warn(`[PasswordReset] template render failed, sending plain-text fallback: ${C}`),await U0.send({to:$,subject:`Reset Your ${L} Password`,text:`Reset your password by visiting: ${F}
|
|
162
|
-
|
|
163
|
-
This link expires in ${Y} minutes. If you didn't request this, you can safely ignore this email.`})}}async function Q(X){let K=await n.selectFrom("password_resets").where("email","=",$).selectAll().executeTakeFirst();if(!K)return!1;if(!V1(K))return await n.deleteFrom("password_resets").where("email","=",$).execute(),!1;let Y=K.token;return await L1(X,Y)}async function G(X,K){let Y=await n.transaction(async(L)=>{let O=L,_=await O.selectFrom("password_resets").where("email","=",$).selectAll().executeTakeFirst();if(!_)return{success:!1,message:"Invalid or expired reset token"};if(!V1(_))return await O.deleteFrom("password_resets").where("email","=",$).execute(),{success:!1,message:"This password reset link has expired. Please request a new one."};let E=_.token;if(!await L1(X,E))return{success:!1,message:"Invalid or expired reset token"};let j=await O.selectFrom("users").where("email","=",$).selectAll().executeTakeFirst();if(!j)return{success:!1,message:"Invalid or expired reset token"};let C=await K1(K,{algorithm:"bcrypt"});try{await O.updateTable("users").set({password:C,password_changed_at:ZW(new Date)}).where("email","=",$).executeTakeFirst()}catch(N){let R=N instanceof Error?N.message:String(N);if(/password_changed_at|no such column|unknown column/i.test(R))WW.warn("[PasswordReset] password_changed_at column missing \u2014 run `buddy migrate`; resetting without the credential-version stamp"),await O.updateTable("users").set({password:C}).where("email","=",$).executeTakeFirst();else throw N}return await O.deleteFrom("password_resets").where("email","=",$).execute(),{success:!0,userId:Number(j.id)}});if(Y.success)return await z0(Y.userId),await h0(Y.userId),JW($).catch((L)=>{console.error("[PasswordReset] Failed to send notification:",L)}),{success:!0};return Y}return{sendEmail:J,verifyToken:Q,resetPassword:G}}import{db as QW}from"@stacksjs/database";import{HttpError as q0}from"@stacksjs/error-handling";import{User as GW}from"@stacksjs/orm";import{makeHash as XW}from"@stacksjs/security";import{db as E2}from"@stacksjs/database";import{isUniqueViolation as B1}from"@stacksjs/orm";var YW=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;async function f2($){let{email:W,password:Z,name:J}=$;if(typeof W!=="string"||W.length>254||!YW.test(W))throw new q0(422,"Email address is invalid");if(typeof Z!=="string"||Z.length<8)throw new q0(422,"Password must be at least 8 characters");let Q=await XW(Z,{algorithm:"bcrypt"}),G=await QW.transaction(async(K)=>{let Y=K;if(await Y.selectFrom("users").where("email","=",W).selectAll().executeTakeFirst())throw new q0(409,"Email already exists");try{await Y.insertInto("users").values({email:W,password:Q,name:J}).execute()}catch(_){if(B1(_))throw new q0(409,"Email already exists");throw _}let O=await Y.selectFrom("users").where("email","=",W).selectAll().executeTakeFirst();if(!O)throw Error("Failed to retrieve created user");return Number(O.id)}),X=await GW.find(G);if(!X)throw Error("Failed to retrieve created user");return{token:await x.createToken(X,"user-auth-token")}}import{request as s}from"@stacksjs/router";async function L0(){let $=s?._authenticatedUser;if($)return $;let W=s.bearerToken?.();if(!W){let Z=s.headers?.get?.("authorization")||s.headers?.get?.("Authorization");if(Z&&Z.startsWith("Bearer "))W=Z.substring(7)}if(!W)return;return await x.getUserFromToken(W)}async function g2(){return L0()}async function KW(){return!!await L0()}async function h2(){return(await L0())?.id}async function p2(){return(await L0())?.email}async function m2(){return(await L0())?.name}async function c2(){return await KW()}async function u2(){await x.logout()}async function l2(){if(s?._authenticatedUser)s._authenticatedUser=void 0}Y0();W0();W0();W0();import{log as h}from"@stacksjs/logging";import*as S0 from"@stacksjs/path";class VW{allow($){return U.allow($)}deny($,W){return U.deny($,W)}denyIf($,W){if($)return this.deny(W);return!0}denyUnless($,W){if(!$)return this.deny(W);return!0}allowIf($,W){if($)return this.allow(W);return!1}}async function FW(){let{fs:$}=await import("@stacksjs/storage"),W=S0.appPath("Policies");if(!$.existsSync(W)){h.debug("No Policies directory found");return}try{let G=(await import(S0.appPath("Gates.ts"))).policies||{};for(let[X,K]of Object.entries(G)){let Y=typeof K==="string"?K:K.policy,L=`${W}/${Y}.ts`;if($.existsSync(L)){let O=await import(L),_=O.default||O[Y];if(_)F0(X,_),h.debug(`Registered policy: ${Y} for ${X}`)}}}catch{h.debug("No Gates.ts found, using convention-based discovery")}let Z=$.readdirSync(W).filter((J)=>J.endsWith("Policy.ts"));for(let J of Z){let Q=J.replace(".ts",""),G=Q.replace("Policy",""),X=`${W}/${J}`;try{let K=await import(X),Y=K.default||K[Q];if(Y)F0(G,Y),h.debug(`Auto-discovered policy: ${Q} for ${G}`)}catch(K){h.error(`Failed to load policy ${Q}:`,K)}}}async function BW(){let{define:$,before:W,after:Z}=await Promise.resolve().then(() => (W0(),S1));try{let Q=await import(S0.appPath("Gates.ts")),G=Q.gates||Q.default?.gates||{};for(let[Y,L]of Object.entries(G))if(typeof L==="function")$(Y,L),h.debug(`Registered gate: ${Y}`);let X=Q.before||Q.default?.before||[];for(let Y of X)if(typeof Y==="function")W(Y);let K=Q.after||Q.default?.after||[];for(let Y of K)if(typeof Y==="function")Z(Y);h.debug("Gates registered successfully")}catch{h.debug("No Gates.ts found or failed to load")}}async function o2(){await BW(),await FW()}W0();async function OW($,W,...Z){return C0(W,$,...Z)}async function _W($,W,...Z){return P0(W,$,...Z)}async function zW($,W,...Z){return B0(W,$,...Z)}async function DW($,W,...Z){return v0(W,$,...Z)}async function jW($,W,...Z){return E0(W,$,...Z)}async function s2($,W,...Z){return e(W,$,...Z)}function e2($){return Object.assign($,{can:(Z,...J)=>OW($,Z,...J),cannot:(Z,...J)=>_W($,Z,...J),canAny:(Z,...J)=>zW($,Z,...J),canAll:(Z,...J)=>DW($,Z,...J),authorize:(Z,...J)=>jW($,Z,...J)})}class x0{max;map=new Map;constructor($){this.max=$}get($){return this.map.get($)}has($){return this.map.has($)}set($,W){if(this.map.has($))this.map.delete($);if(this.map.set($,W),this.map.size>this.max){let Z=this.map.keys().next().value;if(Z!==void 0)this.map.delete(Z)}return this}delete($){return this.map.delete($)}clear(){this.map.clear()}}var c0=1e4,V={userRoles:new x0(c0),userPermissions:new x0(c0),rolePermissions:new x0(c0),roles:new Map,permissions:new Map},u0=null;function HW($){u0=$,R0()}function D(){if(!u0)throw Error("RBAC store not configured. Call setRbacStore() first.");return u0}function R0(){V.userRoles.clear(),V.userPermissions.clear(),V.rolePermissions.clear(),V.roles.clear(),V.permissions.clear()}function A($){if(typeof $==="number"){if(!Number.isFinite($)||$<=0)throw TypeError("RBAC user id must be a positive number");return $}let W=$.id;if(typeof W!=="number"||!Number.isFinite(W)||W<=0)throw TypeError(`RBAC user id must be a positive number, got ${typeof W} (${String(W)})`);return W}async function MW($,W="web",Z){let J=await D().createRole($,W,Z);return V.roles.set(`${$}:${W}`,J),J}async function p($,W="web"){let Z=`${$}:${W}`;if(V.roles.has(Z))return V.roles.get(Z);let J=await D().findRoleByName($,W);if(J)V.roles.set(Z,J);return J}async function UW($,W="web"){let Z=await p($,W);if(Z)await D().deleteRole(Z.id),V.roles.delete(`${$}:${W}`),R0()}async function qW($){return D().getAllRoles($)}async function CW($,W="web",Z){let J=await D().createPermission($,W,Z);return V.permissions.set(`${$}:${W}`,J),J}async function m($,W="web"){let Z=`${$}:${W}`;if(V.permissions.has(Z))return V.permissions.get(Z);let J=await D().findPermissionByName($,W);if(J)V.permissions.set(Z,J);return J}async function PW($,W="web"){let Z=await m($,W);if(Z)await D().deletePermission(Z.id),V.permissions.delete(`${$}:${W}`),R0()}async function vW($){return D().getAllPermissions($)}async function Z0($){let W=A($);if(V.userRoles.has(W))return V.userRoles.get(W);let Z=await D().getUserRoles(W);return V.userRoles.set(W,Z),Z}async function x1($,W,Z="web"){let J=A($),Q=await p(W,Z);if(!Q)throw Error(`Role '${W}' not found.`);await D().assignRoleToUser(J,Q.id),V.userRoles.delete(J),V.userPermissions.delete(J)}async function R1($,W,Z="web"){let J=A($),Q=await p(W,Z);if(!Q)return;await D().removeRoleFromUser(J,Q.id),V.userRoles.delete(J),V.userPermissions.delete(J)}async function EW($){let W=A($);await D().removeAllRolesFromUser(W),V.userRoles.delete(W),V.userPermissions.delete(W)}async function A1($,W,Z="web"){let J=A($),Q=[];for(let G of W){let X=await p(G,Z);if(!X)throw Error(`Role '${G}' not found.`);Q.push(X.id)}await D().syncUserRoles(J,Q),V.userRoles.delete(J),V.userPermissions.delete(J)}async function y1($,W,Z="web"){return(await Z0($)).some((Q)=>Q.name===W&&Q.guard_name===Z)}async function N1($,W,Z="web"){let J=await Z0($);return W.some((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function w1($,W,Z="web"){let J=await Z0($);return W.every((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function O0($){let W=A($);if(V.userPermissions.has(W))return V.userPermissions.get(W);let Z=await D().getUserDirectPermissions(W),J=await Z0($),Q=[];for(let K of J){let Y=await h1(K.id);Q.push(...Y)}let G=new Set,X=[];for(let K of[...Z,...Q])if(!G.has(K.id))G.add(K.id),X.push(K);return V.userPermissions.set(W,X),X}async function T1($,W,Z="web"){let J=A($),Q=await m(W,Z);if(!Q)throw Error(`Permission '${W}' not found.`);await D().assignPermissionToUser(J,Q.id),V.userPermissions.delete(J)}async function f1($,W,Z="web"){let J=A($),Q=await m(W,Z);if(!Q)return;await D().removePermissionFromUser(J,Q.id),V.userPermissions.delete(J)}async function SW($){let W=A($);await D().removeAllPermissionsFromUser(W),V.userPermissions.delete(W)}async function I1($,W,Z="web"){let J=A($),Q=[];for(let G of W){let X=await m(G,Z);if(!X)throw Error(`Permission '${G}' not found.`);Q.push(X.id)}await D().syncUserPermissions(J,Q),V.userPermissions.delete(J)}async function b1($,W,Z="web"){return(await O0($)).some((Q)=>Q.name===W&&Q.guard_name===Z)}async function k1($,W,Z="web"){let J=await O0($);return W.some((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function g1($,W,Z="web"){let J=await O0($);return W.every((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function h1($){if(V.rolePermissions.has($))return V.rolePermissions.get($);let W=await D().getRolePermissions($);return V.rolePermissions.set($,W),W}async function xW($,W,Z="web"){let J=await p($,Z);if(!J)throw Error(`Role '${$}' not found.`);let Q=await m(W,Z);if(!Q)throw Error(`Permission '${W}' not found.`);await D().assignPermissionToRole(J.id,Q.id),V.rolePermissions.delete(J.id),V.userPermissions.clear()}async function RW($,W,Z="web"){let J=await p($,Z);if(!J)return;let Q=await m(W,Z);if(!Q)return;await D().removePermissionFromRole(J.id,Q.id),V.rolePermissions.delete(J.id),V.userPermissions.clear()}async function AW($,W,Z="web"){let J=await p($,Z);if(!J)throw Error(`Role '${$}' not found.`);let Q=[];for(let G of W){let X=await m(G,Z);if(!X)throw Error(`Permission '${G}' not found.`);Q.push(X.id)}await D().syncRolePermissions(J.id,Q),V.rolePermissions.delete(J.id),V.userPermissions.clear()}function yW($){let W=A($);return Object.assign($,{hasRole:(J,Q)=>y1(W,J,Q),hasAnyRole:(J,Q)=>N1(W,J,Q),hasAllRoles:(J,Q)=>w1(W,J,Q),hasPermission:(J,Q)=>b1(W,J,Q),hasAnyPermission:(J,Q)=>k1(W,J,Q),hasAllPermissions:(J,Q)=>g1(W,J,Q),getRoles:()=>Z0(W),getPermissions:()=>O0(W),assignRole:(J,Q)=>x1(W,J,Q),removeRole:(J,Q)=>R1(W,J,Q),syncRoles:(J,Q)=>A1(W,J,Q),givePermission:(J,Q)=>T1(W,J,Q),revokePermission:(J,Q)=>f1(W,J,Q),syncPermissions:(J,Q)=>I1(W,J,Q)})}var W3={setStore:HW,flushCache:R0,createRole:MW,findRole:p,deleteRole:UW,getAllRoles:qW,createPermission:CW,findPermission:m,deletePermission:PW,getAllPermissions:vW,getUserRoles:Z0,assignRole:x1,removeRole:R1,removeAllRoles:EW,syncRoles:A1,hasRole:y1,hasAnyRole:N1,hasAllRoles:w1,getUserPermissions:O0,givePermission:T1,revokePermission:f1,revokeAllPermissions:SW,syncPermissions:I1,hasPermission:b1,hasAnyPermission:k1,hasAllPermissions:g1,getRolePermissions:h1,givePermissionToRole:xW,revokePermissionFromRole:RW,syncRolePermissions:AW,withRbac:yW};import{Buffer as p1}from"buffer";import{createHmac as c1,randomBytes as NW,timingSafeEqual as wW}from"crypto";import{config as J0}from"@stacksjs/config";import{db as i}from"@stacksjs/database";import{mail as m1,template as TW}from"@stacksjs/email";import{log as fW}from"@stacksjs/logging";function u1(){let $=J0.app.key;if(typeof $!=="string"||$.length===0)throw Error("[auth] config.app.key is not set \u2014 email-verification HMAC requires a real APP_KEY. "+"Run `./buddy key:generate` to provision one, or set the APP_KEY env var before booting the app.");return $}function IW($){let W=NW(32).toString("hex"),Z=`${$}:${W}`,J=c1("sha256",u1()).update(Z).digest("hex");return{token:W,hash:J}}function bW($,W,Z){let J=`${$}:${W}`,Q=c1("sha256",u1()).update(J).digest("hex"),G=p1.from(Q),X=p1.from(Z);if(G.length!==X.length)return!1;return wW(G,X)}function kW(){let W=(J0.auth??{}).emailVerification;if(W!=null&&typeof W==="object"){let Z=W;if(typeof Z.expire==="number")return Z.expire}return 60}function gW($,W){let Z=J0.app.url?`https://${J0.app.url}`:`http://localhost:${process.env.PORT||"3000"}`,Q=(J0.auth.emailVerification?.url??"/verify-email/{id}/{token}").replace("{id}",String($)).replace("{token}",W);return/^https?:\/\//.test(Q)?Q:`${Z}${Q.startsWith("/")?"":"/"}${Q}`}function l1($){return $.email_verified_at!=null}async function d1($){let{token:W,hash:Z}=IW($.id),J=kW(),Q=new Date(Date.now()+J*60*1000);await i.deleteFrom("email_verifications").where("user_id","=",$.id).execute(),await i.insertInto("email_verifications").values({user_id:$.id,token:Z,expires_at:Q.toISOString()}).executeTakeFirst();let G=gW($.id,W),X=J0.app.name||"Stacks";try{let{html:K,text:Y}=await TW("email-verification",{subject:`Verify Your ${X} Email Address`,variables:{verificationUrl:G,expiryMinutes:J,userName:$.name||$.email}});if(!K&&!Y)throw Error("email-verification template missing or rendered empty");await m1.send({to:$.email,subject:`Verify Your ${X} Email Address`,text:Y,html:K})}catch(K){let Y=K instanceof Error?K.message:String(K);fW.warn(`[email] Email verification template failed, using plain text fallback: ${Y}`),await m1.send({to:$.email,subject:`Verify Your ${X} Email Address`,text:`Please verify your email address by visiting: ${G}
|
|
164
|
-
|
|
165
|
-
This link expires in ${J} minutes.`})}}async function hW($,W){let Z=await i.selectFrom("email_verifications").where("user_id","=",$).selectAll().executeTakeFirst();if(!Z)return{success:!1,message:"No verification request found. Please request a new verification email."};let J=new Date(Z.expires_at);if(new Date>J)return await i.deleteFrom("email_verifications").where("user_id","=",$).execute(),{success:!1,message:"Verification link has expired. Please request a new one."};if(!bW($,W,Z.token))return{success:!1,message:"Invalid verification link."};return await i.updateTable("users").set({email_verified_at:new Date().toISOString()}).where("id","=",$).executeTakeFirst(),await i.deleteFrom("email_verifications").where("user_id","=",$).execute(),{success:!0,message:"Email verified successfully."}}async function pW($){if(l1($))return{success:!1,message:"Email is already verified."};let W=await i.selectFrom("email_verifications").where("user_id","=",$.id).selectAll().executeTakeFirst();if(W){let Z=new Date(W.created_at),J=(Date.now()-Z.getTime())/1000;if(J<60)return{success:!1,message:`Please wait ${Math.ceil(60-J)} seconds before requesting another verification email.`}}return await d1($),{success:!0,message:"Verification email sent."}}var L3={isVerified:l1,send:d1,verify:hW,resend:pW};import{randomBytes as mW}from"crypto";import{db as y}from"@stacksjs/database";var cW=300;async function n1($){let W=await y.selectFrom("users").where("id","=",$).select(["two_factor_secret","two_factor_enabled"]).executeTakeFirst();return{secret:W?.two_factor_secret??null,enabled:Boolean(W?.two_factor_enabled)}}function uW($){return Boolean($.two_factor_enabled)}function lW($,W){let Z=b0(),J=Q1($,W,Z);return{secret:Z,uri:J}}var dW=600;async function nW($,W,Z=dW){let J=new Date(Date.now()+Z*1000).toISOString();await y.deleteFrom("two_factor_pending_secrets").where("user_id","=",$).execute(),await y.insertInto("two_factor_pending_secrets").values({user_id:$,secret:W,expires_at:J}).execute()}async function iW($){let W=await y.selectFrom("two_factor_pending_secrets").where("user_id","=",$).selectAll().executeTakeFirst();if(!W)return null;await y.deleteFrom("two_factor_pending_secrets").where("user_id","=",$).execute();let Z=W.expires_at?new Date(String(W.expires_at)).getTime():0;if(Date.now()>Z)return null;return String(W.secret)}async function tW($,W,Z){if(!await k0(Z,W))return!1;return await y.updateTable("users").set({two_factor_secret:W,two_factor_enabled:!0}).where("id","=",$).executeTakeFirst(),!0}async function oW($){await y.updateTable("users").set({two_factor_secret:null,two_factor_enabled:!1}).where("id","=",$).executeTakeFirst()}async function aW($,W){let{secret:Z,enabled:J}=await n1($);if(!J||!Z)return!1;return k0(W,Z)}async function rW($,W=cW){let Z=mW(32).toString("hex"),J=new Date(Date.now()+W*1000).toISOString();return await y.deleteFrom("two_factor_challenges").where("user_id","=",$).execute(),await y.insertInto("two_factor_challenges").values({id:Z,user_id:$,expires_at:J}).execute(),Z}async function sW($){let W=await y.selectFrom("two_factor_challenges").where("id","=",$).selectAll().executeTakeFirst();if(!W)return null;await y.deleteFrom("two_factor_challenges").where("id","=",$).execute();let Z=W.expires_at?new Date(String(W.expires_at)).getTime():0;if(Date.now()>Z)return null;return Number(W.user_id)}var _3={isEnabled:uW,getState:n1,generateSetup:lW,stashPendingSecret:nW,consumePendingSecret:iW,enable:tW,disable:oW,verifyLoginCode:aW,createChallenge:rW,consumeChallenge:sW};import{config as c}from"@stacksjs/config";import{db as A0}from"@stacksjs/database";var l0="active_team";function i1($){let W=$.rolePriority??{owner:0,admin:1},Z=[...$.memberships??[]].sort((G,X)=>(W[G.role??""]??2)-(W[X.role??""]??2)),J=$.activeTeamId==null?null:Number($.activeTeamId);if(J!=null&&Number.isFinite(J)){let G=Z.find((X)=>Number(X.team_id)===J);if(G)return{teamId:J,role:G.role??null};if($.allowAnyTeam)return{teamId:J,role:"admin"}}let Q=Z[0];return Q?{teamId:Number(Q.team_id),role:Q.role??null}:{teamId:null,role:null}}function t1($){let W=$.cookies?.get(l0);if(!W)return null;let Z=Number(W);return Number.isFinite(Z)&&Z>0?Z:null}function U3($,W={}){let Z=Math.max(0,Math.floor(W.maxAgeSeconds??31536000)),J=[`${l0}=${encodeURIComponent(String($))}`,"Path=/","HttpOnly","SameSite=Lax",`Max-Age=${Z}`];if(W.secure)J.push("Secure");return J.join("; ")}function q3(){return`${l0}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`}async function eW($){let W=await $Z($);if(!W?.id)return null;let Z=await A0.selectFrom("team_members").where("user_id","=",W.id).where("status","=","active").select(["team_id","role"]).execute();if(Z.length===0)return null;let J=i1({memberships:Z.map((Q)=>({team_id:Number(Q.team_id),role:String(Q.role)})),activeTeamId:t1($)});return J.teamId!=null?{teamId:J.teamId,role:J.role??String(Z[0].role)}:null}async function C3($,W={}){let Z={user:null,teamId:null,role:null,teams:[],activeTeamId:null},J=c.auth?.default||"api",G=(c.auth?.guards?.[J]?.driver||"token")==="session"?await o1($):await a1($);if(!G?.id)return Z;let X=await A0.selectFrom("team_members").where("user_id","=",G.id).where("status","=","active").select(["team_id","role"]).execute(),K=W.allowAnyTeam?!!W.allowAnyTeam(G):!1,Y=t1($),L=i1({memberships:X.map((F)=>({team_id:Number(F.team_id),role:String(F.role)})),activeTeamId:Y,allowAnyTeam:K}),O=new Map(X.map((F)=>[Number(F.team_id),String(F.role)])),_=[];if(K)_=await A0.selectFrom("teams").select(["id","name"]).execute();else{let F=[...O.keys()];_=F.length?await A0.selectFrom("teams").whereIn("id",F).select(["id","name"]).execute():[]}let E=_.map((F)=>({id:Number(F.id),name:String(F.name),role:O.get(Number(F.id))||"viewer"})).sort((F,j)=>F.name.localeCompare(j.name));return{user:G,teamId:L.teamId,role:L.role,teams:E,activeTeamId:Y}}async function P3($){let W=await eW($);return W?W.teamId:null}async function $Z($){let W=c.auth?.default||"api",Q=((c.auth?.guards?.[W]||{driver:"token"}).driver||"token")==="session"?await o1($):await a1($);return Q?.id?Q:void 0}async function o1($){let W=$.cookies?.get("session_id");if(!W)return;return p0(W)}async function a1($){let W=c.auth?.defaultTokenName||"auth-token",Z=(typeof $.bearerToken==="function"?$.bearerToken():void 0)??$.cookies?.get(W);if(!Z)return;return x.getUserFromToken(Z)}export{yW as withRbac,e2 as withAuthorization,aW as verifyTwoFactorLoginCode,k0 as verifyTwoFactorCode,o3 as verifyTOTP,aZ as verifyRegistrationResponse,hW as verifyEmail,rZ as verifyAuthenticationResponse,j$ as validateRefreshToken,_W as userCannot,zW as userCanAny,DW as userCanAll,OW as userCan,cZ as updatePasskeyCounter,r3 as totpKeyUri,V$ as tokens,B$ as tokenCant,_$ as tokenCanAny,O$ as tokenCanAll,s0 as tokenCan,z$ as tokenAbilities,F$ as token,A1 as syncRoles,AW as syncRolePermissions,I1 as syncPermissions,lZ as storeWebAuthnChallenge,nW as stashPendingTwoFactorSecret,sZ as startRegistration,eZ as startAuthentication,HW as setRbacStore,uZ as setCurrentRegistrationOptions,p0 as sessionUser,e$ as sessionRefresh,r$ as sessionLogout,a$ as sessionLogin,h0 as sessionDestroyAll,s$ as sessionCheck,d1 as sendVerificationEmail,i1 as selectActiveTeam,l3 as seedDefaultRoles,C$ as revokeTokenById,q$ as revokeToken,H$ as revokeRefreshToken,RW as revokePermissionFromRole,f1 as revokePermission,P$ as revokeOtherTokens,A$ as revokeClient,z0 as revokeAllTokens,e0 as revokeAllRefreshTokens,SW as revokeAllPermissions,C3 as resolveTeamContext,$Z as resolveAuthenticatedUser,P3 as resolveAuthenticatedTeamId,eW as resolveAuthenticatedMembership,pW as resendVerificationEmail,R1 as removeRole,EW as removeAllRoles,BW as registerGates,f2 as register,D$ as refreshToken,l2 as refresh,F0 as policy,Z2 as platformAuthenticatorIsAvailable,C2 as passwordResets,w as parseScopes,M1 as none,m2 as name,u2 as logout,uW as isTwoFactorEnabled,u as isIssuedBeforePasswordChange,l1 as isEmailVerified,c2 as isAuthenticated,s2 as inspectUser,e as inspect,o2 as initializeAuthorization,h2 as id,y1 as hasRole,C1 as hasPolicy,b1 as hasPermission,N1 as hasAnyRole,k1 as hasAnyPermission,w1 as hasAllRoles,g1 as hasAllPermissions,q1 as has,xW as givePermissionToRole,T1 as givePermission,Z0 as getUserRoles,O0 as getUserPermissions,mZ as getUserPasskeys,l$ as getUserPasskey,n1 as getTwoFactorState,h1 as getRolePermissions,U1 as getPolicyFor,a as getPasswordChangedAt,g2 as getCurrentUser,qW as getAllRoles,vW as getAllPermissions,t1 as getActiveTeamPreference,Q1 as generateTwoFactorUri,xZ as generateTwoFactorToken,lW as generateTwoFactorSetup,b0 as generateTwoFactorSecret,a3 as generateTOTPSecret,t3 as generateTOTP,tZ as generateRegistrationOptions,oZ as generateAuthenticationOptions,R0 as flushRbacCache,v1 as flush,r0 as findToken,p as findRole,m as findPermission,x$ as findClient,tW as enableTwoFactor,p2 as email,FW as discoverPolicies,oW as disableTwoFactor,H1 as denies,UW as deleteRole,E$ as deleteRevokedTokens,U$ as deleteRevokedRefreshTokens,PW as deletePermission,v$ as deleteExpiredTokens,M$ as deleteExpiredRefreshTokens,_1 as define,l as currentAccessToken,rW as createTwoFactorChallenge,N0 as createToken,MW as createRole,fZ as createPersonalAccessClient,CW as createPermission,R$ as createClient,m3 as createBqbRbacStore,dZ as consumeWebAuthnChallenge,sW as consumeTwoFactorChallenge,iW as consumePendingTwoFactorSecret,S$ as clients,q3 as clearActiveTeamCookie,KW as check,P0 as cannot,C0 as can,U3 as buildActiveTeamCookie,W2 as browserSupportsWebAuthnAutofill,$2 as browserSupportsWebAuthn,z1 as before,jW as authorizeUser,E0 as authorize,L0 as authUser,kZ as authMiddlewareHandler,u$ as authMiddleware,x1 as assignRole,B0 as any,j1 as allows,v0 as all,D1 as after,P1 as abilities,_3 as TwoFactor,F2 as SessionAuth,W3 as Rbac,f as RateLimiter,E1 as Gate,L3 as EmailVerification,u3 as DEFAULT_ROLE_PACKS,VW as BasePolicy,U as AuthorizationResponse,V0 as AuthorizationException,x as Auth,l0 as ACTIVE_TEAM_COOKIE};
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Internal constants shared across the auth package.
|
|
3
|
-
*
|
|
4
|
-
* Lives in its own file so consumers like `authentication.ts` and
|
|
5
|
-
* `session-auth.ts` can pull the same literal without re-declaring it
|
|
6
|
-
* (stacksjs/stacks#1861 L-1).
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Pre-computed bcrypt hash of nothing — used to keep timing constant
|
|
10
|
-
* when the lookup-by-email half of a login fails. Without it, the
|
|
11
|
-
* "user not found" branch would short-circuit fast and the "wrong
|
|
12
|
-
* password" branch would always pay the bcrypt cost, leaking
|
|
13
|
-
* "does this email exist?" via response time.
|
|
14
|
-
*
|
|
15
|
-
* The hash itself is intentionally non-verifiable: nothing the
|
|
16
|
-
* attacker types will ever match it, so the dummy compare always
|
|
17
|
-
* returns false but spends the same CPU as a real one would.
|
|
18
|
-
*/
|
|
19
|
-
export declare const DUMMY_BCRYPT_HASH: '$2b$12$000000000000000000000uGByljkdFkOJRCRiYZGFOAstyLlSgTSW';
|
package/dist/middleware.d.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Built-in auth middleware handler
|
|
3
|
-
* Validates bearer token and sets the authenticated user on Auth
|
|
4
|
-
*/
|
|
5
|
-
export declare function authMiddleware(request: any): Promise<void>;
|
|
6
|
-
/**
|
|
7
|
-
* Auth middleware object with handle method (for compatibility with middleware loader)
|
|
8
|
-
* @defaultValue `{ name: 'auth' }`
|
|
9
|
-
*/
|
|
10
|
-
export declare const authMiddlewareHandler: {
|
|
11
|
-
/** @defaultValue 'auth' */
|
|
12
|
-
name: string;
|
|
13
|
-
handle: unknown
|
|
14
|
-
};
|
package/dist/passkey.d.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { User } from '@stacksjs/orm';
|
|
2
|
-
import type { Insertable } from '@stacksjs/database';
|
|
3
|
-
import type { VerifiedRegistrationResponse } from '@stacksjs/ts-auth';
|
|
4
|
-
// Re-export WebAuthn types from ts-auth
|
|
5
|
-
export type {
|
|
6
|
-
VerifiedRegistrationResponse,
|
|
7
|
-
VerifiedAuthenticationResponse,
|
|
8
|
-
RegistrationCredential,
|
|
9
|
-
AuthenticationCredential,
|
|
10
|
-
PublicKeyCredentialCreationOptions,
|
|
11
|
-
PublicKeyCredentialRequestOptions,
|
|
12
|
-
RegistrationOptions,
|
|
13
|
-
AuthenticationOptions,
|
|
14
|
-
} from '@stacksjs/ts-auth';
|
|
15
|
-
export declare function getUserPasskeys(userId: number): Promise<PasskeyAttribute[]>;
|
|
16
|
-
export declare function getUserPasskey(userId: number, passkeyId: string): Promise<PasskeyAttribute | undefined>;
|
|
17
|
-
/**
|
|
18
|
-
* Persist the post-verification authenticator counter and refresh the
|
|
19
|
-
* passkey's last-used timestamp. WebAuthn's anti-cloning guarantee
|
|
20
|
-
* depends on the relying party rejecting any authentication whose
|
|
21
|
-
* `newCounter` is **not strictly greater** than the stored value —
|
|
22
|
-
* authenticators monotonically increment their counter on every use,
|
|
23
|
-
* so a counter that doesn't advance (or goes backwards) signals a
|
|
24
|
-
* cloned or replayed credential.
|
|
25
|
-
*
|
|
26
|
-
* Returns `true` when the counter was updated successfully; `false`
|
|
27
|
-
* when the new counter is not greater than the stored one (the
|
|
28
|
-
* authentication MUST be rejected by the caller in that case).
|
|
29
|
-
* stacksjs/stacks#1861 A-4.
|
|
30
|
-
*/
|
|
31
|
-
export declare function updatePasskeyCounter(userId: number, passkeyId: string, newCounter: number): Promise<boolean>;
|
|
32
|
-
export declare function setCurrentRegistrationOptions(user: UserModel, verified: VerifiedRegistrationResponse): Promise<void>;
|
|
33
|
-
/**
|
|
34
|
-
* Persist a server-issued WebAuthn challenge for the given user +
|
|
35
|
-
* purpose. Deletes any prior challenge for the same (user, purpose)
|
|
36
|
-
* pair so a fresh `generateOptions` invalidates the previous one.
|
|
37
|
-
*
|
|
38
|
-
* The unique index on `(user_id, purpose)` enforces single-outstanding
|
|
39
|
-
* at the DB layer; this delete makes the upsert safe even on installs
|
|
40
|
-
* that ran an earlier auth:setup before the unique index existed.
|
|
41
|
-
*/
|
|
42
|
-
export declare function storeWebAuthnChallenge(userId: number, challenge: string | Uint8Array, purpose: WebAuthnChallengePurpose, ttlSeconds?: number): Promise<void>;
|
|
43
|
-
/**
|
|
44
|
-
* Read + delete (single-use) a WebAuthn challenge for the given user
|
|
45
|
-
* + purpose. Returns `null` when no outstanding challenge exists or
|
|
46
|
-
* when the stored challenge has expired.
|
|
47
|
-
*
|
|
48
|
-
* Single-use semantics matter: a successful verify must invalidate
|
|
49
|
-
* the challenge so a captured assertion can't be replayed even within
|
|
50
|
-
* the TTL window. Callers MUST treat a `null` return as a verification
|
|
51
|
-
* failure.
|
|
52
|
-
*/
|
|
53
|
-
export declare function consumeWebAuthnChallenge(userId: number, purpose: WebAuthnChallengePurpose): Promise<Uint8Array | null>;
|
|
54
|
-
export declare interface PasskeyAttribute {
|
|
55
|
-
id: string
|
|
56
|
-
cred_public_key: string
|
|
57
|
-
user_id: number
|
|
58
|
-
webauthn_user_id: string
|
|
59
|
-
counter: number
|
|
60
|
-
credential_type: string
|
|
61
|
-
device_type: string
|
|
62
|
-
backup_eligible: boolean
|
|
63
|
-
backup_status: boolean
|
|
64
|
-
transports?: string
|
|
65
|
-
created_at?: Date
|
|
66
|
-
last_used_at: string
|
|
67
|
-
}
|
|
68
|
-
declare type UserModel = NonNullable<Awaited<ReturnType<typeof User.find>>>;
|
|
69
|
-
declare type PasskeyInsertable = Insertable<PasskeyAttribute>;
|
|
70
|
-
// =============================================================================
|
|
71
|
-
// WebAuthn challenge persistence (stacksjs/stacks#1866)
|
|
72
|
-
// =============================================================================
|
|
73
|
-
//
|
|
74
|
-
// WebAuthn relying parties MUST verify the assertion's challenge
|
|
75
|
-
// against a server-issued nonce. Previously Stacks returned the
|
|
76
|
-
// challenge in `generateOptions` and trusted the client to echo it
|
|
77
|
-
// back on verify — so any attacker who captured the assertion AND the
|
|
78
|
-
// challenge could replay the response. Persisting the challenge
|
|
79
|
-
// server-side and consuming it on verify closes that gap.
|
|
80
|
-
//
|
|
81
|
-
// The default TTL is 5 minutes — long enough for slow biometric
|
|
82
|
-
// flows, short enough to bound the replay window. Override per-call
|
|
83
|
-
// via the `ttlSeconds` parameter.
|
|
84
|
-
export type WebAuthnChallengePurpose = 'registration' | 'authentication';
|
|
85
|
-
// Re-export WebAuthn functions from ts-auth
|
|
86
|
-
export {
|
|
87
|
-
generateRegistrationOptions,
|
|
88
|
-
generateAuthenticationOptions,
|
|
89
|
-
verifyRegistrationResponse,
|
|
90
|
-
verifyAuthenticationResponse,
|
|
91
|
-
// Browser-side functions (for client use)
|
|
92
|
-
startRegistration,
|
|
93
|
-
startAuthentication,
|
|
94
|
-
browserSupportsWebAuthn,
|
|
95
|
-
browserSupportsWebAuthnAutofill,
|
|
96
|
-
platformAuthenticatorIsAvailable,
|
|
97
|
-
} from '@stacksjs/ts-auth';
|
package/dist/password/reset.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
export declare function passwordResets(email: string): PasswordResetActions;
|
|
2
|
-
export declare interface PasswordResetResult {
|
|
3
|
-
success: boolean
|
|
4
|
-
message?: string
|
|
5
|
-
}
|
|
6
|
-
export declare interface PasswordResetActions {
|
|
7
|
-
sendEmail: () => Promise<void>
|
|
8
|
-
verifyToken: (token: string) => Promise<boolean>
|
|
9
|
-
resetPassword: (token: string, newPassword: string) => Promise<PasswordResetResult>
|
|
10
|
-
}
|
package/dist/policy.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { AuthorizationResponse } from './gate';
|
|
2
|
-
import type { UserModel as OrmUserModel } from '@stacksjs/orm';
|
|
3
|
-
/**
|
|
4
|
-
* Discover and register policies from app/Policies directory
|
|
5
|
-
*/
|
|
6
|
-
export declare function discoverPolicies(): Promise<void>;
|
|
7
|
-
/**
|
|
8
|
-
* Register inline gates from Gates.ts
|
|
9
|
-
*/
|
|
10
|
-
export declare function registerGates(): Promise<void>;
|
|
11
|
-
/**
|
|
12
|
-
* Initialize authorization system
|
|
13
|
-
*/
|
|
14
|
-
export declare function initializeAuthorization(): Promise<void>;
|
|
15
|
-
// Use the row/instance shape from orm so policies operate on the
|
|
16
|
-
// authenticated user object, not the User class constructor.
|
|
17
|
-
declare type UserModel = OrmUserModel;
|
|
18
|
-
export declare abstract class BasePolicy<T = any> {
|
|
19
|
-
before?(user: UserModel | null, ability: string): boolean | null | Promise<boolean | null>;
|
|
20
|
-
viewAny?(user: UserModel | null): boolean | Promise<boolean> | AuthorizationResponse;
|
|
21
|
-
view?(user: UserModel | null, model: T): boolean | Promise<boolean> | AuthorizationResponse;
|
|
22
|
-
create?(user: UserModel | null): boolean | Promise<boolean> | AuthorizationResponse;
|
|
23
|
-
update?(user: UserModel | null, model: T): boolean | Promise<boolean> | AuthorizationResponse;
|
|
24
|
-
delete?(user: UserModel | null, model: T): boolean | Promise<boolean> | AuthorizationResponse;
|
|
25
|
-
restore?(user: UserModel | null, model: T): boolean | Promise<boolean> | AuthorizationResponse;
|
|
26
|
-
forceDelete?(user: UserModel | null, model: T): boolean | Promise<boolean> | AuthorizationResponse;
|
|
27
|
-
protected allow(message?: string): AuthorizationResponse;
|
|
28
|
-
protected deny(message?: string, code?: string): AuthorizationResponse;
|
|
29
|
-
protected denyIf(condition: boolean, message?: string): AuthorizationResponse | boolean;
|
|
30
|
-
protected denyUnless(condition: boolean, message?: string): AuthorizationResponse | boolean;
|
|
31
|
-
protected allowIf(condition: boolean, message?: string): AuthorizationResponse | boolean;
|
|
32
|
-
}
|
|
33
|
-
export { AuthorizationResponse };
|
package/dist/rate-limiter.d.ts
DELETED
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
export declare interface RateLimitEntry {
|
|
2
|
-
attempts: number
|
|
3
|
-
lockedUntil: number
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Pluggable backing store for the auth rate limiter.
|
|
7
|
-
*
|
|
8
|
-
* Methods may be sync or async — the limiter awaits them either way. The
|
|
9
|
-
* default {@link MemoryStore} is process-local (fine for single-instance and
|
|
10
|
-
* dev). On a horizontally-scaled deployment the in-memory store is trivially
|
|
11
|
-
* bypassed by spreading attempts across instances, so production should swap
|
|
12
|
-
* in a shared store via `RateLimiter.useSharedStore()` (cache-backed; becomes
|
|
13
|
-
* cluster-wide when the cache driver is Redis) or a custom `useStore()`.
|
|
14
|
-
*/
|
|
15
|
-
export declare interface RateLimiterStore {
|
|
16
|
-
get: (key: string) => Promise<RateLimitEntry | undefined> | RateLimitEntry | undefined
|
|
17
|
-
set: (key: string, entry: RateLimitEntry, ttlMs: number) => Promise<void> | void
|
|
18
|
-
delete: (key: string) => Promise<void> | void
|
|
19
|
-
}
|
|
20
|
-
/** Process-local store — the default. */
|
|
21
|
-
declare class MemoryStore implements RateLimiterStore {
|
|
22
|
-
get(key: string): RateLimitEntry | undefined;
|
|
23
|
-
set(key: string, entry: RateLimitEntry): void;
|
|
24
|
-
delete(key: string): void;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Cache-backed store. Cross-instance when the configured cache driver is
|
|
28
|
-
* Redis; otherwise behaves like an in-memory store with TTL eviction. Entries
|
|
29
|
-
* carry a TTL so attempts decay automatically — no separate eviction pass.
|
|
30
|
-
*/
|
|
31
|
-
declare class CacheStore implements RateLimiterStore {
|
|
32
|
-
get(key: string): Promise<RateLimitEntry | undefined>;
|
|
33
|
-
set(key: string, entry: RateLimitEntry, ttlMs: number): Promise<void>;
|
|
34
|
-
delete(key: string): Promise<void>;
|
|
35
|
-
}
|
|
36
|
-
export declare class RateLimiter {
|
|
37
|
-
static useStore(custom: RateLimiterStore): void;
|
|
38
|
-
static useSharedStore(): void;
|
|
39
|
-
static useMemoryStore(): void;
|
|
40
|
-
static isRateLimited(email: string): Promise<boolean>;
|
|
41
|
-
static recordFailedAttempt(email: string): Promise<void>;
|
|
42
|
-
static resetAttempts(email: string): Promise<void>;
|
|
43
|
-
static validateAttempt(email: string): Promise<void>;
|
|
44
|
-
}
|
package/dist/rbac-seed.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { RoleRecord } from './rbac';
|
|
2
|
-
/**
|
|
3
|
-
* Idempotently seed the three default role packs. Existing role records
|
|
4
|
-
* with the same `(name, guard_name)` are left untouched.
|
|
5
|
-
*
|
|
6
|
-
* Throws only if the underlying `createRole` throws for a reason other
|
|
7
|
-
* than a unique-constraint race (the BqbRbacStore's `swallowDuplicate`
|
|
8
|
-
* already handles that case). Caller should typically log the error and
|
|
9
|
-
* surface it — a missing roles table means the migrations haven't run
|
|
10
|
-
* yet, which is a different bug than a seeder failure.
|
|
11
|
-
*/
|
|
12
|
-
export declare function seedDefaultRoles(): Promise<SeedDefaultRolesResult>;
|
|
13
|
-
/**
|
|
14
|
-
* The role packs every Stacks install starts with. `useRole()`'s built-in
|
|
15
|
-
* predicates (`isAdmin`, `isDev`, `isClient`) check these names verbatim,
|
|
16
|
-
* so renaming them in a project effectively unbinds the composable from
|
|
17
|
-
* its defaults — that's fine, but obvious to readers.
|
|
18
|
-
* @defaultValue `[ { name: 'admin', guard_name: 'web', description: 'Full access. Sees every dashboard surface, every model, every infra control.', }, { name: 'dev', guard_name: 'web', description: 'Developer / infra. Sees dev-mode surfaces (CI, query inspector, runner alerts) but not billing/admin-only management.', }, { name: 'client', guard_name: 'web', description: 'End user / client. Sees content, orders, profile, billing — no dev tools, no infra surfaces.', }, ]`
|
|
19
|
-
*/
|
|
20
|
-
export declare const DEFAULT_ROLE_PACKS: Array<{ name: string, guard_name: string, description: string }>;
|
|
21
|
-
export declare interface SeedDefaultRolesResult {
|
|
22
|
-
created: RoleRecord[]
|
|
23
|
-
skipped: Array<{ name: string, guard_name: string, reason: 'already_exists' }>
|
|
24
|
-
}
|
package/dist/rbac-store-bqb.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { isUniqueViolation } from '@stacksjs/orm';
|
|
2
|
-
import type { PermissionRecord, RbacStore, RoleRecord } from './rbac';
|
|
3
|
-
/**
|
|
4
|
-
* Swallow unique-constraint violations. Anything else is re-thrown so a
|
|
5
|
-
* "connection lost" mid-INSERT doesn't get silently treated as
|
|
6
|
-
* "already assigned".
|
|
7
|
-
*
|
|
8
|
-
* Exported for direct unit testing — the live pivot helpers below also use it.
|
|
9
|
-
*/
|
|
10
|
-
export declare function swallowDuplicate(err: unknown): void;
|
|
11
|
-
/**
|
|
12
|
-
* Map a raw DB row to a typed record. Both `roles` and `permissions` share
|
|
13
|
-
* the same shape, so one mapper covers both — the call site provides the
|
|
14
|
-
* generic. Exported for unit testing.
|
|
15
|
-
*/
|
|
16
|
-
export declare function toRecord<T extends RoleRecord | PermissionRecord>(row: Record<string, unknown> | undefined): T | null;
|
|
17
|
-
export declare function createBqbRbacStore(): RbacStore;
|
|
18
|
-
export { isUniqueViolation };
|