@stacksjs/auth 0.70.72 → 0.70.74

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,6 +1,6 @@
1
1
  import { User } from '@stacksjs/orm';
2
2
  import type { AuthCredentials, AuthToken, NewAccessToken, PersonalAccessToken, TokenCreateOptions } from '@stacksjs/types';
3
- declare type UserModel = InstanceType<typeof User>;
3
+ declare type UserModel = NonNullable<Awaited<ReturnType<typeof User.find>>>;
4
4
  export declare class Auth {
5
5
  static attempt(credentials: AuthCredentials): Promise<boolean>;
6
6
  static validate(credentials: AuthCredentials): Promise<boolean>;
package/dist/gate.d.ts CHANGED
@@ -105,7 +105,26 @@ export declare function flush(): void;
105
105
  * Gate facade for convenient access
106
106
  */
107
107
  export declare const Gate: {
108
-
108
+ define: typeof define;
109
+ policy: typeof policy;
110
+ before: typeof before;
111
+ after: typeof after;
112
+ allows: typeof allows;
113
+ denies: typeof denies;
114
+ can: typeof can;
115
+ cannot: typeof cannot;
116
+ any: typeof any;
117
+ all: typeof all;
118
+ none: typeof none;
119
+ authorize: typeof authorize;
120
+ inspect: typeof inspect;
121
+ has: typeof has;
122
+ hasPolicy: typeof hasPolicy;
123
+ abilities: typeof abilities;
124
+ getPolicyFor: typeof getPolicyFor;
125
+ flush: typeof flush;
126
+ AuthorizationResponse: typeof AuthorizationResponse;
127
+ AuthorizationException: typeof AuthorizationException
109
128
  };
110
129
  /**
111
130
  * Policy class interface
package/dist/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  // @bun
2
- var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null,W)}var e0=($,W)=>{for(var Z in W)X$($,Z,{get:W[Z],enumerable:!0,configurable:!0,set:K$.bind(W,Z)})};var $1=($,W)=>()=>($&&(W=$($=0)),W);var D0=import.meta.require;var K1={};e0(K1,{validateRefreshToken:()=>E$,tokens:()=>M$,tokenCant:()=>q$,tokenCanAny:()=>P$,tokenCanAll:()=>C$,tokenCan:()=>X1,tokenAbilities:()=>v$,token:()=>U$,revokeTokenById:()=>y$,revokeToken:()=>N$,revokeRefreshToken:()=>x$,revokeOtherTokens:()=>T$,revokeClient:()=>g$,revokeAllTokens:()=>j0,revokeAllRefreshTokens:()=>Y1,refreshToken:()=>S$,parseScopes:()=>f,isIssuedBeforePasswordChange:()=>d,getPasswordChangedAt:()=>s,findToken:()=>G1,findClient:()=>b$,deleteRevokedTokens:()=>w$,deleteRevokedRefreshTokens:()=>A$,deleteExpiredTokens:()=>f$,deleteExpiredRefreshTokens:()=>R$,currentAccessToken:()=>n,createToken:()=>f0,createClient:()=>k$,clients:()=>I$});import{createHash as O$,randomBytes as _$}from"crypto";import{db as B}from"@stacksjs/database";import{HttpError as X0}from"@stacksjs/error-handling";import{getCurrentRequest as z$}from"@stacksjs/router";import{env as D$}from"@stacksjs/env";import{sqlHelpers as j$}from"@stacksjs/database";function z($){return J1.param($)}function h($){return O$("sha256").update($).digest("hex")}function Q1($){let W=$.indexOf(":"),Z=W===-1?$:$.substring(0,W);return h(Z)}function Y0($=40){return _$($).toString("hex")}async function s($,W=B){if($===null||$===void 0)return null;try{let J=(await W.unsafe(`
2
+ var Y$=Object.defineProperty;var K$=($)=>$;function L$($,W){this[$]=K$.bind(null,W)}var e0=($,W)=>{for(var Z in W)Y$($,Z,{get:W[Z],enumerable:!0,configurable:!0,set:L$.bind(W,Z)})};var $1=($,W)=>()=>($&&(W=$($=0)),W);var D0=import.meta.require;var K1={};e0(K1,{validateRefreshToken:()=>x$,tokens:()=>U$,tokenCant:()=>C$,tokenCanAny:()=>v$,tokenCanAll:()=>P$,tokenCan:()=>X1,tokenAbilities:()=>S$,token:()=>q$,revokeTokenById:()=>T$,revokeToken:()=>y$,revokeRefreshToken:()=>R$,revokeOtherTokens:()=>w$,revokeClient:()=>h$,revokeAllTokens:()=>j0,revokeAllRefreshTokens:()=>Y1,refreshToken:()=>E$,parseScopes:()=>w,isIssuedBeforePasswordChange:()=>d,getPasswordChangedAt:()=>s,findToken:()=>G1,findClient:()=>k$,deleteRevokedTokens:()=>I$,deleteRevokedRefreshTokens:()=>N$,deleteExpiredTokens:()=>f$,deleteExpiredRefreshTokens:()=>A$,currentAccessToken:()=>n,createToken:()=>w0,createClient:()=>g$,clients:()=>b$});import{createHash as _$,randomBytes as z$}from"crypto";import{db as B}from"@stacksjs/database";import{HttpError as X0}from"@stacksjs/error-handling";import{getCurrentRequest as D$}from"@stacksjs/router";import{env as j$}from"@stacksjs/env";import{sqlHelpers as H$}from"@stacksjs/database";function z($){return J1.param($)}function h($){return _$("sha256").update($).digest("hex")}function Q1($){let W=$.indexOf(":"),Z=W===-1?$:$.substring(0,W);return h(Z)}function Y0($=40){return z$($).toString("hex")}async function s($,W=B){if($===null||$===void 0)return null;try{let J=(await W.unsafe(`
3
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 d($,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 M$($){return(await B.unsafe(`
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 d($,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 U$($){return(await B.unsafe(`
5
5
  SELECT t.*, c.provider as client_provider
6
6
  FROM oauth_access_tokens t
7
7
  LEFT JOIN oauth_clients c ON t.oauth_client_id = c.id
8
8
  WHERE t.user_id = ${z(1)}
9
9
  AND t.revoked = ${K0}
10
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:f(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 G1($){let W=Q1($),J=(await B.unsafe(`
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 G1($){let W=Q1($),J=(await B.unsafe(`
12
12
  SELECT * FROM oauth_access_tokens
13
13
  WHERE token = ${z(1)}
14
14
  AND revoked = ${K0}
15
15
  AND (expires_at IS NULL OR expires_at > ${U})
16
16
  LIMIT 1
17
- `,[W]))[0];if(!J)return null;if(d(J.created_at,await s(J.user_id)))return null;return{id:J.id,userId:J.user_id,clientId:J.oauth_client_id,name:J.name||"access-token",scopes:f(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 n(){let $=z$();if(!$)return null;let W=$._currentAccessToken;if(W)return W;let Z=$.bearerToken?.();if(!Z)return null;let J=await G1(Z);if(J)$._currentAccessToken=J;return J}async function X1($){let W=await n();if(!W)return!1;if(W.scopes.includes("*"))return!0;return W.scopes.includes($)}async function q$($){return!await X1($)}async function C$($){let W=await n();if(!W)return!1;if(W.scopes.includes("*"))return!0;return $.every((Z)=>W.scopes.includes(Z))}async function P$($){let W=await n();if(!W)return!1;if(W.scopes.includes("*"))return!0;return $.some((Z)=>W.scopes.includes(Z))}async function v$(){return(await n())?.scopes||[]}async function f0($,W="access-token",Z=["*"],J={}){let{expiresInMinutes:Q=60,withRefreshToken:G=!0,refreshExpiresInDays:X=30}=J,Y=(await B.unsafe(`
17
+ `,[W]))[0];if(!J)return null;if(d(J.created_at,await s(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 n(){let $=D$();if(!$)return null;let W=$._currentAccessToken;if(W)return W;let Z=$.bearerToken?.();if(!Z)return null;let J=await G1(Z);if(J)$._currentAccessToken=J;return J}async function X1($){let W=await n();if(!W)return!1;if(W.scopes.includes("*"))return!0;return W.scopes.includes($)}async function C$($){return!await X1($)}async function P$($){let W=await n();if(!W)return!1;if(W.scopes.includes("*"))return!0;return $.every((Z)=>W.scopes.includes(Z))}async function v$($){let W=await n();if(!W)return!1;if(W.scopes.includes("*"))return!0;return $.some((Z)=>W.scopes.includes(Z))}async function S$(){return(await n())?.scopes||[]}async function w0($,W="access-token",Z=["*"],J={}){let{expiresInMinutes:Q=60,withRefreshToken:G=!0,refreshExpiresInDays:X=30}=J,Y=(await B.unsafe(`
18
18
  SELECT id FROM oauth_clients WHERE personal_access_client = ${v} LIMIT 1
19
19
  `))[0];if(!Y)throw new X0(500,"No personal access client found. Run ./buddy auth:setup first.");let L=Y0(40),O=h(L),_=new Date;if(_.setMinutes(_.getMinutes()+Q),r)await B.unsafe(`
20
20
  INSERT INTO oauth_access_tokens (user_id, oauth_client_id, token, name, scopes, revoked, expires_at, created_at, updated_at)
@@ -24,13 +24,13 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
24
24
  VALUES (?, ?, ?, ?, ?, 0, ?, ${U}, ${U})
25
25
  `,[$,Y.id,O,W,JSON.stringify(Z),_.toISOString()]);let F=(await B.unsafe(`
26
26
  SELECT * FROM oauth_access_tokens WHERE token = ${z(1)} LIMIT 1
27
- `,[O]))[0];if(!F)throw new X0(500,"Failed to create access token \u2014 inserted row not found");let H={id:F.id,userId:F.user_id,clientId:F.oauth_client_id,name:F.name,scopes:f(F.scopes),revoked:!1,expiresAt:_,createdAt:new Date(F.created_at),updatedAt:new Date(F.updated_at)},P;if(G){P=Y0(40);let T=h(P),A=new Date;if(A.setDate(A.getDate()+X),r)await B.unsafe(`
27
+ `,[O]))[0];if(!F)throw new X0(500,"Failed to create access token \u2014 inserted row not found");let H={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)},P;if(G){P=Y0(40);let T=h(P),A=new Date;if(A.setDate(A.getDate()+X),r)await B.unsafe(`
28
28
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
29
29
  VALUES ($1, $2, false, $3, NOW())
30
30
  `,[H.id,T,A.toISOString()]);else await B.unsafe(`
31
31
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
32
32
  VALUES (?, ?, 0, ?, ${U})
33
- `,[H.id,T,A.toISOString()])}return{accessToken:H,plainTextToken:L,refreshToken:P,expiresIn:Q*60}}async function S$($,W={}){let{expiresInMinutes:Z=60,refreshExpiresInDays:J=30}=W,Q=h($);return await B.transaction(async(G)=>{let X=G,Y=(await X.unsafe(`
33
+ `,[H.id,T,A.toISOString()])}return{accessToken:H,plainTextToken:L,refreshToken:P,expiresIn:Q*60}}async function E$($,W={}){let{expiresInMinutes:Z=60,refreshExpiresInDays:J=30}=W,Q=h($);return await B.transaction(async(G)=>{let X=G,Y=(await X.unsafe(`
34
34
  SELECT r.*, t.user_id, t.oauth_client_id, t.name, t.scopes
35
35
  FROM oauth_refresh_tokens r
36
36
  JOIN oauth_access_tokens t ON r.access_token_id = t.id
@@ -54,19 +54,19 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
54
54
  VALUES (?, ?, ?, ?, ?, 0, ?, ${U}, ${U})
55
55
  `,[Y.user_id,Y.oauth_client_id,O,Y.name,Y.scopes,_.toISOString()]);let F=(await X.unsafe(`
56
56
  SELECT * FROM oauth_access_tokens WHERE token = ${z(1)} LIMIT 1
57
- `,[O]))[0],H={id:F.id,userId:F.user_id,clientId:F.oauth_client_id,name:F.name,scopes:f(F.scopes),revoked:!1,expiresAt:_,createdAt:new Date(F.created_at),updatedAt:new Date(F.updated_at)};if(d(F.created_at,await s(Y.user_id,X)))throw new X0(401,"Invalid or expired refresh token");let P=Y0(40),T=h(P),A=new Date;if(A.setDate(A.getDate()+J),r)await X.unsafe(`
57
+ `,[O]))[0],H={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(d(F.created_at,await s(Y.user_id,X)))throw new X0(401,"Invalid or expired refresh token");let P=Y0(40),T=h(P),A=new Date;if(A.setDate(A.getDate()+J),r)await X.unsafe(`
58
58
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
59
59
  VALUES ($1, $2, false, $3, NOW())
60
60
  `,[H.id,T,A.toISOString()]);else await X.unsafe(`
61
61
  INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
62
62
  VALUES (?, ?, 0, ?, ${U})
63
- `,[H.id,T,A.toISOString()]);return{accessToken:H,plainTextToken:L,refreshToken:P,expiresIn:Z*60}})}async function E$($){let W=h($);return(await B.unsafe(`
63
+ `,[H.id,T,A.toISOString()]);return{accessToken:H,plainTextToken:L,refreshToken:P,expiresIn:Z*60}})}async function x$($){let W=h($);return(await B.unsafe(`
64
64
  SELECT id FROM oauth_refresh_tokens
65
65
  WHERE token = ${z(1)}
66
66
  AND revoked = ${K0}
67
67
  AND (expires_at IS NULL OR expires_at > ${U})
68
68
  LIMIT 1
69
- `,[W])).length>0}async function x$($){let W=h($);await B.unsafe(`
69
+ `,[W])).length>0}async function R$($){let W=h($);await B.unsafe(`
70
70
  UPDATE oauth_refresh_tokens
71
71
  SET revoked = ${v}
72
72
  WHERE token = ${z(1)}
@@ -76,13 +76,13 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
76
76
  WHERE access_token_id IN (
77
77
  SELECT id FROM oauth_access_tokens WHERE user_id = ${z(1)}
78
78
  )
79
- `,[$])}async function R$(){let $=await B.unsafe(`
79
+ `,[$])}async function A$(){let $=await B.unsafe(`
80
80
  DELETE FROM oauth_refresh_tokens
81
81
  WHERE expires_at < ${U}
82
- `);return $?.changes||$?.rowCount||0}async function A$($=7){let W=new Date;W.setDate(W.getDate()-$);let Z=await B.unsafe(`
82
+ `);return $?.changes||$?.rowCount||0}async function N$($=7){let W=new Date;W.setDate(W.getDate()-$);let Z=await B.unsafe(`
83
83
  DELETE FROM oauth_refresh_tokens
84
84
  WHERE revoked = ${v} AND created_at < ${z(1)}
85
- `,[W.toISOString()]);return Z?.changes||Z?.rowCount||0}async function N$($){let W=Q1($);await B.unsafe(`
85
+ `,[W.toISOString()]);return Z?.changes||Z?.rowCount||0}async function y$($){let W=Q1($);await B.unsafe(`
86
86
  UPDATE oauth_refresh_tokens
87
87
  SET revoked = ${v}
88
88
  WHERE access_token_id IN (
@@ -92,7 +92,7 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
92
92
  UPDATE oauth_access_tokens
93
93
  SET revoked = ${v}, updated_at = ${U}
94
94
  WHERE token = ${z(1)}
95
- `,[W])}async function y$($){await B.unsafe(`
95
+ `,[W])}async function T$($){await B.unsafe(`
96
96
  UPDATE oauth_refresh_tokens
97
97
  SET revoked = ${v}
98
98
  WHERE access_token_id = ${z(1)}
@@ -104,7 +104,7 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
104
104
  UPDATE oauth_access_tokens
105
105
  SET revoked = ${v}, updated_at = ${U}
106
106
  WHERE user_id = ${z(1)}
107
- `,[$])}async function T$($){let W=await n();if(!W)return j0($);if(r)await B.unsafe(`
107
+ `,[$])}async function w$($){let W=await n();if(!W)return j0($);if(r)await B.unsafe(`
108
108
  UPDATE oauth_refresh_tokens
109
109
  SET revoked = true
110
110
  WHERE access_token_id IN (
@@ -132,7 +132,7 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
132
132
  `);let $=await B.unsafe(`
133
133
  DELETE FROM oauth_access_tokens
134
134
  WHERE expires_at < ${U}
135
- `);return $?.changes||$?.rowCount||0}async function w$($=7){let W=new Date;W.setDate(W.getDate()-$),await B.unsafe(`
135
+ `);return $?.changes||$?.rowCount||0}async function I$($=7){let W=new Date;W.setDate(W.getDate()-$),await B.unsafe(`
136
136
  DELETE FROM oauth_refresh_tokens
137
137
  WHERE access_token_id IN (
138
138
  SELECT id FROM oauth_access_tokens WHERE revoked = ${v} AND updated_at < ${z(1)}
@@ -140,13 +140,13 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
140
140
  `,[W.toISOString()]);let Z=await B.unsafe(`
141
141
  DELETE FROM oauth_access_tokens
142
142
  WHERE revoked = ${v} AND updated_at < ${z(1)}
143
- `,[W.toISOString()]);return Z?.changes||Z?.rowCount||0}async function I$($){return(await B.unsafe(`
143
+ `,[W.toISOString()]);return Z?.changes||Z?.rowCount||0}async function b$($){return(await B.unsafe(`
144
144
  SELECT * FROM oauth_clients
145
145
  WHERE user_id = ${z(1)} AND revoked = ${K0}
146
146
  ORDER BY created_at DESC
147
- `,[$])).map(w0)}async function b$($){let Z=(await B.unsafe(`
147
+ `,[$])).map(f0)}async function k$($){let Z=(await B.unsafe(`
148
148
  SELECT * FROM oauth_clients WHERE id = ${z(1)} LIMIT 1
149
- `,[$]))[0];return Z?w0(Z):null}async function k$($){let W=Y0(40);if(r)await B.unsafe(`
149
+ `,[$]))[0];return Z?f0(Z):null}async function g$($){let W=Y0(40);if(r)await B.unsafe(`
150
150
  INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
151
151
  VALUES ($1, $2, 'local', $3, $4, $5, false, NOW())
152
152
  `,[$.name,W,$.redirect,$.personalAccessClient||!1,$.passwordClient||!1]);else await B.unsafe(`
@@ -154,12 +154,12 @@ var X$=Object.defineProperty;var Y$=($)=>$;function K$($,W){this[$]=Y$.bind(null
154
154
  VALUES (?, ?, 'local', ?, ?, ?, 0, ${U})
155
155
  `,[$.name,W,$.redirect,$.personalAccessClient?1:0,$.passwordClient?1:0]);let Z=await B.unsafe(`
156
156
  SELECT * FROM oauth_clients WHERE secret = ${z(1)} LIMIT 1
157
- `,[W]);return{client:w0(Z[0]),plainTextSecret:W}}async function g$($){await B.unsafe(`
157
+ `,[W]);return{client:f0(Z[0]),plainTextSecret:W}}async function h$($){await B.unsafe(`
158
158
  UPDATE oauth_clients
159
159
  SET revoked = ${v}, updated_at = ${U}
160
160
  WHERE id = ${z(1)}
161
- `,[$])}function f($){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 H$,J1,r,qZ,U,v,K0,U$;var L0=$1(()=>{H$=D$.DB_CONNECTION||"sqlite",J1=j$(H$),{isPostgres:r,isMysql:qZ,now:U,boolTrue:v,boolFalse:K0}=J1;U$=n});var w1={};e0(w1,{policy:()=>O0,none:()=>x1,inspect:()=>W0,hasPolicy:()=>N1,has:()=>A1,getPolicyFor:()=>R1,flush:()=>T1,denies:()=>E1,define:()=>C1,default:()=>MW,cannot:()=>S0,can:()=>v0,before:()=>P1,authorize:()=>x0,any:()=>_0,allows:()=>S1,all:()=>E0,after:()=>v1,abilities:()=>y1,Gate:()=>f1,AuthorizationResponse:()=>q,AuthorizationException:()=>B0});class q{isAllowed;message;code;constructor($,W,Z){this.isAllowed=$,this.message=W,this.code=Z}static allow($){return new q(!0,$)}static deny($,W){return new q(!1,$||"This action is unauthorized.",W)}allowed(){return this.isAllowed}denied(){return!this.isAllowed}authorize(){if(!this.isAllowed)throw new B0(this.message||"This action is unauthorized.",this.code)}}function C1($,W){C.gates.set($,W)}function O0($,W){let Z=typeof $==="string"?$:$.name;C.policies.set(Z,W)}function P1($){C.beforeCallbacks.push($)}function v1($){C.afterCallbacks.push($)}async function S1($,W,...Z){return Z0($,W,...Z)}async function E1($,W,...Z){return!await Z0($,W,...Z)}async function v0($,W,...Z){return Z0($,W,...Z)}async function S0($,W,...Z){return!await Z0($,W,...Z)}async function _0($,W,...Z){for(let J of $)if(await Z0(J,W,...Z))return!0;return!1}async function E0($,W,...Z){for(let J of $)if(!await Z0(J,W,...Z))return!1;return!0}async function x1($,W,...Z){return!await _0($,W,...Z)}async function x0($,W,...Z){let J=await W0($,W,...Z);if(!J.isAllowed)throw new B0(J.message,J.code);return J}async function W0($,W,...Z){for(let G of C.beforeCallbacks){let X=await G(W,$,Z);if(X===!0)return q.allow();if(X===!1)return q.deny()}let J=Z[0];if(J&&typeof J==="object"){let G=J.constructor?.name,X=C.policies.get(G);if(X){let K=new X;if(K.before){let L=await K.before(W,$);if(L===!0)return q.allow();if(L===!1)return q.deny()}let Y=K[$];if(Y){let L=await Y.call(K,W,...Z);return q1(L??!1)}}}let Q=C.gates.get($);if(Q){let G=await Q(W,...Z),X=q1(G);for(let K of C.afterCallbacks){let Y=await K(W,$,X.isAllowed,Z);if(typeof Y==="boolean")return Y?q.allow():q.deny()}return X}return q.deny(`No gate or policy defined for ability: ${$}`)}async function Z0($,W,...Z){return(await W0($,W,...Z)).isAllowed}function q1($){if($ instanceof q)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 $?q.allow():q.deny()}function R1($){if(!$||typeof $!=="object")return null;let W=$.constructor?.name,Z=C.policies.get(W);if(Z)return new Z;return null}function A1($){return C.gates.has($)}function N1($){let W=typeof $==="string"?$:$.name;return C.policies.has(W)}function y1(){return Array.from(C.gates.keys())}function T1(){C.gates.clear(),C.policies.clear(),C.beforeCallbacks=[],C.afterCallbacks=[]}var B0,C,f1,MW;var J0=$1(()=>{B0=class B0 extends Error{code;status;constructor($="This action is unauthorized.",W,Z=403){super($);this.code=W;this.status=Z;this.name="AuthorizationException"}};C={gates:new Map,policies:new Map,beforeCallbacks:[],afterCallbacks:[]};f1={define:C1,policy:O0,before:P1,after:v1,allows:S1,denies:E1,can:v0,cannot:S0,any:_0,all:E0,none:x1,authorize:x0,inspect:W0,has:A1,hasPolicy:N1,abilities:y1,getPolicyFor:R1,flush:T1,AuthorizationResponse:q,AuthorizationException:B0},MW=f1});import{config as S}from"@stacksjs/config";import{db as M}from"@stacksjs/database";import{HttpError as I0}from"@stacksjs/error-handling";import{formatDate as i,User as e}from"@stacksjs/orm";import{getCurrentRequest as h$,request as b0}from"@stacksjs/router";import{Buffer as H0}from"buffer";import{createHash as p$,timingSafeEqual as k0}from"crypto";import{decrypt as L1,encrypt as m$,verifyHash as M0}from"@stacksjs/security";import{log as V1}from"@stacksjs/logging";var a="$2b$12$000000000000000000000uGByljkdFkOJRCRiYZGFOAstyLlSgTSW";import{HttpError as L$}from"@stacksjs/error-handling";var V$=5,W1=900000,F$=1e4,B$=300000;class T0{store=new Map;lastEviction=Date.now();evict(){let $=Date.now(),W=$-this.lastEviction>=B$,Z=this.store.size>=F$;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 Z1{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 g=new T0;class k{static useStore($){g=$}static useSharedStore(){g=new Z1}static useMemoryStore(){g=new T0}static async isRateLimited($){$=$.toLowerCase();let W=Date.now(),Z=await g.get($);if(!Z)return!1;if(Z.lockedUntil>0&&Z.lockedUntil<=W)return await g.delete($),!1;return Z.lockedUntil>0}static async recordFailedAttempt($){$=$.toLowerCase();let W=Date.now(),Z=await g.get($)||{attempts:0,lockedUntil:0};if(Z.attempts++,Z.attempts>=V$)Z.lockedUntil=W+W1,Z.attempts=0;await g.set($,Z,W1)}static async resetAttempts($){await g.delete($.toLowerCase())}static async validateAttempt($){if(await this.isRateLimited($))throw new L$(429,"Too many login attempts. Please try again later.")}}L0();var F1=Symbol.for("stacks.requestAuthState");function x(){let $=h$();if(!$)return null;let W=$[F1];if(!W)W={},$[F1]=W;return W}function V0($){return p$("sha256").update($).digest("hex")}class R{static getBearerToken(){let $=b0.bearerToken?.();if(!$){let W=b0.headers?.get?.("authorization")||b0.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 $=x();if($?.clientSecret)return $.clientSecret;let W=await this.getPersonalAccessClient();if($)$.clientSecret=W.secret;return W.secret}static async encryptTokenId($){return await m$(String($))}static async decryptTokenId($){try{return await L1($)}catch{try{let W=await this.getClientSecret();return await L1($,W)}catch{return null}}}static async getPersonalAccessClient(){try{let $=await M.selectFrom("oauth_clients").where("personal_access_client","=",!0).where("revoked","=",!1).selectAll().executeTakeFirst();if(!$)throw new I0(500,"No personal access client found. Please run `./buddy auth:setup` first.");return $}catch($){if($ instanceof Error&&$.message.includes("does not exist"))throw new I0(500,"OAuth tables not found. Please run `./buddy auth:setup` first.");throw $}}static async validateClient($,W){let Z=await M.selectFrom("oauth_clients").where("id","=",$).where("revoked","=",!1).selectAll().executeTakeFirst(),J=H0.from(W);if(!Z?.secret){let X=H0.alloc(Math.max(J.length,1)),K=J.length>0?J:H0.alloc(1);return k0(X,K),!1}let Q=String(Z.secret);if(Q.startsWith("$2"))return await M0(W,Q);let G=H0.from(Q);if(G.length!==J.length)return k0(G,G),!1;return k0(G,J)}static async getTokenFromId($){let W=await M.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:f(Z.scopes),abilities:f(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=S.auth.username||"email",Z=S.auth.password||"password",J=$[W];if(!J)return!1;let Q=await k.isRateLimited(J),G=await e.where("email","=",J).first(),X=$[Z]||"",K=G?.password||a,Y=await M0(X,K);if(Q)return!1;if(Y&&G){await k.resetAttempts(J);let L=x();if(L)L.authUser=G;return!0}return await k.recordFailedAttempt(J),!1}static async validate($){let W=S.auth.username||"email",Z=S.auth.password||"password",J=$[W];if(!J)return!1;let Q=await e.where("email","=",J).first(),G=$[Z]||"",X=Q?.password||a;return await M0(G,X)&&!!Q}static async login($,W){let Z=await this.attempt($),J=x()?.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 e.find($);if(!Z)return null;let J=x();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 M.selectFrom("oauth_access_tokens").where("token","=",V0($)).select(["id"]).executeTakeFirst();if(Z)await M.updateTable("oauth_refresh_tokens").set({revoked:!0}).where("access_token_id","=",Number(Z.id)).execute();await this.revokeToken($)}let W=x();if(W)W.authUser=void 0,W.currentToken=void 0}static async user(){let $=x();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=x();if(W)W.authUser=$}static async createTokenForUser($,W){let Z=W?.name??S.auth.defaultTokenName??"auth-token",J=W?.abilities??W?.scopes??S.auth.defaultAbilities??["*"],Q=W?.expiresInMinutes!==void 0?W.expiresInMinutes*60*1000:S.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((S.auth.refreshTokenExpiry??2592000000)/86400000));V1.debug(`[auth] Creating token for user#${$.id}: ${Z}`);let L=await f0($.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=S.auth.defaultTokenName||"auth-token",Z=S.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 I0(401,"Invalid client credentials");let Q=await this.attempt($),G=x()?.authUser;if(!Q||!G)return null;return{token:await this.createToken(G,"user-auth-token")}}static async validateToken($){let W=V0($),Z=await M.selectFrom("oauth_access_tokens").where("token","=",W).selectAll().executeTakeFirst();if(!Z)return!1;if(V1.debug(`[auth] Token validated for token#${Z.id}`),Z.expires_at&&new Date(String(Z.expires_at))<new Date)return await M.deleteFrom("oauth_access_tokens").where("id","=",Z.id).execute(),!1;if(Z.revoked)return!1;if(d(Z.created_at,await s(Z.user_id)))return!1;return await M.updateTable("oauth_access_tokens").set({updated_at:i(new Date)}).where("id","=",Z.id).execute(),!0}static async getUserFromToken($){let W=V0($),Z=await M.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 M.deleteFrom("oauth_access_tokens").where("id","=",Z.id).execute();return}if(Z.revoked)return;let J=x();if(J)J.currentToken=await this.getTokenFromId(Z.id)??void 0;if(await M.updateTable("oauth_access_tokens").set({updated_at:i(new Date)}).where("id","=",Z.id).execute(),!Z?.user_id)return;let Q=await e.find(Z.user_id),G=Q?.password_changed_at,X=G?new Date(String(G)):null;if(d(Z.created_at,X))return;return Q}static async currentAccessToken(){let $=x();if($?.currentToken)return $.currentToken;let W=this.getBearerToken();if(!W)return;let Z=await M.selectFrom("oauth_access_tokens").where("token","=",V0(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 M.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:f(String(J.scopes??"")),abilities:f(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 M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(new Date)}).where("token","=",V0($)).execute()}static async revokeTokenById($){await M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(new Date)}).where("id","=",$).execute()}static async revokeAllTokens($){let W=$??await this.id();if(!W)return;await M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(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 M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(new Date)}).where("user_id","=",W).where("id","!=",Z.id).execute()}static async pruneExpiredTokens(){let $=await M.deleteFrom("oauth_access_tokens").where("expires_at","<",i(new Date)).executeTakeFirst();return Number($?.numDeletedRows)||0}static async pruneRevokedTokens(){let $=await M.deleteFrom("oauth_access_tokens").where("revoked","=",!0).executeTakeFirst();return Number($?.numDeletedRows)||0}static async rotateToken($){let{findToken:W,revokeToken:Z}=await Promise.resolve().then(() => (L0(),K1)),J=await W($);if(!J)return null;let Q=J.expiresAt?J.expiresAt.getTime()-Date.now():S.auth.tokenExpiry??3600000,G=Math.max(1,Math.floor(Q/60000));await Z($);let X=await e.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=S.auth.username||"email",Z=S.auth.password||"password",J=$[W];if(!J)return!1;let Q=await e.where("email","=",J).first(),G=$[Z]||"",X=Q?.password||a;if(await M0(G,X)&&Q){let Y=x();if(Y)Y.authUser=Q;return!0}return!1}static guard($){return this}static viaRemember(){return!1}static clearState(){let $=x();if($)$.authUser=void 0,$.currentToken=void 0,$.clientSecret=void 0}}import{generateTOTP as c$,generateTOTPSecret as u$,totpKeyUri as l$,verifyTOTP as d$}from"@stacksjs/ts-auth";function g0(){return u$()}async function kZ($){return c$({secret:$})}async function h0($,W){return d$($,{secret:W})}function B1($,W,Z){let J=$||"johndoe@example.com",Q=W||"StacksJS 2fa",G=Z||g0();return l$(J,Q,G)}import{randomBytes as n$}from"crypto";import{db as U0}from"@stacksjs/database";import{formatDate as i$}from"@stacksjs/orm";import{err as t$,HttpError as o$,ok as a$}from"@stacksjs/error-handling";import{makeHash as r$}from"@stacksjs/security";async function lZ(){if((await U0.selectFrom("oauth_clients").where("personal_access_client","=",!0).where("revoked","=",!1).select(["id"]).executeTakeFirst())?.id)return t$({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=n$(40).toString("hex"),Z=await r$(W,{algorithm:"bcrypt"});if(await U0.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:i$(new Date)}).execute(),!(await U0.selectFrom("oauth_clients").where("secret","=",Z).select(["id"]).executeTakeFirst())?.id)throw new o$(500,"Failed to create personal access client");return a$(W)}async function s$($){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 R.getUserFromToken(W);if(!Z){let Q=Error("Invalid or expired authentication token.");throw Q.statusCode=401,Q}R.setUser(Z),$._authenticatedUser=Z;let J=await R.currentAccessToken();$._currentAccessToken=J}var iZ={name:"auth",handle:s$};import{db as p}from"@stacksjs/database";import{generateRegistrationOptions as J2,generateAuthenticationOptions as Q2,verifyRegistrationResponse as G2,verifyAuthenticationResponse as X2,startRegistration as Y2,startAuthentication as K2,browserSupportsWebAuthn as L2,browserSupportsWebAuthnAutofill as V2,platformAuthenticatorIsAvailable as F2}from"@stacksjs/ts-auth";async function aZ($){return await p.selectFrom("passkeys").selectAll().where("user_id","=",$).execute()}async function e$($,W){return await p.selectFrom("passkeys").selectAll().where("id","=",W).where("user_id","=",$).executeTakeFirst()}async function rZ($,W,Z){let J=await e$($,W);if(!J)return!1;let Q=Number(J.counter??0);if(Z!==0&&Z<=Q)return!1;return await p.updateTable("passkeys").set({counter:Z,last_used_at:O1()}).where("id","=",W).where("user_id","=",$).execute(),!0}async function sZ($,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:O1()};await p.insertInto("passkeys").values(Q).executeTakeFirstOrThrow()}function O1(){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 $W=300;async function eZ($,W,Z,J=$W){let Q=new Date(Date.now()+J*1000).toISOString();await p.deleteFrom("webauthn_challenges").where("user_id","=",$).where("purpose","=",Z).execute(),await p.insertInto("webauthn_challenges").values({user_id:$,challenge:W,purpose:Z,expires_at:Q}).execute()}async function $2($,W){let Z=await p.selectFrom("webauthn_challenges").where("user_id","=",$).where("purpose","=",W).selectAll().executeTakeFirst();if(!Z)return null;await p.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 String(Z.challenge)}import{randomBytes as LW}from"crypto";import{config as m}from"@stacksjs/config";import{db as t}from"@stacksjs/database";import{mail as C0,template as H1}from"@stacksjs/email";import{log as VW}from"@stacksjs/logging";import{formatDate as FW}from"@stacksjs/orm";import{makeHash as z1,verifyHash as D1}from"@stacksjs/security";import{HttpError as p0}from"@stacksjs/error-handling";import{log as q0}from"@stacksjs/logging";import{User as _1}from"@stacksjs/orm";import{verifyHash as WW}from"@stacksjs/security";import{db as w}from"@stacksjs/database";import{getCurrentRequest as ZW}from"@stacksjs/router";function JW(){let $=crypto.getRandomValues(new Uint8Array(32));return Array.from($,(W)=>W.toString(16).padStart(2,"0")).join("")}function QW($){if($)return{ip:$.ip??null,userAgent:$.userAgent??null};let W=ZW();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 GW($,W,Z){let J=($||"").toLowerCase(),Q=await k.isRateLimited(J),G=await _1.where("email",$).first(),X=G?.password||a,K=await WW(W,X);if(Q)throw new p0(429,"Too many login attempts. Please try again later.");if(!K||!G)throw await k.recordFailedAttempt(J),new p0(401,"Invalid credentials");await k.resetAttempts(J);let Y=JW(),L=new Date(Date.now()+86400000),{ip:O,userAgent:_}=QW(Z);q0.debug(`[auth] Session created for user#${G.id}`);try{await w.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 q0.error(`[auth] Session persistence failed for user#${G.id}: ${E.message}`),new p0(500,"Session could not be created. Ensure the `sessions` table exists (run `./buddy migrate`).")}return{user:G,sessionId:Y}}async function XW($){q0.debug("[auth] Session destroyed");try{await w.deleteFrom("sessions").where("id","=",$).execute()}catch(W){q0.debug(`[auth] Session destroy failed: ${W.message}`)}}async function m0($){try{await w.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 c0($){try{let W=await w.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 w.deleteFrom("sessions").where("id","=",$).execute();return}return await _1.find(W.user_id)}catch{return}}async function YW($){try{let W=await w.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 w.deleteFrom("sessions").where("id","=",$).execute(),!1;return!0}catch{return!1}}async function KW($,W=86400000){try{let Z=await w.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 w.deleteFrom("sessions").where("id","=",$).execute(),!1;let Q=new Date(Date.now()+W);return await w.updateTable("sessions").set({expires_at:Q.toISOString(),last_activity:Math.floor(Date.now()/1000)}).where("id","=",$).execute(),!0}catch{return!1}}var U2={login:GW,logout:XW,destroyAll:m0,user:c0,check:YW,refresh:KW};L0();function u0(){return m.auth.passwordReset?.expire??60}function j1($){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=u0();return new Date(Z).getTime()+J*60000>Date.now()}return!1}async function BW($){let W=m.app.name||"Stacks",Z=m.app.supportEmail||m.email?.from?.address||"",J=new Date().toLocaleString("en-US",{dateStyle:"full",timeStyle:"short"});try{let{html:Q,text:G}=await H1("password-changed",{subject:`Your ${W} password has been changed`,variables:{changedAt:J,supportEmail:Z}});if(!Q&&!G){await C0.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 C0.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 y2($){function W(){return LW(32).toString("hex")}async function Z(){let X=W(),K=await z1(X,{algorithm:"bcrypt"}),Y=u0(),L=new Date(Date.now()+Y*60000).toISOString();return await t.deleteFrom("password_resets").where("email","=",$).execute(),await t.insertInto("password_resets").values({email:$,token:K,expires_at:L}).executeTakeFirst(),X}async function J(){if(!await t.selectFrom("users").where("email","=",$).selectAll().executeTakeFirst())return;let K=await Z(),Y=u0(),L=m.app.name||"Stacks",O=m.app.url?`https://${m.app.url}`:`http://localhost:${process.env.PORT||"3000"}`,E=(m.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:H,text:P}=await H1("password-reset",{subject:`Reset Your ${L} Password`,variables:{resetUrl:F,expireMinutes:Y}});if(!H&&!P)throw Error("password-reset template missing or rendered empty");await C0.send({to:$,subject:`Reset Your ${L} Password`,text:P,html:H})}catch(H){let P=H instanceof Error?H.message:String(H);console.warn(`[PasswordReset] template render failed, sending plain-text fallback: ${P}`),await C0.send({to:$,subject:`Reset Your ${L} Password`,text:`Reset your password by visiting: ${F}
161
+ `,[$])}function w($){if(!$)return[];if(Array.isArray($))return $;try{let W=JSON.parse($);return Array.isArray(W)?W:[]}catch{return[]}}function f0($){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 M$,J1,r,CZ,U,v,K0,q$;var L0=$1(()=>{M$=j$.DB_CONNECTION||"sqlite",J1=H$(M$),{isPostgres:r,isMysql:CZ,now:U,boolTrue:v,boolFalse:K0}=J1;q$=n});var I1={};e0(I1,{policy:()=>O0,none:()=>R1,inspect:()=>W0,hasPolicy:()=>y1,has:()=>N1,getPolicyFor:()=>A1,flush:()=>w1,denies:()=>x1,define:()=>P1,default:()=>UW,cannot:()=>S0,can:()=>v0,before:()=>v1,authorize:()=>x0,any:()=>_0,allows:()=>E1,all:()=>E0,after:()=>S1,abilities:()=>T1,Gate:()=>f1,AuthorizationResponse:()=>q,AuthorizationException:()=>B0});class q{isAllowed;message;code;constructor($,W,Z){this.isAllowed=$,this.message=W,this.code=Z}static allow($){return new q(!0,$)}static deny($,W){return new q(!1,$||"This action is unauthorized.",W)}allowed(){return this.isAllowed}denied(){return!this.isAllowed}authorize(){if(!this.isAllowed)throw new B0(this.message||"This action is unauthorized.",this.code)}}function P1($,W){C.gates.set($,W)}function O0($,W){let Z=typeof $==="string"?$:$.name;C.policies.set(Z,W)}function v1($){C.beforeCallbacks.push($)}function S1($){C.afterCallbacks.push($)}async function E1($,W,...Z){return Z0($,W,...Z)}async function x1($,W,...Z){return!await Z0($,W,...Z)}async function v0($,W,...Z){return Z0($,W,...Z)}async function S0($,W,...Z){return!await Z0($,W,...Z)}async function _0($,W,...Z){for(let J of $)if(await Z0(J,W,...Z))return!0;return!1}async function E0($,W,...Z){for(let J of $)if(!await Z0(J,W,...Z))return!1;return!0}async function R1($,W,...Z){return!await _0($,W,...Z)}async function x0($,W,...Z){let J=await W0($,W,...Z);if(!J.isAllowed)throw new B0(J.message,J.code);return J}async function W0($,W,...Z){for(let G of C.beforeCallbacks){let X=await G(W,$,Z);if(X===!0)return q.allow();if(X===!1)return q.deny()}let J=Z[0];if(J&&typeof J==="object"){let G=J.constructor?.name,X=C.policies.get(G);if(X){let K=new X;if(K.before){let L=await K.before(W,$);if(L===!0)return q.allow();if(L===!1)return q.deny()}let Y=K[$];if(Y){let L=await Y.call(K,W,...Z);return C1(L??!1)}}}let Q=C.gates.get($);if(Q){let G=await Q(W,...Z),X=C1(G);for(let K of C.afterCallbacks){let Y=await K(W,$,X.isAllowed,Z);if(typeof Y==="boolean")return Y?q.allow():q.deny()}return X}return q.deny(`No gate or policy defined for ability: ${$}`)}async function Z0($,W,...Z){return(await W0($,W,...Z)).isAllowed}function C1($){if($ instanceof q)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 $?q.allow():q.deny()}function A1($){if(!$||typeof $!=="object")return null;let W=$.constructor?.name,Z=C.policies.get(W);if(Z)return new Z;return null}function N1($){return C.gates.has($)}function y1($){let W=typeof $==="string"?$:$.name;return C.policies.has(W)}function T1(){return Array.from(C.gates.keys())}function w1(){C.gates.clear(),C.policies.clear(),C.beforeCallbacks=[],C.afterCallbacks=[]}var B0,C,f1,UW;var J0=$1(()=>{B0=class B0 extends Error{code;status;constructor($="This action is unauthorized.",W,Z=403){super($);this.code=W;this.status=Z;this.name="AuthorizationException"}};C={gates:new Map,policies:new Map,beforeCallbacks:[],afterCallbacks:[]};f1={define:P1,policy:O0,before:v1,after:S1,allows:E1,denies:x1,can:v0,cannot:S0,any:_0,all:E0,none:R1,authorize:x0,inspect:W0,has:N1,hasPolicy:y1,abilities:T1,getPolicyFor:A1,flush:w1,AuthorizationResponse:q,AuthorizationException:B0},UW=f1});import{config as S}from"@stacksjs/config";import{db as M}from"@stacksjs/database";import{HttpError as I0}from"@stacksjs/error-handling";import{formatDate as i,User as e}from"@stacksjs/orm";import{getCurrentRequest as p$,request as b0}from"@stacksjs/router";import{Buffer as H0}from"buffer";import{createHash as m$,timingSafeEqual as k0}from"crypto";import{decrypt as L1,encrypt as c$,verifyHash as M0}from"@stacksjs/security";import{log as V1}from"@stacksjs/logging";var a="$2b$12$000000000000000000000uGByljkdFkOJRCRiYZGFOAstyLlSgTSW";import{HttpError as V$}from"@stacksjs/error-handling";var F$=5,W1=900000,B$=1e4,O$=300000;class T0{store=new Map;lastEviction=Date.now();evict(){let $=Date.now(),W=$-this.lastEviction>=O$,Z=this.store.size>=B$;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 Z1{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 g=new T0;class k{static useStore($){g=$}static useSharedStore(){g=new Z1}static useMemoryStore(){g=new T0}static async isRateLimited($){$=$.toLowerCase();let W=Date.now(),Z=await g.get($);if(!Z)return!1;if(Z.lockedUntil>0&&Z.lockedUntil<=W)return await g.delete($),!1;return Z.lockedUntil>0}static async recordFailedAttempt($){$=$.toLowerCase();let W=Date.now(),Z=await g.get($)||{attempts:0,lockedUntil:0};if(Z.attempts++,Z.attempts>=F$)Z.lockedUntil=W+W1,Z.attempts=0;await g.set($,Z,W1)}static async resetAttempts($){await g.delete($.toLowerCase())}static async validateAttempt($){if(await this.isRateLimited($))throw new V$(429,"Too many login attempts. Please try again later.")}}L0();var F1=Symbol.for("stacks.requestAuthState");function x(){let $=p$();if(!$)return null;let W=$[F1];if(!W)W={},$[F1]=W;return W}function V0($){return m$("sha256").update($).digest("hex")}class R{static getBearerToken(){let $=b0.bearerToken?.();if(!$){let W=b0.headers?.get?.("authorization")||b0.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 $=x();if($?.clientSecret)return $.clientSecret;let W=await this.getPersonalAccessClient();if($)$.clientSecret=W.secret;return W.secret}static async encryptTokenId($){return await c$(String($))}static async decryptTokenId($){try{return await L1($)}catch{try{let W=await this.getClientSecret();return await L1($,W)}catch{return null}}}static async getPersonalAccessClient(){try{let $=await M.selectFrom("oauth_clients").where("personal_access_client","=",!0).where("revoked","=",!1).selectAll().executeTakeFirst();if(!$)throw new I0(500,"No personal access client found. Please run `./buddy auth:setup` first.");return $}catch($){if($ instanceof Error&&$.message.includes("does not exist"))throw new I0(500,"OAuth tables not found. Please run `./buddy auth:setup` first.");throw $}}static async validateClient($,W){let Z=await M.selectFrom("oauth_clients").where("id","=",$).where("revoked","=",!1).selectAll().executeTakeFirst(),J=H0.from(W);if(!Z?.secret){let X=H0.alloc(Math.max(J.length,1)),K=J.length>0?J:H0.alloc(1);return k0(X,K),!1}let Q=String(Z.secret);if(Q.startsWith("$2"))return await M0(W,Q);let G=H0.from(Q);if(G.length!==J.length)return k0(G,G),!1;return k0(G,J)}static async getTokenFromId($){let W=await M.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=S.auth.username||"email",Z=S.auth.password||"password",J=$[W];if(!J)return!1;let Q=await k.isRateLimited(J),G=await e.where("email","=",J).first(),X=$[Z]||"",K=G?.password||a,Y=await M0(X,K);if(Q)return!1;if(Y&&G){await k.resetAttempts(J);let L=x();if(L)L.authUser=G;return!0}return await k.recordFailedAttempt(J),!1}static async validate($){let W=S.auth.username||"email",Z=S.auth.password||"password",J=$[W];if(!J)return!1;let Q=await e.where("email","=",J).first(),G=$[Z]||"",X=Q?.password||a;return await M0(G,X)&&!!Q}static async login($,W){let Z=await this.attempt($),J=x()?.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 e.find($);if(!Z)return null;let J=x();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 M.selectFrom("oauth_access_tokens").where("token","=",V0($)).select(["id"]).executeTakeFirst();if(Z)await M.updateTable("oauth_refresh_tokens").set({revoked:!0}).where("access_token_id","=",Number(Z.id)).execute();await this.revokeToken($)}let W=x();if(W)W.authUser=void 0,W.currentToken=void 0}static async user(){let $=x();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=x();if(W)W.authUser=$}static async createTokenForUser($,W){let Z=W?.name??S.auth.defaultTokenName??"auth-token",J=W?.abilities??W?.scopes??S.auth.defaultAbilities??["*"],Q=W?.expiresInMinutes!==void 0?W.expiresInMinutes*60*1000:S.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((S.auth.refreshTokenExpiry??2592000000)/86400000));V1.debug(`[auth] Creating token for user#${$.id}: ${Z}`);let L=await w0($.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=S.auth.defaultTokenName||"auth-token",Z=S.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 I0(401,"Invalid client credentials");let Q=await this.attempt($),G=x()?.authUser;if(!Q||!G)return null;return{token:await this.createToken(G,"user-auth-token")}}static async validateToken($){let W=V0($),Z=await M.selectFrom("oauth_access_tokens").where("token","=",W).selectAll().executeTakeFirst();if(!Z)return!1;if(V1.debug(`[auth] Token validated for token#${Z.id}`),Z.expires_at&&new Date(String(Z.expires_at))<new Date)return await M.deleteFrom("oauth_access_tokens").where("id","=",Z.id).execute(),!1;if(Z.revoked)return!1;if(d(Z.created_at,await s(Z.user_id)))return!1;return await M.updateTable("oauth_access_tokens").set({updated_at:i(new Date)}).where("id","=",Z.id).execute(),!0}static async getUserFromToken($){let W=V0($),Z=await M.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 M.deleteFrom("oauth_access_tokens").where("id","=",Z.id).execute();return}if(Z.revoked)return;let J=x();if(J)J.currentToken=await this.getTokenFromId(Z.id)??void 0;if(await M.updateTable("oauth_access_tokens").set({updated_at:i(new Date)}).where("id","=",Z.id).execute(),!Z?.user_id)return;let Q=await e.find(Z.user_id),G=Q?.password_changed_at,X=G?new Date(String(G)):null;if(d(Z.created_at,X))return;return Q}static async currentAccessToken(){let $=x();if($?.currentToken)return $.currentToken;let W=this.getBearerToken();if(!W)return;let Z=await M.selectFrom("oauth_access_tokens").where("token","=",V0(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 M.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 M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(new Date)}).where("token","=",V0($)).execute()}static async revokeTokenById($){await M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(new Date)}).where("id","=",$).execute()}static async revokeAllTokens($){let W=$??await this.id();if(!W)return;await M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(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 M.updateTable("oauth_access_tokens").set({revoked:!0,updated_at:i(new Date)}).where("user_id","=",W).where("id","!=",Z.id).execute()}static async pruneExpiredTokens(){let $=await M.deleteFrom("oauth_access_tokens").where("expires_at","<",i(new Date)).executeTakeFirst();return Number($?.numDeletedRows)||0}static async pruneRevokedTokens(){let $=await M.deleteFrom("oauth_access_tokens").where("revoked","=",!0).executeTakeFirst();return Number($?.numDeletedRows)||0}static async rotateToken($){let{findToken:W,revokeToken:Z}=await Promise.resolve().then(() => (L0(),K1)),J=await W($);if(!J)return null;let Q=J.expiresAt?J.expiresAt.getTime()-Date.now():S.auth.tokenExpiry??3600000,G=Math.max(1,Math.floor(Q/60000));await Z($);let X=await e.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=S.auth.username||"email",Z=S.auth.password||"password",J=$[W];if(!J)return!1;let Q=await e.where("email","=",J).first(),G=$[Z]||"",X=Q?.password||a;if(await M0(G,X)&&Q){let Y=x();if(Y)Y.authUser=Q;return!0}return!1}static guard($){return this}static viaRemember(){return!1}static clearState(){let $=x();if($)$.authUser=void 0,$.currentToken=void 0,$.clientSecret=void 0}}import{generateTOTP as u$,generateTOTPSecret as l$,totpKeyUri as d$,verifyTOTP as n$}from"@stacksjs/ts-auth";function g0(){return l$()}async function gZ($){return u$({secret:$})}async function h0($,W){return n$($,{secret:W})}function B1($,W,Z){let J=$||"johndoe@example.com",Q=W||"StacksJS 2fa",G=Z||g0();return d$(J,Q,G)}import{randomBytes as i$}from"crypto";import{db as U0}from"@stacksjs/database";import{formatDate as t$}from"@stacksjs/orm";import{err as o$,HttpError as a$,ok as r$}from"@stacksjs/error-handling";import{makeHash as s$}from"@stacksjs/security";async function dZ(){if((await U0.selectFrom("oauth_clients").where("personal_access_client","=",!0).where("revoked","=",!1).select(["id"]).executeTakeFirst())?.id)return o$({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=i$(40).toString("hex"),Z=await s$(W,{algorithm:"bcrypt"});if(await U0.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:t$(new Date)}).execute(),!(await U0.selectFrom("oauth_clients").where("secret","=",Z).select(["id"]).executeTakeFirst())?.id)throw new a$(500,"Failed to create personal access client");return r$(W)}async function e$($){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 R.getUserFromToken(W);if(!Z){let Q=Error("Invalid or expired authentication token.");throw Q.statusCode=401,Q}R.setUser(Z),$._authenticatedUser=Z;let J=await R.currentAccessToken();$._currentAccessToken=J}var tZ={name:"auth",handle:e$};import{Buffer as O1}from"buffer";import{db as p}from"@stacksjs/database";import{generateRegistrationOptions as G2,generateAuthenticationOptions as X2,verifyRegistrationResponse as Y2,verifyAuthenticationResponse as K2,startRegistration as L2,startAuthentication as V2,browserSupportsWebAuthn as F2,browserSupportsWebAuthnAutofill as B2,platformAuthenticatorIsAvailable as O2}from"@stacksjs/ts-auth";async function sZ($){return await p.selectFrom("passkeys").selectAll().where("user_id","=",$).execute()}async function $W($,W){return await p.selectFrom("passkeys").selectAll().where("id","=",W).where("user_id","=",$).executeTakeFirst()}async function eZ($,W,Z){let J=await $W($,W);if(!J)return!1;let Q=Number(J.counter??0);if(Z!==0&&Z<=Q)return!1;return await p.updateTable("passkeys").set({counter:Z,last_used_at:_1()}).where("id","=",W).where("user_id","=",$).execute(),!0}async function $2($,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:_1()};await p.insertInto("passkeys").values(Q).executeTakeFirstOrThrow()}function _1(){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 WW=300;async function W2($,W,Z,J=WW){let Q=new Date(Date.now()+J*1000).toISOString(),G=typeof W==="string"?W:O1.from(W).toString("base64url");await p.deleteFrom("webauthn_challenges").where("user_id","=",$).where("purpose","=",Z).execute(),await p.insertInto("webauthn_challenges").values({user_id:$,challenge:G,purpose:Z,expires_at:Q}).execute()}async function Z2($,W){let Z=await p.selectFrom("webauthn_challenges").where("user_id","=",$).where("purpose","=",W).selectAll().executeTakeFirst();if(!Z)return null;await p.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 O1.from(String(Z.challenge),"base64url")}import{randomBytes as VW}from"crypto";import{config as m}from"@stacksjs/config";import{db as t}from"@stacksjs/database";import{mail as C0,template as M1}from"@stacksjs/email";import{log as FW}from"@stacksjs/logging";import{formatDate as BW}from"@stacksjs/orm";import{makeHash as D1,verifyHash as j1}from"@stacksjs/security";import{HttpError as p0}from"@stacksjs/error-handling";import{log as q0}from"@stacksjs/logging";import{User as z1}from"@stacksjs/orm";import{verifyHash as ZW}from"@stacksjs/security";import{db as f}from"@stacksjs/database";import{getCurrentRequest as JW}from"@stacksjs/router";function QW(){let $=crypto.getRandomValues(new Uint8Array(32));return Array.from($,(W)=>W.toString(16).padStart(2,"0")).join("")}function GW($){if($)return{ip:$.ip??null,userAgent:$.userAgent??null};let W=JW();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 XW($,W,Z){let J=($||"").toLowerCase(),Q=await k.isRateLimited(J),G=await z1.where("email",$).first(),X=G?.password||a,K=await ZW(W,X);if(Q)throw new p0(429,"Too many login attempts. Please try again later.");if(!K||!G)throw await k.recordFailedAttempt(J),new p0(401,"Invalid credentials");await k.resetAttempts(J);let Y=QW(),L=new Date(Date.now()+86400000),{ip:O,userAgent:_}=GW(Z);q0.debug(`[auth] Session created for user#${G.id}`);try{await f.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 q0.error(`[auth] Session persistence failed for user#${G.id}: ${E.message}`),new p0(500,"Session could not be created. Ensure the `sessions` table exists (run `./buddy migrate`).")}return{user:G,sessionId:Y}}async function YW($){q0.debug("[auth] Session destroyed");try{await f.deleteFrom("sessions").where("id","=",$).execute()}catch(W){q0.debug(`[auth] Session destroy failed: ${W.message}`)}}async function m0($){try{await f.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 c0($){try{let W=await f.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 f.deleteFrom("sessions").where("id","=",$).execute();return}return await z1.find(W.user_id)}catch{return}}async function KW($){try{let W=await f.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 f.deleteFrom("sessions").where("id","=",$).execute(),!1;return!0}catch{return!1}}async function LW($,W=86400000){try{let Z=await f.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 f.deleteFrom("sessions").where("id","=",$).execute(),!1;let Q=new Date(Date.now()+W);return await f.updateTable("sessions").set({expires_at:Q.toISOString(),last_activity:Math.floor(Date.now()/1000)}).where("id","=",$).execute(),!0}catch{return!1}}var C2={login:XW,logout:YW,destroyAll:m0,user:c0,check:KW,refresh:LW};L0();function u0(){return m.auth.passwordReset?.expire??60}function H1($){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=u0();return new Date(Z).getTime()+J*60000>Date.now()}return!1}async function OW($){let W=m.app.name||"Stacks",Z=m.app.supportEmail||m.email?.from?.address||"",J=new Date().toLocaleString("en-US",{dateStyle:"full",timeStyle:"short"});try{let{html:Q,text:G}=await M1("password-changed",{subject:`Your ${W} password has been changed`,variables:{changedAt:J,supportEmail:Z}});if(!Q&&!G){await C0.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 C0.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 w2($){function W(){return VW(32).toString("hex")}async function Z(){let X=W(),K=await D1(X,{algorithm:"bcrypt"}),Y=u0(),L=new Date(Date.now()+Y*60000).toISOString();return await t.deleteFrom("password_resets").where("email","=",$).execute(),await t.insertInto("password_resets").values({email:$,token:K,expires_at:L}).executeTakeFirst(),X}async function J(){if(!await t.selectFrom("users").where("email","=",$).selectAll().executeTakeFirst())return;let K=await Z(),Y=u0(),L=m.app.name||"Stacks",O=m.app.url?`https://${m.app.url}`:`http://localhost:${process.env.PORT||"3000"}`,E=(m.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:H,text:P}=await M1("password-reset",{subject:`Reset Your ${L} Password`,variables:{resetUrl:F,expireMinutes:Y}});if(!H&&!P)throw Error("password-reset template missing or rendered empty");await C0.send({to:$,subject:`Reset Your ${L} Password`,text:P,html:H})}catch(H){let P=H instanceof Error?H.message:String(H);console.warn(`[PasswordReset] template render failed, sending plain-text fallback: ${P}`),await C0.send({to:$,subject:`Reset Your ${L} Password`,text:`Reset your password by visiting: ${F}
162
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 t.selectFrom("password_resets").where("email","=",$).selectAll().executeTakeFirst();if(!K)return!1;if(!j1(K))return await t.deleteFrom("password_resets").where("email","=",$).execute(),!1;let Y=K.token;return await D1(X,Y)}async function G(X,K){let Y=await t.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(!j1(_))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 D1(X,E))return{success:!1,message:"Invalid or expired reset token"};let H=await O.selectFrom("users").where("email","=",$).selectAll().executeTakeFirst();if(!H)return{success:!1,message:"Invalid or expired reset token"};let P=await z1(K,{algorithm:"bcrypt"});try{await O.updateTable("users").set({password:P,password_changed_at:FW(new Date)}).where("email","=",$).executeTakeFirst()}catch(T){let A=T instanceof Error?T.message:String(T);if(/password_changed_at|no such column|unknown column/i.test(A))VW.warn("[PasswordReset] password_changed_at column missing \u2014 run `buddy migrate`; resetting without the credential-version stamp"),await O.updateTable("users").set({password:P}).where("email","=",$).executeTakeFirst();else throw T}return await O.deleteFrom("password_resets").where("email","=",$).execute(),{success:!0,userId:Number(H.id)}});if(Y.success)return await j0(Y.userId),await m0(Y.userId),BW($).catch((L)=>{console.error("[PasswordReset] Failed to send notification:",L)}),{success:!0};return Y}return{sendEmail:J,verifyToken:Q,resetPassword:G}}import{db as _W}from"@stacksjs/database";import{HttpError as P0}from"@stacksjs/error-handling";import{User as zW}from"@stacksjs/orm";import{makeHash as DW}from"@stacksjs/security";import{db as D}from"@stacksjs/database";import{isUniqueViolation as t0}from"@stacksjs/orm";function i0($){if(!t0($))throw $}function I($){if(!$)return null;return{id:Number($.id),name:String($.name),guard_name:String($.guard_name),description:$.description==null?void 0:String($.description),created_at:$.created_at==null?void 0:String($.created_at),updated_at:$.updated_at==null?void 0:String($.updated_at)}}async function M1($,W,Z,J){await D.insertInto($).values({name:W,guard_name:Z,description:J??null}).execute();let Q=await D.selectFrom($).selectAll().where("name","=",W).where("guard_name","=",Z).orderBy("id","desc").limit(1).executeTakeFirst(),G=I(Q);if(!G)throw Error(`[rbac] insertAndFetch(${$}, ${W}, ${Z}) succeeded but follow-up SELECT returned nothing`);return G}async function l0($,W){let Z=D.selectFrom($).select("user_id"in W?"user_id":"role_id");for(let[Q,G]of Object.entries(W))Z=Z.where(Q,"=",G);if(await Z.limit(1).executeTakeFirst())return;try{await D.insertInto($).values(W).execute()}catch(Q){i0(Q)}}async function d0($,W){let Z=D.deleteFrom($);for(let[J,Q]of Object.entries(W))Z=Z.where(J,"=",Q);await Z.execute()}async function U1($,W,Z){await D.deleteFrom($).where(W,"=",Z).execute()}async function n0($,W,Z,J,Q){let G=Array.from(new Set(Q));await D.transaction(async(X)=>{let K=X;if(await K.deleteFrom($).where(W,"=",Z).execute(),G.length===0)return;let Y=G.map((L)=>({[W]:Z,[J]:L}));try{await K.insertInto($).values(Y).execute()}catch(L){i0(L);for(let O of G)try{await K.insertInto($).values({[W]:Z,[J]:O}).execute()}catch(_){i0(_)}}})}function OW(){return{async findRoleByName($,W="web"){let Z=await D.selectFrom("roles").selectAll().where("name","=",$).where("guard_name","=",W).limit(1).executeTakeFirst();return I(Z)},async findRoleById($){let W=await D.selectFrom("roles").selectAll().where("id","=",$).limit(1).executeTakeFirst();return I(W)},async createRole($,W="web",Z){return await M1("roles",$,W,Z)},async deleteRole($){await D.deleteFrom("user_roles").where("role_id","=",$).execute(),await D.deleteFrom("role_permissions").where("role_id","=",$).execute(),await D.deleteFrom("roles").where("id","=",$).execute()},async getAllRoles($){let W=D.selectFrom("roles").selectAll();if($)W=W.where("guard_name","=",$);return(await W.orderBy("id","asc").execute()).map((J)=>I(J)).filter(Boolean)},async findPermissionByName($,W="web"){let Z=await D.selectFrom("permissions").selectAll().where("name","=",$).where("guard_name","=",W).limit(1).executeTakeFirst();return I(Z)},async findPermissionById($){let W=await D.selectFrom("permissions").selectAll().where("id","=",$).limit(1).executeTakeFirst();return I(W)},async createPermission($,W="web",Z){return await M1("permissions",$,W,Z)},async deletePermission($){await D.deleteFrom("user_permissions").where("permission_id","=",$).execute(),await D.deleteFrom("role_permissions").where("permission_id","=",$).execute(),await D.deleteFrom("permissions").where("id","=",$).execute()},async getAllPermissions($){let W=D.selectFrom("permissions").selectAll();if($)W=W.where("guard_name","=",$);return(await W.orderBy("id","asc").execute()).map((J)=>I(J)).filter(Boolean)},async getUserRoles($){return(await D.selectFrom("roles").innerJoin("user_roles","user_roles.role_id","=","roles.id").select(["roles.id as id","roles.name as name","roles.guard_name as guard_name","roles.description as description","roles.created_at as created_at","roles.updated_at as updated_at"]).where("user_roles.user_id","=",$).orderBy("roles.id","asc").execute()).map((Z)=>I(Z)).filter(Boolean)},assignRoleToUser($,W){return l0("user_roles",{user_id:$,role_id:W})},removeRoleFromUser($,W){return d0("user_roles",{user_id:$,role_id:W})},removeAllRolesFromUser($){return U1("user_roles","user_id",$)},syncUserRoles($,W){return n0("user_roles","user_id",$,"role_id",W)},async getUserDirectPermissions($){return(await D.selectFrom("permissions").innerJoin("user_permissions","user_permissions.permission_id","=","permissions.id").select(["permissions.id as id","permissions.name as name","permissions.guard_name as guard_name","permissions.description as description","permissions.created_at as created_at","permissions.updated_at as updated_at"]).where("user_permissions.user_id","=",$).orderBy("permissions.id","asc").execute()).map((Z)=>I(Z)).filter(Boolean)},assignPermissionToUser($,W){return l0("user_permissions",{user_id:$,permission_id:W})},removePermissionFromUser($,W){return d0("user_permissions",{user_id:$,permission_id:W})},removeAllPermissionsFromUser($){return U1("user_permissions","user_id",$)},syncUserPermissions($,W){return n0("user_permissions","user_id",$,"permission_id",W)},async getRolePermissions($){return(await D.selectFrom("permissions").innerJoin("role_permissions","role_permissions.permission_id","=","permissions.id").select(["permissions.id as id","permissions.name as name","permissions.guard_name as guard_name","permissions.description as description","permissions.created_at as created_at","permissions.updated_at as updated_at"]).where("role_permissions.role_id","=",$).orderBy("permissions.id","asc").execute()).map((Z)=>I(Z)).filter(Boolean)},assignPermissionToRole($,W){return l0("role_permissions",{role_id:$,permission_id:W})},removePermissionFromRole($,W){return d0("role_permissions",{role_id:$,permission_id:W})},syncRolePermissions($,W){return n0("role_permissions","role_id",$,"permission_id",W)}}}var jW=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;async function c2($){let{email:W,password:Z,name:J}=$;if(typeof W!=="string"||W.length>254||!jW.test(W))throw new P0(422,"Email address is invalid");if(typeof Z!=="string"||Z.length<8)throw new P0(422,"Password must be at least 8 characters");let Q=await DW(Z,{algorithm:"bcrypt"}),G=await _W.transaction(async(K)=>{let Y=K;if(await Y.selectFrom("users").where("email","=",W).selectAll().executeTakeFirst())throw new P0(409,"Email already exists");try{await Y.insertInto("users").values({email:W,password:Q,name:J}).execute()}catch(_){if(t0(_))throw new P0(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 zW.find(G);if(!X)throw Error("Failed to retrieve created user");return{token:await R.createToken(X,"user-auth-token")}}import{request as $0}from"@stacksjs/router";async function F0(){let $=$0?._authenticatedUser;if($)return $;let W=$0.bearerToken?.();if(!W){let Z=$0.headers?.get?.("authorization")||$0.headers?.get?.("Authorization");if(Z&&Z.startsWith("Bearer "))W=Z.substring(7)}if(!W)return;return await R.getUserFromToken(W)}async function n2(){return F0()}async function HW(){return!!await F0()}async function i2(){return(await F0())?.id}async function t2(){return(await F0())?.email}async function o2(){return(await F0())?.name}async function a2(){return await HW()}async function r2(){await R.logout()}async function s2(){if($0?._authenticatedUser)$0._authenticatedUser=void 0}L0();J0();J0();J0();import{log as c}from"@stacksjs/logging";import*as R0 from"@stacksjs/path";class UW{allow($){return q.allow($)}deny($,W){return q.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 qW(){let{fs:$}=await import("@stacksjs/storage"),W=R0.appPath("Policies");if(!$.existsSync(W)){c.debug("No Policies directory found");return}try{let G=(await import(R0.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(_)O0(X,_),c.debug(`Registered policy: ${Y} for ${X}`)}}}catch{c.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)O0(G,Y),c.debug(`Auto-discovered policy: ${Q} for ${G}`)}catch(K){c.error(`Failed to load policy ${Q}:`,K)}}}async function CW(){let{define:$,before:W,after:Z}=await Promise.resolve().then(() => (J0(),w1));try{let Q=await import(R0.appPath("Gates.ts")),G=Q.gates||Q.default?.gates||{};for(let[Y,L]of Object.entries(G))if(typeof L==="function")$(Y,L),c.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);c.debug("Gates registered successfully")}catch{c.debug("No Gates.ts found or failed to load")}}async function J3(){await CW(),await qW()}J0();async function PW($,W,...Z){return v0(W,$,...Z)}async function vW($,W,...Z){return S0(W,$,...Z)}async function SW($,W,...Z){return _0(W,$,...Z)}async function EW($,W,...Z){return E0(W,$,...Z)}async function xW($,W,...Z){return x0(W,$,...Z)}async function X3($,W,...Z){return W0(W,$,...Z)}function Y3($){return Object.assign($,{can:(Z,...J)=>PW($,Z,...J),cannot:(Z,...J)=>vW($,Z,...J),canAny:(Z,...J)=>SW($,Z,...J),canAll:(Z,...J)=>EW($,Z,...J),authorize:(Z,...J)=>xW($,Z,...J)})}class A0{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 o0=1e4,V={userRoles:new A0(o0),userPermissions:new A0(o0),rolePermissions:new A0(o0),roles:new Map,permissions:new Map},a0=null;function RW($){a0=$,N0()}function j(){if(!a0)throw Error("RBAC store not configured. Call setRbacStore() first.");return a0}function N0(){V.userRoles.clear(),V.userPermissions.clear(),V.rolePermissions.clear(),V.roles.clear(),V.permissions.clear()}function N($){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 r0($,W="web",Z){let J=await j().createRole($,W,Z);return V.roles.set(`${$}:${W}`,J),J}async function b($,W="web"){let Z=`${$}:${W}`;if(V.roles.has(Z))return V.roles.get(Z);let J=await j().findRoleByName($,W);if(J)V.roles.set(Z,J);return J}async function AW($,W="web"){let Z=await b($,W);if(Z)await j().deleteRole(Z.id),V.roles.delete(`${$}:${W}`),N0()}async function NW($){return j().getAllRoles($)}async function yW($,W="web",Z){let J=await j().createPermission($,W,Z);return V.permissions.set(`${$}:${W}`,J),J}async function u($,W="web"){let Z=`${$}:${W}`;if(V.permissions.has(Z))return V.permissions.get(Z);let J=await j().findPermissionByName($,W);if(J)V.permissions.set(Z,J);return J}async function TW($,W="web"){let Z=await u($,W);if(Z)await j().deletePermission(Z.id),V.permissions.delete(`${$}:${W}`),N0()}async function fW($){return j().getAllPermissions($)}async function Q0($){let W=N($);if(V.userRoles.has(W))return V.userRoles.get(W);let Z=await j().getUserRoles(W);return V.userRoles.set(W,Z),Z}async function I1($,W,Z="web"){let J=N($),Q=await b(W,Z);if(!Q)throw Error(`Role '${W}' not found.`);await j().assignRoleToUser(J,Q.id),V.userRoles.delete(J),V.userPermissions.delete(J)}async function b1($,W,Z="web"){let J=N($),Q=await b(W,Z);if(!Q)return;await j().removeRoleFromUser(J,Q.id),V.userRoles.delete(J),V.userPermissions.delete(J)}async function wW($){let W=N($);await j().removeAllRolesFromUser(W),V.userRoles.delete(W),V.userPermissions.delete(W)}async function k1($,W,Z="web"){let J=N($),Q=[];for(let G of W){let X=await b(G,Z);if(!X)throw Error(`Role '${G}' not found.`);Q.push(X.id)}await j().syncUserRoles(J,Q),V.userRoles.delete(J),V.userPermissions.delete(J)}async function g1($,W,Z="web"){return(await Q0($)).some((Q)=>Q.name===W&&Q.guard_name===Z)}async function h1($,W,Z="web"){let J=await Q0($);return W.some((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function p1($,W,Z="web"){let J=await Q0($);return W.every((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function z0($){let W=N($);if(V.userPermissions.has(W))return V.userPermissions.get(W);let Z=await j().getUserDirectPermissions(W),J=await Q0($),Q=[];for(let K of J){let Y=await i1(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 m1($,W,Z="web"){let J=N($),Q=await u(W,Z);if(!Q)throw Error(`Permission '${W}' not found.`);await j().assignPermissionToUser(J,Q.id),V.userPermissions.delete(J)}async function c1($,W,Z="web"){let J=N($),Q=await u(W,Z);if(!Q)return;await j().removePermissionFromUser(J,Q.id),V.userPermissions.delete(J)}async function IW($){let W=N($);await j().removeAllPermissionsFromUser(W),V.userPermissions.delete(W)}async function u1($,W,Z="web"){let J=N($),Q=[];for(let G of W){let X=await u(G,Z);if(!X)throw Error(`Permission '${G}' not found.`);Q.push(X.id)}await j().syncUserPermissions(J,Q),V.userPermissions.delete(J)}async function l1($,W,Z="web"){return(await z0($)).some((Q)=>Q.name===W&&Q.guard_name===Z)}async function d1($,W,Z="web"){let J=await z0($);return W.some((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function n1($,W,Z="web"){let J=await z0($);return W.every((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function i1($){if(V.rolePermissions.has($))return V.rolePermissions.get($);let W=await j().getRolePermissions($);return V.rolePermissions.set($,W),W}async function bW($,W,Z="web"){let J=await b($,Z);if(!J)throw Error(`Role '${$}' not found.`);let Q=await u(W,Z);if(!Q)throw Error(`Permission '${W}' not found.`);await j().assignPermissionToRole(J.id,Q.id),V.rolePermissions.delete(J.id),V.userPermissions.clear()}async function kW($,W,Z="web"){let J=await b($,Z);if(!J)return;let Q=await u(W,Z);if(!Q)return;await j().removePermissionFromRole(J.id,Q.id),V.rolePermissions.delete(J.id),V.userPermissions.clear()}async function gW($,W,Z="web"){let J=await b($,Z);if(!J)throw Error(`Role '${$}' not found.`);let Q=[];for(let G of W){let X=await u(G,Z);if(!X)throw Error(`Permission '${G}' not found.`);Q.push(X.id)}await j().syncRolePermissions(J.id,Q),V.rolePermissions.delete(J.id),V.userPermissions.clear()}function hW($){let W=N($);return Object.assign($,{hasRole:(J,Q)=>g1(W,J,Q),hasAnyRole:(J,Q)=>h1(W,J,Q),hasAllRoles:(J,Q)=>p1(W,J,Q),hasPermission:(J,Q)=>l1(W,J,Q),hasAnyPermission:(J,Q)=>d1(W,J,Q),hasAllPermissions:(J,Q)=>n1(W,J,Q),getRoles:()=>Q0(W),getPermissions:()=>z0(W),assignRole:(J,Q)=>I1(W,J,Q),removeRole:(J,Q)=>b1(W,J,Q),syncRoles:(J,Q)=>k1(W,J,Q),givePermission:(J,Q)=>m1(W,J,Q),revokePermission:(J,Q)=>c1(W,J,Q),syncPermissions:(J,Q)=>u1(W,J,Q)})}var L3={setStore:RW,flushCache:N0,createRole:r0,findRole:b,deleteRole:AW,getAllRoles:NW,createPermission:yW,findPermission:u,deletePermission:TW,getAllPermissions:fW,getUserRoles:Q0,assignRole:I1,removeRole:b1,removeAllRoles:wW,syncRoles:k1,hasRole:g1,hasAnyRole:h1,hasAllRoles:p1,getUserPermissions:z0,givePermission:m1,revokePermission:c1,revokeAllPermissions:IW,syncPermissions:u1,hasPermission:l1,hasAnyPermission:d1,hasAllPermissions:n1,getRolePermissions:i1,givePermissionToRole:bW,revokePermissionFromRole:kW,syncRolePermissions:gW,withRbac:hW};var t1=[{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 \u2014 no dev tools, no infra surfaces."}];async function pW(){let $=[],W=[];for(let Z of t1){if(await b(Z.name,Z.guard_name)){W.push({name:Z.name,guard_name:Z.guard_name,reason:"already_exists"});continue}let Q=await r0(Z.name,Z.guard_name,Z.description);$.push(Q)}return{created:$,skipped:W}}import{Buffer as o1}from"buffer";import{createHmac as r1,randomBytes as mW,timingSafeEqual as cW}from"crypto";import{config as G0}from"@stacksjs/config";import{db as o}from"@stacksjs/database";import{mail as a1,template as uW}from"@stacksjs/email";import{log as lW}from"@stacksjs/logging";function s1(){let $=G0.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 dW($){let W=mW(32).toString("hex"),Z=`${$}:${W}`,J=r1("sha256",s1()).update(Z).digest("hex");return{token:W,hash:J}}function nW($,W,Z){let J=`${$}:${W}`,Q=r1("sha256",s1()).update(J).digest("hex"),G=o1.from(Q),X=o1.from(Z);if(G.length!==X.length)return!1;return cW(G,X)}function iW(){let W=(G0.auth??{}).emailVerification;if(W!=null&&typeof W==="object"){let Z=W;if(typeof Z.expire==="number")return Z.expire}return 60}function tW($,W){let Z=G0.app.url?`https://${G0.app.url}`:`http://localhost:${process.env.PORT||"3000"}`,Q=(G0.auth.emailVerification?.url??"/verify-email/{id}/{token}").replace("{id}",String($)).replace("{token}",W);return/^https?:\/\//.test(Q)?Q:`${Z}${Q.startsWith("/")?"":"/"}${Q}`}function e1($){return $.email_verified_at!=null}async function $$($){let{token:W,hash:Z}=dW($.id),J=iW(),Q=new Date(Date.now()+J*60*1000);await o.deleteFrom("email_verifications").where("user_id","=",$.id).execute(),await o.insertInto("email_verifications").values({user_id:$.id,token:Z,expires_at:Q.toISOString()}).executeTakeFirst();let G=tW($.id,W),X=G0.app.name||"Stacks";try{let{html:K,text:Y}=await uW("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 a1.send({to:$.email,subject:`Verify Your ${X} Email Address`,text:Y,html:K})}catch(K){let Y=K instanceof Error?K.message:String(K);lW.warn(`[email] Email verification template failed, using plain text fallback: ${Y}`),await a1.send({to:$.email,subject:`Verify Your ${X} Email Address`,text:`Please verify your email address by visiting: ${G}
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 t.selectFrom("password_resets").where("email","=",$).selectAll().executeTakeFirst();if(!K)return!1;if(!H1(K))return await t.deleteFrom("password_resets").where("email","=",$).execute(),!1;let Y=K.token;return await j1(X,Y)}async function G(X,K){let Y=await t.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(!H1(_))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 j1(X,E))return{success:!1,message:"Invalid or expired reset token"};let H=await O.selectFrom("users").where("email","=",$).selectAll().executeTakeFirst();if(!H)return{success:!1,message:"Invalid or expired reset token"};let P=await D1(K,{algorithm:"bcrypt"});try{await O.updateTable("users").set({password:P,password_changed_at:BW(new Date)}).where("email","=",$).executeTakeFirst()}catch(T){let A=T instanceof Error?T.message:String(T);if(/password_changed_at|no such column|unknown column/i.test(A))FW.warn("[PasswordReset] password_changed_at column missing \u2014 run `buddy migrate`; resetting without the credential-version stamp"),await O.updateTable("users").set({password:P}).where("email","=",$).executeTakeFirst();else throw T}return await O.deleteFrom("password_resets").where("email","=",$).execute(),{success:!0,userId:Number(H.id)}});if(Y.success)return await j0(Y.userId),await m0(Y.userId),OW($).catch((L)=>{console.error("[PasswordReset] Failed to send notification:",L)}),{success:!0};return Y}return{sendEmail:J,verifyToken:Q,resetPassword:G}}import{db as zW}from"@stacksjs/database";import{HttpError as P0}from"@stacksjs/error-handling";import{User as DW}from"@stacksjs/orm";import{makeHash as jW}from"@stacksjs/security";import{db as D}from"@stacksjs/database";import{isUniqueViolation as t0}from"@stacksjs/orm";function i0($){if(!t0($))throw $}function I($){if(!$)return null;return{id:Number($.id),name:String($.name),guard_name:String($.guard_name),description:$.description==null?void 0:String($.description),created_at:$.created_at==null?void 0:String($.created_at),updated_at:$.updated_at==null?void 0:String($.updated_at)}}async function U1($,W,Z,J){await D.insertInto($).values({name:W,guard_name:Z,description:J??null}).execute();let Q=await D.selectFrom($).selectAll().where("name","=",W).where("guard_name","=",Z).orderBy("id","desc").limit(1).executeTakeFirst(),G=I(Q);if(!G)throw Error(`[rbac] insertAndFetch(${$}, ${W}, ${Z}) succeeded but follow-up SELECT returned nothing`);return G}async function l0($,W){let Z=D.selectFrom($).select("user_id"in W?"user_id":"role_id");for(let[Q,G]of Object.entries(W))Z=Z.where(Q,"=",G);if(await Z.limit(1).executeTakeFirst())return;try{await D.insertInto($).values(W).execute()}catch(Q){i0(Q)}}async function d0($,W){let Z=D.deleteFrom($);for(let[J,Q]of Object.entries(W))Z=Z.where(J,"=",Q);await Z.execute()}async function q1($,W,Z){await D.deleteFrom($).where(W,"=",Z).execute()}async function n0($,W,Z,J,Q){let G=Array.from(new Set(Q));await D.transaction(async(X)=>{let K=X;if(await K.deleteFrom($).where(W,"=",Z).execute(),G.length===0)return;let Y=G.map((L)=>({[W]:Z,[J]:L}));try{await K.insertInto($).values(Y).execute()}catch(L){i0(L);for(let O of G)try{await K.insertInto($).values({[W]:Z,[J]:O}).execute()}catch(_){i0(_)}}})}function _W(){return{async findRoleByName($,W="web"){let Z=await D.selectFrom("roles").selectAll().where("name","=",$).where("guard_name","=",W).limit(1).executeTakeFirst();return I(Z)},async findRoleById($){let W=await D.selectFrom("roles").selectAll().where("id","=",$).limit(1).executeTakeFirst();return I(W)},async createRole($,W="web",Z){return await U1("roles",$,W,Z)},async deleteRole($){await D.deleteFrom("user_roles").where("role_id","=",$).execute(),await D.deleteFrom("role_permissions").where("role_id","=",$).execute(),await D.deleteFrom("roles").where("id","=",$).execute()},async getAllRoles($){let W=D.selectFrom("roles").selectAll();if($)W=W.where("guard_name","=",$);return(await W.orderBy("id","asc").execute()).map((J)=>I(J)).filter(Boolean)},async findPermissionByName($,W="web"){let Z=await D.selectFrom("permissions").selectAll().where("name","=",$).where("guard_name","=",W).limit(1).executeTakeFirst();return I(Z)},async findPermissionById($){let W=await D.selectFrom("permissions").selectAll().where("id","=",$).limit(1).executeTakeFirst();return I(W)},async createPermission($,W="web",Z){return await U1("permissions",$,W,Z)},async deletePermission($){await D.deleteFrom("user_permissions").where("permission_id","=",$).execute(),await D.deleteFrom("role_permissions").where("permission_id","=",$).execute(),await D.deleteFrom("permissions").where("id","=",$).execute()},async getAllPermissions($){let W=D.selectFrom("permissions").selectAll();if($)W=W.where("guard_name","=",$);return(await W.orderBy("id","asc").execute()).map((J)=>I(J)).filter(Boolean)},async getUserRoles($){return(await D.selectFrom("roles").innerJoin("user_roles","user_roles.role_id","=","roles.id").select(["roles.id as id","roles.name as name","roles.guard_name as guard_name","roles.description as description","roles.created_at as created_at","roles.updated_at as updated_at"]).where("user_roles.user_id","=",$).orderBy("roles.id","asc").execute()).map((Z)=>I(Z)).filter(Boolean)},assignRoleToUser($,W){return l0("user_roles",{user_id:$,role_id:W})},removeRoleFromUser($,W){return d0("user_roles",{user_id:$,role_id:W})},removeAllRolesFromUser($){return q1("user_roles","user_id",$)},syncUserRoles($,W){return n0("user_roles","user_id",$,"role_id",W)},async getUserDirectPermissions($){return(await D.selectFrom("permissions").innerJoin("user_permissions","user_permissions.permission_id","=","permissions.id").select(["permissions.id as id","permissions.name as name","permissions.guard_name as guard_name","permissions.description as description","permissions.created_at as created_at","permissions.updated_at as updated_at"]).where("user_permissions.user_id","=",$).orderBy("permissions.id","asc").execute()).map((Z)=>I(Z)).filter(Boolean)},assignPermissionToUser($,W){return l0("user_permissions",{user_id:$,permission_id:W})},removePermissionFromUser($,W){return d0("user_permissions",{user_id:$,permission_id:W})},removeAllPermissionsFromUser($){return q1("user_permissions","user_id",$)},syncUserPermissions($,W){return n0("user_permissions","user_id",$,"permission_id",W)},async getRolePermissions($){return(await D.selectFrom("permissions").innerJoin("role_permissions","role_permissions.permission_id","=","permissions.id").select(["permissions.id as id","permissions.name as name","permissions.guard_name as guard_name","permissions.description as description","permissions.created_at as created_at","permissions.updated_at as updated_at"]).where("role_permissions.role_id","=",$).orderBy("permissions.id","asc").execute()).map((Z)=>I(Z)).filter(Boolean)},assignPermissionToRole($,W){return l0("role_permissions",{role_id:$,permission_id:W})},removePermissionFromRole($,W){return d0("role_permissions",{role_id:$,permission_id:W})},syncRolePermissions($,W){return n0("role_permissions","role_id",$,"permission_id",W)}}}var HW=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;async function l2($){let{email:W,password:Z,name:J}=$;if(typeof W!=="string"||W.length>254||!HW.test(W))throw new P0(422,"Email address is invalid");if(typeof Z!=="string"||Z.length<8)throw new P0(422,"Password must be at least 8 characters");let Q=await jW(Z,{algorithm:"bcrypt"}),G=await zW.transaction(async(K)=>{let Y=K;if(await Y.selectFrom("users").where("email","=",W).selectAll().executeTakeFirst())throw new P0(409,"Email already exists");try{await Y.insertInto("users").values({email:W,password:Q,name:J}).execute()}catch(_){if(t0(_))throw new P0(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 DW.find(G);if(!X)throw Error("Failed to retrieve created user");return{token:await R.createToken(X,"user-auth-token")}}import{request as $0}from"@stacksjs/router";async function F0(){let $=$0?._authenticatedUser;if($)return $;let W=$0.bearerToken?.();if(!W){let Z=$0.headers?.get?.("authorization")||$0.headers?.get?.("Authorization");if(Z&&Z.startsWith("Bearer "))W=Z.substring(7)}if(!W)return;return await R.getUserFromToken(W)}async function t2(){return F0()}async function MW(){return!!await F0()}async function o2(){return(await F0())?.id}async function a2(){return(await F0())?.email}async function r2(){return(await F0())?.name}async function s2(){return await MW()}async function e2(){await R.logout()}async function $3(){if($0?._authenticatedUser)$0._authenticatedUser=void 0}L0();J0();J0();J0();import{log as c}from"@stacksjs/logging";import*as R0 from"@stacksjs/path";class qW{allow($){return q.allow($)}deny($,W){return q.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 CW(){let{fs:$}=await import("@stacksjs/storage"),W=R0.appPath("Policies");if(!$.existsSync(W)){c.debug("No Policies directory found");return}try{let G=(await import(R0.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(_)O0(X,_),c.debug(`Registered policy: ${Y} for ${X}`)}}}catch{c.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)O0(G,Y),c.debug(`Auto-discovered policy: ${Q} for ${G}`)}catch(K){c.error(`Failed to load policy ${Q}:`,K)}}}async function PW(){let{define:$,before:W,after:Z}=await Promise.resolve().then(() => (J0(),I1));try{let Q=await import(R0.appPath("Gates.ts")),G=Q.gates||Q.default?.gates||{};for(let[Y,L]of Object.entries(G))if(typeof L==="function")$(Y,L),c.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);c.debug("Gates registered successfully")}catch{c.debug("No Gates.ts found or failed to load")}}async function G3(){await PW(),await CW()}J0();async function vW($,W,...Z){return v0(W,$,...Z)}async function SW($,W,...Z){return S0(W,$,...Z)}async function EW($,W,...Z){return _0(W,$,...Z)}async function xW($,W,...Z){return E0(W,$,...Z)}async function RW($,W,...Z){return x0(W,$,...Z)}async function K3($,W,...Z){return W0(W,$,...Z)}function L3($){return Object.assign($,{can:(Z,...J)=>vW($,Z,...J),cannot:(Z,...J)=>SW($,Z,...J),canAny:(Z,...J)=>EW($,Z,...J),canAll:(Z,...J)=>xW($,Z,...J),authorize:(Z,...J)=>RW($,Z,...J)})}class A0{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 o0=1e4,V={userRoles:new A0(o0),userPermissions:new A0(o0),rolePermissions:new A0(o0),roles:new Map,permissions:new Map},a0=null;function AW($){a0=$,N0()}function j(){if(!a0)throw Error("RBAC store not configured. Call setRbacStore() first.");return a0}function N0(){V.userRoles.clear(),V.userPermissions.clear(),V.rolePermissions.clear(),V.roles.clear(),V.permissions.clear()}function N($){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 r0($,W="web",Z){let J=await j().createRole($,W,Z);return V.roles.set(`${$}:${W}`,J),J}async function b($,W="web"){let Z=`${$}:${W}`;if(V.roles.has(Z))return V.roles.get(Z);let J=await j().findRoleByName($,W);if(J)V.roles.set(Z,J);return J}async function NW($,W="web"){let Z=await b($,W);if(Z)await j().deleteRole(Z.id),V.roles.delete(`${$}:${W}`),N0()}async function yW($){return j().getAllRoles($)}async function TW($,W="web",Z){let J=await j().createPermission($,W,Z);return V.permissions.set(`${$}:${W}`,J),J}async function u($,W="web"){let Z=`${$}:${W}`;if(V.permissions.has(Z))return V.permissions.get(Z);let J=await j().findPermissionByName($,W);if(J)V.permissions.set(Z,J);return J}async function wW($,W="web"){let Z=await u($,W);if(Z)await j().deletePermission(Z.id),V.permissions.delete(`${$}:${W}`),N0()}async function fW($){return j().getAllPermissions($)}async function Q0($){let W=N($);if(V.userRoles.has(W))return V.userRoles.get(W);let Z=await j().getUserRoles(W);return V.userRoles.set(W,Z),Z}async function b1($,W,Z="web"){let J=N($),Q=await b(W,Z);if(!Q)throw Error(`Role '${W}' not found.`);await j().assignRoleToUser(J,Q.id),V.userRoles.delete(J),V.userPermissions.delete(J)}async function k1($,W,Z="web"){let J=N($),Q=await b(W,Z);if(!Q)return;await j().removeRoleFromUser(J,Q.id),V.userRoles.delete(J),V.userPermissions.delete(J)}async function IW($){let W=N($);await j().removeAllRolesFromUser(W),V.userRoles.delete(W),V.userPermissions.delete(W)}async function g1($,W,Z="web"){let J=N($),Q=[];for(let G of W){let X=await b(G,Z);if(!X)throw Error(`Role '${G}' not found.`);Q.push(X.id)}await j().syncUserRoles(J,Q),V.userRoles.delete(J),V.userPermissions.delete(J)}async function h1($,W,Z="web"){return(await Q0($)).some((Q)=>Q.name===W&&Q.guard_name===Z)}async function p1($,W,Z="web"){let J=await Q0($);return W.some((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function m1($,W,Z="web"){let J=await Q0($);return W.every((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function z0($){let W=N($);if(V.userPermissions.has(W))return V.userPermissions.get(W);let Z=await j().getUserDirectPermissions(W),J=await Q0($),Q=[];for(let K of J){let Y=await t1(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 c1($,W,Z="web"){let J=N($),Q=await u(W,Z);if(!Q)throw Error(`Permission '${W}' not found.`);await j().assignPermissionToUser(J,Q.id),V.userPermissions.delete(J)}async function u1($,W,Z="web"){let J=N($),Q=await u(W,Z);if(!Q)return;await j().removePermissionFromUser(J,Q.id),V.userPermissions.delete(J)}async function bW($){let W=N($);await j().removeAllPermissionsFromUser(W),V.userPermissions.delete(W)}async function l1($,W,Z="web"){let J=N($),Q=[];for(let G of W){let X=await u(G,Z);if(!X)throw Error(`Permission '${G}' not found.`);Q.push(X.id)}await j().syncUserPermissions(J,Q),V.userPermissions.delete(J)}async function d1($,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 i1($,W,Z="web"){let J=await z0($);return W.every((Q)=>J.some((G)=>G.name===Q&&G.guard_name===Z))}async function t1($){if(V.rolePermissions.has($))return V.rolePermissions.get($);let W=await j().getRolePermissions($);return V.rolePermissions.set($,W),W}async function kW($,W,Z="web"){let J=await b($,Z);if(!J)throw Error(`Role '${$}' not found.`);let Q=await u(W,Z);if(!Q)throw Error(`Permission '${W}' not found.`);await j().assignPermissionToRole(J.id,Q.id),V.rolePermissions.delete(J.id),V.userPermissions.clear()}async function gW($,W,Z="web"){let J=await b($,Z);if(!J)return;let Q=await u(W,Z);if(!Q)return;await j().removePermissionFromRole(J.id,Q.id),V.rolePermissions.delete(J.id),V.userPermissions.clear()}async function hW($,W,Z="web"){let J=await b($,Z);if(!J)throw Error(`Role '${$}' not found.`);let Q=[];for(let G of W){let X=await u(G,Z);if(!X)throw Error(`Permission '${G}' not found.`);Q.push(X.id)}await j().syncRolePermissions(J.id,Q),V.rolePermissions.delete(J.id),V.userPermissions.clear()}function pW($){let W=N($);return Object.assign($,{hasRole:(J,Q)=>h1(W,J,Q),hasAnyRole:(J,Q)=>p1(W,J,Q),hasAllRoles:(J,Q)=>m1(W,J,Q),hasPermission:(J,Q)=>d1(W,J,Q),hasAnyPermission:(J,Q)=>n1(W,J,Q),hasAllPermissions:(J,Q)=>i1(W,J,Q),getRoles:()=>Q0(W),getPermissions:()=>z0(W),assignRole:(J,Q)=>b1(W,J,Q),removeRole:(J,Q)=>k1(W,J,Q),syncRoles:(J,Q)=>g1(W,J,Q),givePermission:(J,Q)=>c1(W,J,Q),revokePermission:(J,Q)=>u1(W,J,Q),syncPermissions:(J,Q)=>l1(W,J,Q)})}var F3={setStore:AW,flushCache:N0,createRole:r0,findRole:b,deleteRole:NW,getAllRoles:yW,createPermission:TW,findPermission:u,deletePermission:wW,getAllPermissions:fW,getUserRoles:Q0,assignRole:b1,removeRole:k1,removeAllRoles:IW,syncRoles:g1,hasRole:h1,hasAnyRole:p1,hasAllRoles:m1,getUserPermissions:z0,givePermission:c1,revokePermission:u1,revokeAllPermissions:bW,syncPermissions:l1,hasPermission:d1,hasAnyPermission:n1,hasAllPermissions:i1,getRolePermissions:t1,givePermissionToRole:kW,revokePermissionFromRole:gW,syncRolePermissions:hW,withRbac:pW};var o1=[{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 \u2014 no dev tools, no infra surfaces."}];async function mW(){let $=[],W=[];for(let Z of o1){if(await b(Z.name,Z.guard_name)){W.push({name:Z.name,guard_name:Z.guard_name,reason:"already_exists"});continue}let Q=await r0(Z.name,Z.guard_name,Z.description);$.push(Q)}return{created:$,skipped:W}}import{Buffer as a1}from"buffer";import{createHmac as s1,randomBytes as cW,timingSafeEqual as uW}from"crypto";import{config as G0}from"@stacksjs/config";import{db as o}from"@stacksjs/database";import{mail as r1,template as lW}from"@stacksjs/email";import{log as dW}from"@stacksjs/logging";function e1(){let $=G0.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 nW($){let W=cW(32).toString("hex"),Z=`${$}:${W}`,J=s1("sha256",e1()).update(Z).digest("hex");return{token:W,hash:J}}function iW($,W,Z){let J=`${$}:${W}`,Q=s1("sha256",e1()).update(J).digest("hex"),G=a1.from(Q),X=a1.from(Z);if(G.length!==X.length)return!1;return uW(G,X)}function tW(){let W=(G0.auth??{}).emailVerification;if(W!=null&&typeof W==="object"){let Z=W;if(typeof Z.expire==="number")return Z.expire}return 60}function oW($,W){let Z=G0.app.url?`https://${G0.app.url}`:`http://localhost:${process.env.PORT||"3000"}`,Q=(G0.auth.emailVerification?.url??"/verify-email/{id}/{token}").replace("{id}",String($)).replace("{token}",W);return/^https?:\/\//.test(Q)?Q:`${Z}${Q.startsWith("/")?"":"/"}${Q}`}function $$($){return $.email_verified_at!=null}async function W$($){let{token:W,hash:Z}=nW($.id),J=tW(),Q=new Date(Date.now()+J*60*1000);await o.deleteFrom("email_verifications").where("user_id","=",$.id).execute(),await o.insertInto("email_verifications").values({user_id:$.id,token:Z,expires_at:Q.toISOString()}).executeTakeFirst();let G=oW($.id,W),X=G0.app.name||"Stacks";try{let{html:K,text:Y}=await lW("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 r1.send({to:$.email,subject:`Verify Your ${X} Email Address`,text:Y,html:K})}catch(K){let Y=K instanceof Error?K.message:String(K);dW.warn(`[email] Email verification template failed, using plain text fallback: ${Y}`),await r1.send({to:$.email,subject:`Verify Your ${X} Email Address`,text:`Please verify your email address by visiting: ${G}
164
164
 
165
- This link expires in ${J} minutes.`})}}async function oW($,W){let Z=await o.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 o.deleteFrom("email_verifications").where("user_id","=",$).execute(),{success:!1,message:"Verification link has expired. Please request a new one."};if(!nW($,W,Z.token))return{success:!1,message:"Invalid verification link."};return await o.updateTable("users").set({email_verified_at:new Date().toISOString()}).where("id","=",$).executeTakeFirst(),await o.deleteFrom("email_verifications").where("user_id","=",$).execute(),{success:!0,message:"Email verified successfully."}}async function aW($){if(e1($))return{success:!1,message:"Email is already verified."};let W=await o.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 $$($),{success:!0,message:"Verification email sent."}}var M3={isVerified:e1,send:$$,verify:oW,resend:aW};import{generateTOTP as W4,verifyTOTP as Z4,generateTOTPSecret as J4,totpKeyUri as Q4}from"@stacksjs/ts-auth";import{randomBytes as rW}from"crypto";import{db as y}from"@stacksjs/database";var sW=300;async function W$($){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 eW($){return Boolean($.two_factor_enabled)}function $Z($,W){let Z=g0(),J=B1($,W,Z);return{secret:Z,uri:J}}var WZ=600;async function ZZ($,W,Z=WZ){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 JZ($){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 QZ($,W,Z){if(!await h0(Z,W))return!1;return await y.updateTable("users").set({two_factor_secret:W,two_factor_enabled:!0}).where("id","=",$).executeTakeFirst(),!0}async function GZ($){await y.updateTable("users").set({two_factor_secret:null,two_factor_enabled:!1}).where("id","=",$).executeTakeFirst()}async function XZ($,W){let{secret:Z,enabled:J}=await W$($);if(!J||!Z)return!1;return h0(W,Z)}async function YZ($,W=sW){let Z=rW(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 KZ($){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 v3={isEnabled:eW,getState:W$,generateSetup:$Z,stashPendingSecret:ZZ,consumePendingSecret:JZ,enable:QZ,disable:GZ,verifyLoginCode:XZ,createChallenge:YZ,consumeChallenge:KZ};import{config as l}from"@stacksjs/config";import{db as y0}from"@stacksjs/database";var s0="active_team";function Z$($){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 J$($){let W=$.cookies?.get(s0);if(!W)return null;let Z=Number(W);return Number.isFinite(Z)&&Z>0?Z:null}function N3($,W={}){let Z=Math.max(0,Math.floor(W.maxAgeSeconds??31536000)),J=[`${s0}=${encodeURIComponent(String($))}`,"Path=/","HttpOnly","SameSite=Lax",`Max-Age=${Z}`];if(W.secure)J.push("Secure");return J.join("; ")}function y3(){return`${s0}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`}async function LZ($){let W=await VZ($);if(!W?.id)return null;let Z=await y0.selectFrom("team_members").where("user_id","=",W.id).where("status","=","active").select(["team_id","role"]).execute();if(Z.length===0)return null;let J=Z$({memberships:Z.map((Q)=>({team_id:Number(Q.team_id),role:String(Q.role)})),activeTeamId:J$($)});return J.teamId!=null?{teamId:J.teamId,role:J.role??String(Z[0].role)}:null}async function T3($,W={}){let Z={user:null,teamId:null,role:null,teams:[],activeTeamId:null},J=l.auth?.default||"api",G=(l.auth?.guards?.[J]?.driver||"token")==="session"?await Q$($):await G$($);if(!G?.id)return Z;let X=await y0.selectFrom("team_members").where("user_id","=",G.id).where("status","=","active").select(["team_id","role"]).execute(),K=W.allowAnyTeam?!!W.allowAnyTeam(G):!1,Y=J$($),L=Z$({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 y0.selectFrom("teams").select(["id","name"]).execute();else{let F=[...O.keys()];_=F.length?await y0.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,H)=>F.name.localeCompare(H.name));return{user:G,teamId:L.teamId,role:L.role,teams:E,activeTeamId:Y}}async function f3($){let W=await LZ($);return W?W.teamId:null}async function VZ($){let W=l.auth?.default||"api",Q=((l.auth?.guards?.[W]||{driver:"token"}).driver||"token")==="session"?await Q$($):await G$($);return Q?.id?Q:void 0}async function Q$($){let W=$.cookies?.get("session_id");if(!W)return;return c0(W)}async function G$($){let W=l.auth?.defaultTokenName||"auth-token",Z=(typeof $.bearerToken==="function"?$.bearerToken():void 0)??$.cookies?.get(W);if(!Z)return;return R.getUserFromToken(Z)}export{hW as withRbac,Y3 as withAuthorization,XZ as verifyTwoFactorLoginCode,h0 as verifyTwoFactorCode,Z4 as verifyTOTP,G2 as verifyRegistrationResponse,oW as verifyEmail,X2 as verifyAuthenticationResponse,E$ as validateRefreshToken,vW as userCannot,SW as userCanAny,EW as userCanAll,PW as userCan,rZ as updatePasskeyCounter,Q4 as totpKeyUri,M$ as tokens,q$ as tokenCant,P$ as tokenCanAny,C$ as tokenCanAll,X1 as tokenCan,v$ as tokenAbilities,U$ as token,k1 as syncRoles,gW as syncRolePermissions,u1 as syncPermissions,eZ as storeWebAuthnChallenge,ZZ as stashPendingTwoFactorSecret,Y2 as startRegistration,K2 as startAuthentication,RW as setRbacStore,sZ as setCurrentRegistrationOptions,c0 as sessionUser,KW as sessionRefresh,XW as sessionLogout,GW as sessionLogin,m0 as sessionDestroyAll,YW as sessionCheck,$$ as sendVerificationEmail,Z$ as selectActiveTeam,pW as seedDefaultRoles,y$ as revokeTokenById,N$ as revokeToken,x$ as revokeRefreshToken,kW as revokePermissionFromRole,c1 as revokePermission,T$ as revokeOtherTokens,g$ as revokeClient,j0 as revokeAllTokens,Y1 as revokeAllRefreshTokens,IW as revokeAllPermissions,T3 as resolveTeamContext,VZ as resolveAuthenticatedUser,f3 as resolveAuthenticatedTeamId,LZ as resolveAuthenticatedMembership,aW as resendVerificationEmail,b1 as removeRole,wW as removeAllRoles,CW as registerGates,c2 as register,S$ as refreshToken,s2 as refresh,O0 as policy,F2 as platformAuthenticatorIsAvailable,y2 as passwordResets,f as parseScopes,x1 as none,o2 as name,r2 as logout,eW as isTwoFactorEnabled,d as isIssuedBeforePasswordChange,e1 as isEmailVerified,a2 as isAuthenticated,X3 as inspectUser,W0 as inspect,J3 as initializeAuthorization,i2 as id,g1 as hasRole,N1 as hasPolicy,l1 as hasPermission,h1 as hasAnyRole,d1 as hasAnyPermission,p1 as hasAllRoles,n1 as hasAllPermissions,A1 as has,bW as givePermissionToRole,m1 as givePermission,Q0 as getUserRoles,z0 as getUserPermissions,aZ as getUserPasskeys,e$ as getUserPasskey,W$ as getTwoFactorState,i1 as getRolePermissions,R1 as getPolicyFor,s as getPasswordChangedAt,n2 as getCurrentUser,NW as getAllRoles,fW as getAllPermissions,J$ as getActiveTeamPreference,B1 as generateTwoFactorUri,kZ as generateTwoFactorToken,$Z as generateTwoFactorSetup,g0 as generateTwoFactorSecret,J4 as generateTOTPSecret,W4 as generateTOTP,J2 as generateRegistrationOptions,Q2 as generateAuthenticationOptions,N0 as flushRbacCache,T1 as flush,G1 as findToken,b as findRole,u as findPermission,b$ as findClient,QZ as enableTwoFactor,t2 as email,qW as discoverPolicies,GZ as disableTwoFactor,E1 as denies,AW as deleteRole,w$ as deleteRevokedTokens,A$ as deleteRevokedRefreshTokens,TW as deletePermission,f$ as deleteExpiredTokens,R$ as deleteExpiredRefreshTokens,C1 as define,n as currentAccessToken,YZ as createTwoFactorChallenge,f0 as createToken,r0 as createRole,lZ as createPersonalAccessClient,yW as createPermission,k$ as createClient,OW as createBqbRbacStore,$2 as consumeWebAuthnChallenge,KZ as consumeTwoFactorChallenge,JZ as consumePendingTwoFactorSecret,I$ as clients,y3 as clearActiveTeamCookie,HW as check,S0 as cannot,v0 as can,N3 as buildActiveTeamCookie,V2 as browserSupportsWebAuthnAutofill,L2 as browserSupportsWebAuthn,P1 as before,xW as authorizeUser,x0 as authorize,F0 as authUser,iZ as authMiddlewareHandler,s$ as authMiddleware,I1 as assignRole,_0 as any,S1 as allows,E0 as all,v1 as after,y1 as abilities,v3 as TwoFactor,U2 as SessionAuth,L3 as Rbac,k as RateLimiter,f1 as Gate,M3 as EmailVerification,t1 as DEFAULT_ROLE_PACKS,UW as BasePolicy,q as AuthorizationResponse,B0 as AuthorizationException,R as Auth,s0 as ACTIVE_TEAM_COOKIE};
165
+ This link expires in ${J} minutes.`})}}async function aW($,W){let Z=await o.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 o.deleteFrom("email_verifications").where("user_id","=",$).execute(),{success:!1,message:"Verification link has expired. Please request a new one."};if(!iW($,W,Z.token))return{success:!1,message:"Invalid verification link."};return await o.updateTable("users").set({email_verified_at:new Date().toISOString()}).where("id","=",$).executeTakeFirst(),await o.deleteFrom("email_verifications").where("user_id","=",$).execute(),{success:!0,message:"Email verified successfully."}}async function rW($){if($$($))return{success:!1,message:"Email is already verified."};let W=await o.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 W$($),{success:!0,message:"Verification email sent."}}var q3={isVerified:$$,send:W$,verify:aW,resend:rW};import{generateTOTP as J4,verifyTOTP as Q4,generateTOTPSecret as G4,totpKeyUri as X4}from"@stacksjs/ts-auth";import{randomBytes as sW}from"crypto";import{db as y}from"@stacksjs/database";var eW=300;async function Z$($){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 $Z($){return Boolean($.two_factor_enabled)}function WZ($,W){let Z=g0(),J=B1($,W,Z);return{secret:Z,uri:J}}var ZZ=600;async function JZ($,W,Z=ZZ){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 QZ($){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 GZ($,W,Z){if(!await h0(Z,W))return!1;return await y.updateTable("users").set({two_factor_secret:W,two_factor_enabled:!0}).where("id","=",$).executeTakeFirst(),!0}async function XZ($){await y.updateTable("users").set({two_factor_secret:null,two_factor_enabled:!1}).where("id","=",$).executeTakeFirst()}async function YZ($,W){let{secret:Z,enabled:J}=await Z$($);if(!J||!Z)return!1;return h0(W,Z)}async function KZ($,W=eW){let Z=sW(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 LZ($){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 E3={isEnabled:$Z,getState:Z$,generateSetup:WZ,stashPendingSecret:JZ,consumePendingSecret:QZ,enable:GZ,disable:XZ,verifyLoginCode:YZ,createChallenge:KZ,consumeChallenge:LZ};import{config as l}from"@stacksjs/config";import{db as y0}from"@stacksjs/database";var s0="active_team";function J$($){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 Q$($){let W=$.cookies?.get(s0);if(!W)return null;let Z=Number(W);return Number.isFinite(Z)&&Z>0?Z:null}function T3($,W={}){let Z=Math.max(0,Math.floor(W.maxAgeSeconds??31536000)),J=[`${s0}=${encodeURIComponent(String($))}`,"Path=/","HttpOnly","SameSite=Lax",`Max-Age=${Z}`];if(W.secure)J.push("Secure");return J.join("; ")}function w3(){return`${s0}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`}async function VZ($){let W=await FZ($);if(!W?.id)return null;let Z=await y0.selectFrom("team_members").where("user_id","=",W.id).where("status","=","active").select(["team_id","role"]).execute();if(Z.length===0)return null;let J=J$({memberships:Z.map((Q)=>({team_id:Number(Q.team_id),role:String(Q.role)})),activeTeamId:Q$($)});return J.teamId!=null?{teamId:J.teamId,role:J.role??String(Z[0].role)}:null}async function f3($,W={}){let Z={user:null,teamId:null,role:null,teams:[],activeTeamId:null},J=l.auth?.default||"api",G=(l.auth?.guards?.[J]?.driver||"token")==="session"?await G$($):await X$($);if(!G?.id)return Z;let X=await y0.selectFrom("team_members").where("user_id","=",G.id).where("status","=","active").select(["team_id","role"]).execute(),K=W.allowAnyTeam?!!W.allowAnyTeam(G):!1,Y=Q$($),L=J$({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 y0.selectFrom("teams").select(["id","name"]).execute();else{let F=[...O.keys()];_=F.length?await y0.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,H)=>F.name.localeCompare(H.name));return{user:G,teamId:L.teamId,role:L.role,teams:E,activeTeamId:Y}}async function I3($){let W=await VZ($);return W?W.teamId:null}async function FZ($){let W=l.auth?.default||"api",Q=((l.auth?.guards?.[W]||{driver:"token"}).driver||"token")==="session"?await G$($):await X$($);return Q?.id?Q:void 0}async function G$($){let W=$.cookies?.get("session_id");if(!W)return;return c0(W)}async function X$($){let W=l.auth?.defaultTokenName||"auth-token",Z=(typeof $.bearerToken==="function"?$.bearerToken():void 0)??$.cookies?.get(W);if(!Z)return;return R.getUserFromToken(Z)}export{pW as withRbac,L3 as withAuthorization,YZ as verifyTwoFactorLoginCode,h0 as verifyTwoFactorCode,Q4 as verifyTOTP,Y2 as verifyRegistrationResponse,aW as verifyEmail,K2 as verifyAuthenticationResponse,x$ as validateRefreshToken,SW as userCannot,EW as userCanAny,xW as userCanAll,vW as userCan,eZ as updatePasskeyCounter,X4 as totpKeyUri,U$ as tokens,C$ as tokenCant,v$ as tokenCanAny,P$ as tokenCanAll,X1 as tokenCan,S$ as tokenAbilities,q$ as token,g1 as syncRoles,hW as syncRolePermissions,l1 as syncPermissions,W2 as storeWebAuthnChallenge,JZ as stashPendingTwoFactorSecret,L2 as startRegistration,V2 as startAuthentication,AW as setRbacStore,$2 as setCurrentRegistrationOptions,c0 as sessionUser,LW as sessionRefresh,YW as sessionLogout,XW as sessionLogin,m0 as sessionDestroyAll,KW as sessionCheck,W$ as sendVerificationEmail,J$ as selectActiveTeam,mW as seedDefaultRoles,T$ as revokeTokenById,y$ as revokeToken,R$ as revokeRefreshToken,gW as revokePermissionFromRole,u1 as revokePermission,w$ as revokeOtherTokens,h$ as revokeClient,j0 as revokeAllTokens,Y1 as revokeAllRefreshTokens,bW as revokeAllPermissions,f3 as resolveTeamContext,FZ as resolveAuthenticatedUser,I3 as resolveAuthenticatedTeamId,VZ as resolveAuthenticatedMembership,rW as resendVerificationEmail,k1 as removeRole,IW as removeAllRoles,PW as registerGates,l2 as register,E$ as refreshToken,$3 as refresh,O0 as policy,O2 as platformAuthenticatorIsAvailable,w2 as passwordResets,w as parseScopes,R1 as none,r2 as name,e2 as logout,$Z as isTwoFactorEnabled,d as isIssuedBeforePasswordChange,$$ as isEmailVerified,s2 as isAuthenticated,K3 as inspectUser,W0 as inspect,G3 as initializeAuthorization,o2 as id,h1 as hasRole,y1 as hasPolicy,d1 as hasPermission,p1 as hasAnyRole,n1 as hasAnyPermission,m1 as hasAllRoles,i1 as hasAllPermissions,N1 as has,kW as givePermissionToRole,c1 as givePermission,Q0 as getUserRoles,z0 as getUserPermissions,sZ as getUserPasskeys,$W as getUserPasskey,Z$ as getTwoFactorState,t1 as getRolePermissions,A1 as getPolicyFor,s as getPasswordChangedAt,t2 as getCurrentUser,yW as getAllRoles,fW as getAllPermissions,Q$ as getActiveTeamPreference,B1 as generateTwoFactorUri,gZ as generateTwoFactorToken,WZ as generateTwoFactorSetup,g0 as generateTwoFactorSecret,G4 as generateTOTPSecret,J4 as generateTOTP,G2 as generateRegistrationOptions,X2 as generateAuthenticationOptions,N0 as flushRbacCache,w1 as flush,G1 as findToken,b as findRole,u as findPermission,k$ as findClient,GZ as enableTwoFactor,a2 as email,CW as discoverPolicies,XZ as disableTwoFactor,x1 as denies,NW as deleteRole,I$ as deleteRevokedTokens,N$ as deleteRevokedRefreshTokens,wW as deletePermission,f$ as deleteExpiredTokens,A$ as deleteExpiredRefreshTokens,P1 as define,n as currentAccessToken,KZ as createTwoFactorChallenge,w0 as createToken,r0 as createRole,dZ as createPersonalAccessClient,TW as createPermission,g$ as createClient,_W as createBqbRbacStore,Z2 as consumeWebAuthnChallenge,LZ as consumeTwoFactorChallenge,QZ as consumePendingTwoFactorSecret,b$ as clients,w3 as clearActiveTeamCookie,MW as check,S0 as cannot,v0 as can,T3 as buildActiveTeamCookie,B2 as browserSupportsWebAuthnAutofill,F2 as browserSupportsWebAuthn,v1 as before,RW as authorizeUser,x0 as authorize,F0 as authUser,tZ as authMiddlewareHandler,e$ as authMiddleware,b1 as assignRole,_0 as any,E1 as allows,E0 as all,S1 as after,T1 as abilities,E3 as TwoFactor,C2 as SessionAuth,F3 as Rbac,k as RateLimiter,f1 as Gate,q3 as EmailVerification,o1 as DEFAULT_ROLE_PACKS,qW as BasePolicy,q as AuthorizationResponse,B0 as AuthorizationException,R as Auth,s0 as ACTIVE_TEAM_COOKIE};
package/dist/passkey.d.ts CHANGED
@@ -39,7 +39,7 @@ export declare function setCurrentRegistrationOptions(user: UserModel, verified:
39
39
  * at the DB layer; this delete makes the upsert safe even on installs
40
40
  * that ran an earlier auth:setup before the unique index existed.
41
41
  */
42
- export declare function storeWebAuthnChallenge(userId: number, challenge: string, purpose: WebAuthnChallengePurpose, ttlSeconds?: number): Promise<void>;
42
+ export declare function storeWebAuthnChallenge(userId: number, challenge: string | Uint8Array, purpose: WebAuthnChallengePurpose, ttlSeconds?: number): Promise<void>;
43
43
  /**
44
44
  * Read + delete (single-use) a WebAuthn challenge for the given user
45
45
  * + purpose. Returns `null` when no outstanding challenge exists or
@@ -50,7 +50,7 @@ export declare function storeWebAuthnChallenge(userId: number, challenge: string
50
50
  * the TTL window. Callers MUST treat a `null` return as a verification
51
51
  * failure.
52
52
  */
53
- export declare function consumeWebAuthnChallenge(userId: number, purpose: WebAuthnChallengePurpose): Promise<string | null>;
53
+ export declare function consumeWebAuthnChallenge(userId: number, purpose: WebAuthnChallengePurpose): Promise<Uint8Array | null>;
54
54
  export declare interface PasskeyAttribute {
55
55
  id: string
56
56
  cred_public_key: string
@@ -65,7 +65,7 @@ export declare interface PasskeyAttribute {
65
65
  created_at?: Date
66
66
  last_used_at: string
67
67
  }
68
- declare type UserModel = InstanceType<typeof User>;
68
+ declare type UserModel = NonNullable<Awaited<ReturnType<typeof User.find>>>;
69
69
  declare type PasskeyInsertable = Insertable<PasskeyAttribute>;
70
70
  // =============================================================================
71
71
  // WebAuthn challenge persistence (stacksjs/stacks#1866)
@@ -15,20 +15,9 @@ export declare function seedDefaultRoles(): Promise<SeedDefaultRolesResult>;
15
15
  * predicates (`isAdmin`, `isDev`, `isClient`) check these names verbatim,
16
16
  * so renaming them in a project effectively unbinds the composable from
17
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.', }, ]`
18
19
  */
19
- export declare const DEFAULT_ROLE_PACKS: readonly [{
20
- name: 'admin';
21
- guard_name: 'web';
22
- description: 'Full access. Sees every dashboard surface, every model, every infra control.'
23
- }, {
24
- name: 'dev';
25
- guard_name: 'web';
26
- description: 'Developer / infra. Sees dev-mode surfaces (CI, query inspector, runner alerts) but not billing/admin-only management.'
27
- }, {
28
- name: 'client';
29
- guard_name: 'web';
30
- description: 'End user / client. Sees content, orders, profile, billing — no dev tools, no infra surfaces.'
31
- }];
20
+ export declare const DEFAULT_ROLE_PACKS: Array<{ name: string, guard_name: string, description: string }>;
32
21
  export declare interface SeedDefaultRolesResult {
33
22
  created: RoleRecord[]
34
23
  skipped: Array<{ name: string, guard_name: string, reason: 'already_exists' }>
package/dist/rbac.d.ts CHANGED
@@ -133,7 +133,36 @@ export declare function withRbac<T extends UserModel | { id: number }>(user: T):
133
133
  */
134
134
  export declare const Rbac: {
135
135
  setStore: unknown;
136
- flushCache: unknown
136
+ flushCache: unknown;
137
+ createRole: typeof createRole;
138
+ findRole: typeof findRole;
139
+ deleteRole: typeof deleteRole;
140
+ getAllRoles: typeof getAllRoles;
141
+ createPermission: typeof createPermission;
142
+ findPermission: typeof findPermission;
143
+ deletePermission: typeof deletePermission;
144
+ getAllPermissions: typeof getAllPermissions;
145
+ getUserRoles: typeof getUserRoles;
146
+ assignRole: typeof assignRole;
147
+ removeRole: typeof removeRole;
148
+ removeAllRoles: typeof removeAllRoles;
149
+ syncRoles: typeof syncRoles;
150
+ hasRole: typeof hasRole;
151
+ hasAnyRole: typeof hasAnyRole;
152
+ hasAllRoles: typeof hasAllRoles;
153
+ getUserPermissions: typeof getUserPermissions;
154
+ givePermission: typeof givePermission;
155
+ revokePermission: typeof revokePermission;
156
+ revokeAllPermissions: typeof revokeAllPermissions;
157
+ syncPermissions: typeof syncPermissions;
158
+ hasPermission: typeof hasPermission;
159
+ hasAnyPermission: typeof hasAnyPermission;
160
+ hasAllPermissions: typeof hasAllPermissions;
161
+ getRolePermissions: typeof getRolePermissions;
162
+ givePermissionToRole: typeof givePermissionToRole;
163
+ revokePermissionFromRole: typeof revokePermissionFromRole;
164
+ syncRolePermissions: typeof syncRolePermissions;
165
+ withRbac: typeof withRbac
137
166
  };
138
167
  export declare interface RoleRecord {
139
168
  id: number
@@ -46,4 +46,4 @@ export declare const SessionAuth: {
46
46
  check: unknown;
47
47
  refresh: unknown
48
48
  };
49
- declare type UserModel = InstanceType<typeof User>;
49
+ declare type UserModel = NonNullable<Awaited<ReturnType<typeof User.find>>>;
package/dist/tokens.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { AccessToken, CreateClientOptions, CreateClientResult, OAuthClient, PersonalAccessTokenResult, RefreshTokenResult, TokenScopes } from '@stacksjs/types';
1
+ import type { AccessToken, CreateClientOptions, CreateClientResult, DatabaseDriver, OAuthClient, PersonalAccessTokenResult, RefreshTokenResult, TokenScopes } from '@stacksjs/types';
2
2
  /**
3
3
  * Read `users.password_changed_at` for a user.
4
4
  *
@@ -270,6 +270,8 @@ export declare function revokeClient(clientId: number): Promise<void>;
270
270
  // HELPER FUNCTIONS
271
271
  // ============================================================================
272
272
  export declare function parseScopes(scopes: string | string[] | null | undefined): TokenScopes;
273
+ /** Current database driver */
274
+ declare const dbDriver: DatabaseDriver;
273
275
  /** Cross-database SQL helpers */
274
276
  declare const sql: unknown;
275
277
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/auth",
3
3
  "type": "module",
4
- "version": "0.70.72",
4
+ "version": "0.70.74",
5
5
  "description": "A more simplistic way to authenticate.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -53,8 +53,8 @@
53
53
  "@stacksjs/ts-auth": "^0.4.3"
54
54
  },
55
55
  "devDependencies": {
56
- "better-dx": "^0.2.12",
57
- "@stacksjs/error-handling": "0.70.72",
58
- "@stacksjs/router": "0.70.72"
56
+ "better-dx": "^0.2.16",
57
+ "@stacksjs/error-handling": "0.70.74",
58
+ "@stacksjs/router": "0.70.74"
59
59
  }
60
60
  }