@stacksjs/auth 0.72.102 → 0.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/gate.d.ts +24 -7
- package/dist/magic-link.js +1 -1
- package/dist/request-token.d.ts +14 -1
- package/dist/request-token.js +1 -1
- package/dist/tokens.d.ts +0 -15
- package/dist/tokens.js +11 -11
- package/package.json +3 -3
package/dist/gate.d.ts
CHANGED
|
@@ -43,7 +43,7 @@ export declare function defineGates<const T extends GatesDefinition>(definition:
|
|
|
43
43
|
* define('edit-settings', (user) => user?.isAdmin)
|
|
44
44
|
* define('update-post', (user, post) => user?.id === post.userId)
|
|
45
45
|
*/
|
|
46
|
-
export declare function define<T = any>(ability:
|
|
46
|
+
export declare function define<T = any>(ability: Ability, callback: GateCallback<T>): void;
|
|
47
47
|
/**
|
|
48
48
|
* Register a policy for a model
|
|
49
49
|
*
|
|
@@ -73,28 +73,34 @@ export declare function after(callback: GateAfterCallback): void;
|
|
|
73
73
|
* if (await allows('edit-settings', user)) { ... }
|
|
74
74
|
* if (await allows('update', user, post)) { ... }
|
|
75
75
|
*/
|
|
76
|
-
export declare function allows(ability:
|
|
76
|
+
export declare function allows<const A extends Ability>(ability: A, user: UserModel | null, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
77
77
|
/**
|
|
78
78
|
* Check if the user is denied from performing an ability
|
|
79
79
|
*
|
|
80
80
|
* @example
|
|
81
81
|
* if (await denies('delete', user, post)) { ... }
|
|
82
82
|
*/
|
|
83
|
-
export declare function denies(ability:
|
|
83
|
+
export declare function denies<const A extends Ability>(ability: A, user: UserModel | null, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
84
84
|
/**
|
|
85
85
|
* Check if the user can perform an ability (alias for allows)
|
|
86
86
|
*/
|
|
87
|
-
export declare function can(ability:
|
|
87
|
+
export declare function can<const A extends Ability>(ability: A, user: UserModel | null, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
88
88
|
/**
|
|
89
89
|
* Check if the user cannot perform an ability (alias for denies)
|
|
90
90
|
*/
|
|
91
|
-
export declare function cannot(ability:
|
|
91
|
+
export declare function cannot<const A extends Ability>(ability: A, user: UserModel | null, ...args: AbilityArgs<A>): Promise<boolean>;
|
|
92
92
|
/**
|
|
93
93
|
* Check if the user can perform any of the given abilities
|
|
94
94
|
*
|
|
95
95
|
* @example
|
|
96
96
|
* if (await any(['update', 'delete'], user, post)) { ... }
|
|
97
97
|
*/
|
|
98
|
+
/*
|
|
99
|
+
* `any` / `all` / `none` keep `any[]`: one argument list is checked against
|
|
100
|
+
* SEVERAL abilities, which may each declare different parameters, so there is
|
|
101
|
+
* no single tuple that is correct for the call. The single-ability functions
|
|
102
|
+
* above are where the declaration can be enforced.
|
|
103
|
+
*/
|
|
98
104
|
export declare function any(abilities: readonly Ability[], user: UserModel | null, ...args: any[]): Promise<boolean>;
|
|
99
105
|
/**
|
|
100
106
|
* Check if the user can perform all of the given abilities
|
|
@@ -113,11 +119,11 @@ export declare function none(abilities: readonly Ability[], user: UserModel | nu
|
|
|
113
119
|
* @example
|
|
114
120
|
* await authorize('update', user, post) // Throws if not allowed
|
|
115
121
|
*/
|
|
116
|
-
export declare function authorize(ability:
|
|
122
|
+
export declare function authorize<const A extends Ability>(ability: A, user: UserModel | null, ...args: AbilityArgs<A>): Promise<AuthorizationResponse>;
|
|
117
123
|
/**
|
|
118
124
|
* Get detailed inspection result for an ability check
|
|
119
125
|
*/
|
|
120
|
-
export declare function inspect(ability:
|
|
126
|
+
export declare function inspect<const A extends Ability>(ability: A, user: UserModel | null, ...args: AbilityArgs<A>): Promise<AuthorizationResponse>;
|
|
121
127
|
/**
|
|
122
128
|
* Get a policy instance for a model
|
|
123
129
|
*/
|
|
@@ -246,6 +252,17 @@ export type PolicyAbility = 'viewAny' | 'view' | 'create' | 'update' | 'delete'
|
|
|
246
252
|
*/
|
|
247
253
|
// eslint-disable-next-line ts/ban-types -- `string & {}` keeps literal completions alive
|
|
248
254
|
export type Ability = GateName | PolicyAbility | (string & {});
|
|
255
|
+
/**
|
|
256
|
+
* The arguments an ability takes after the user.
|
|
257
|
+
*
|
|
258
|
+
* A gate the application declares contributes its own parameter list, so
|
|
259
|
+
* `Gate.allows('update-post', user, post)` is checked against how the gate was
|
|
260
|
+
* written. Anything else - a policy ability, or a name computed at runtime -
|
|
261
|
+
* keeps `any[]`, which is what it was for every ability before.
|
|
262
|
+
*/
|
|
263
|
+
export type AbilityArgs<A extends Ability> = A extends keyof AppGates
|
|
264
|
+
? (AppGates[A] extends readonly unknown[] ? AppGates[A] : any[])
|
|
265
|
+
: any[];
|
|
249
266
|
/** A policy class name, as narrow as the application has made it. */
|
|
250
267
|
export type PolicyName = keyof PolicyClasses extends never ? string : keyof PolicyClasses & string;
|
|
251
268
|
/** A model name a policy may be registered for. */
|
package/dist/magic-link.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{createHash,randomBytes}from"node:crypto";import process from"node:process";import{config}from"@stacksjs/config";import{db,sqlDateTime}from"@stacksjs/database";import{mail,
|
|
1
|
+
import{createHash,randomBytes}from"node:crypto";import process from"node:process";import{config}from"@stacksjs/config";import{db,sqlDateTime}from"@stacksjs/database";import{mail,templateByName}from"@stacksjs/email";import{log}from"@stacksjs/logging";import{RateLimiter}from"./rate-limiter";function hashToken(raw){return createHash("sha256").update(raw).digest("hex")}function expireMinutes(){return config.auth.magicLink?.expire??15}function safeRedirect(candidate){const fallback=config.auth.magicLink?.redirectDefault??"/";if(!candidate)return fallback;if(!candidate.startsWith("/")||candidate.startsWith("//"))return fallback;return candidate}async function linkBase(siteId){if(siteId){const primary=await db.selectFrom("site_domains").where("site_id","=",siteId).where("is_primary","=",!0).where("verified_at","is not",null).select(["domain"]).executeTakeFirst();if(primary?.domain)return`https://${primary.domain}`;const site=await db.selectFrom("sites").where("id","=",siteId).select(["subdomain"]).executeTakeFirst(),base=config.sites?.baseDomain;if(site?.subdomain&&base)return`https://${site.subdomain}.${base}`}return config.app.url?`https://${config.app.url}`:`http://localhost:${process.env.PORT||"3000"}`}export async function sendMagicLink(email,options={}){const normalized=email.trim().toLowerCase();if(await RateLimiter.isRateLimited(normalized))return;await RateLimiter.recordFailedAttempt(normalized);let user=await db.selectFrom("users").where("email","=",normalized).select(["id","email"]).executeTakeFirst();if(!user&&options.createUser){await db.insertInto("users").values({email:normalized,name:normalized.split("@")[0],password:null,created_at:sqlDateTime(new Date),updated_at:sqlDateTime(new Date)}).execute();user=await db.selectFrom("users").where("email","=",normalized).select(["id","email"]).executeTakeFirst()}if(!user)return;const raw=randomBytes(32).toString("base64url"),ttl=options.ttlMinutes??expireMinutes(),redirectTo=safeRedirect(options.redirectTo);await db.deleteFrom("magic_link_tokens").where("email","=",normalized).where("consumed_at","is",null).execute();await db.insertInto("magic_link_tokens").values({email:normalized,user_id:user.id,token:hashToken(raw),expires_at:sqlDateTime(new Date(Date.now()+ttl*60000)),redirect_to:redirectTo,site_id:options.siteId??null,created_at:sqlDateTime(new Date),updated_at:sqlDateTime(new Date)}).execute();const base=await linkBase(options.siteId),filled=(config.auth.magicLink?.url??"/auth/magic/{token}").replace("{token}",raw),linkUrl=/^https?:\/\//.test(filled)?filled:`${base}${filled.startsWith("/")?"":"/"}${filled}`,appName=config.app.name||"Stacks";let html,text;try{const rendered=await templateByName("magic-link",{subject:`Sign in to ${appName}`,variables:{linkUrl,expireMinutes:ttl}});if(rendered.html||rendered.text){html=rendered.html;text=rendered.text}}catch{}try{await mail.sendOrFail({to:normalized,subject:`Sign in to ${appName}`,...html?{html}:{},text:text||`Sign in to ${appName}: ${linkUrl}
|
|
2
2
|
|
|
3
3
|
This link works once and expires in ${ttl} minutes. If you didn't request it, ignore this email.`})}catch(error){log.error(`Magic-link email to ${normalized} failed: ${error.message}`)}}export async function consumeMagicLink(raw){if(!raw||raw.length>255)return{ok:!1,reason:"invalid"};const hashed=hashToken(raw),now=sqlDateTime(new Date),claim=await db.updateTable("magic_link_tokens").set({consumed_at:now,updated_at:now}).where("token","=",hashed).where("consumed_at","is",null).where("expires_at",">",now).execute(),updated=typeof claim==="number"?claim:Number(claim?.numUpdatedRows??claim?.changes??0),row=await db.selectFrom("magic_link_tokens").where("token","=",hashed).select(["user_id","email","expires_at","consumed_at","redirect_to"]).executeTakeFirst();if(!row)return{ok:!1,reason:"invalid"};if(updated<1){if(!row.consumed_at&&new Date(row.expires_at).getTime()<=Date.now())return{ok:!1,reason:"expired"};return{ok:!1,reason:"used"}}if(!row.user_id)return{ok:!1,reason:"no-user"};return{ok:!0,userId:Number(row.user_id),email:row.email,redirectTo:safeRedirect(row.redirect_to)}}export async function pruneMagicLinkTokens(olderThanDays=7){const cutoff=sqlDateTime(new Date(Date.now()-olderThanDays*86400000));await db.deleteFrom("magic_link_tokens").where("expires_at","<",cutoff).execute()}
|
package/dist/request-token.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare function requestToken(request: TokenBearingRequest | null | undefined): string | null;
|
|
1
2
|
/**
|
|
2
3
|
* The access token a request carries, from the Authorization header or the
|
|
3
4
|
* auth cookie.
|
|
@@ -13,4 +14,16 @@
|
|
|
13
14
|
*
|
|
14
15
|
* The header is checked first, so an API client behaves exactly as before.
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* What this needs off a request, which is deliberately little.
|
|
19
|
+
*
|
|
20
|
+
* Both members are optional because the function is handed two different
|
|
21
|
+
* shapes: a Stacks request, which answers `bearerToken()`, and a plain
|
|
22
|
+
* `Request`, which only has headers. Written out rather than left as `any` so
|
|
23
|
+
* the optional chaining below is checked against something - as `any` it was
|
|
24
|
+
* indistinguishable from probing for members that do not exist on either.
|
|
25
|
+
*/
|
|
26
|
+
export declare interface TokenBearingRequest {
|
|
27
|
+
bearerToken?: () => string | undefined | null
|
|
28
|
+
headers?: Headers
|
|
29
|
+
}
|
package/dist/request-token.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{authCookieToken}from"./cookie";export function requestToken(request){let token=request?.bearerToken?.();if(!token){const header=request?.headers?.get?.("authorization")||request?.headers?.get?.("Authorization");if(typeof header==="string"&&header.startsWith("Bearer "))token=header.substring(7)}if(!token&&request?.headers)token=authCookieToken(request);return token||null}
|
|
1
|
+
import{authCookieToken}from"./cookie";export function requestToken(request){let token=request?.bearerToken?.();if(!token){const header=request?.headers?.get?.("authorization")||request?.headers?.get?.("Authorization");if(typeof header==="string"&&header.startsWith("Bearer "))token=header.substring(7)}if(!token&&request?.headers)token=authCookieToken({headers:request.headers});return token||null}
|
package/dist/tokens.d.ts
CHANGED
|
@@ -1,19 +1,4 @@
|
|
|
1
1
|
import type { AccessToken, CreateClientOptions, CreateClientResult, DatabaseDriver, OAuthClient, PersonalAccessTokenResult, RefreshTokenResult, TokenScopes } from '@stacksjs/types';
|
|
2
|
-
/**
|
|
3
|
-
* Read `users.password_changed_at` for a user.
|
|
4
|
-
*
|
|
5
|
-
* Binds a token's validity to the account's credential state: a token
|
|
6
|
-
* issued before the user last changed their password is no longer
|
|
7
|
-
* trusted, regardless of its own `revoked`/`expires_at` flags. This is
|
|
8
|
-
* the durable, use-time backstop behind the post-reset revocation sweep
|
|
9
|
-
* (#1947) — even a freshly minted pair that the sweep never saw is
|
|
10
|
-
* rejected on first use.
|
|
11
|
-
*
|
|
12
|
-
* Returns `null` on ANY error (missing column / missing table) so a
|
|
13
|
-
* not-yet-migrated database degrades to legacy-allow rather than locking
|
|
14
|
-
* everyone out. Accepts an optional query runner so the refresh exchange
|
|
15
|
-
* can read the stamp inside its own transaction.
|
|
16
|
-
*/
|
|
17
2
|
export declare function getPasswordChangedAt(userId: unknown, q?: { unsafe: (sql: string, params?: any[]) => any }): Promise<Date | null>;
|
|
18
3
|
/**
|
|
19
4
|
* True when a credential issued at `createdAt` predates the user's last
|
package/dist/tokens.js
CHANGED
|
@@ -23,7 +23,7 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
|
|
|
23
23
|
VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?, ${appNow()}, ${appNow()})
|
|
24
24
|
`,[userId,client.id,hashedToken,name,JSON.stringify(scopes),sqlDateTime(expiresAt),agent,address]);const row=(await db.unsafe(`
|
|
25
25
|
SELECT * FROM oauth_access_tokens WHERE token = ${param(1)} LIMIT 1
|
|
26
|
-
`,[hashedToken]))[0];if(!row)throw new HttpError(500,"Failed to create access token - inserted row not found");const accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name,scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:new Date(row.updated_at)};let refreshTokenPlain;if(withRefreshToken){refreshTokenPlain=generateSecureToken(40);const hashedRefreshToken=hashToken(refreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await db.unsafe(`
|
|
26
|
+
`,[hashedToken]))[0];if(!row)throw new HttpError(500,"Failed to create access token - inserted row not found");const accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name,scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:row.updated_at?new Date(row.updated_at):new Date(row.created_at)};let refreshTokenPlain;if(withRefreshToken){refreshTokenPlain=generateSecureToken(40);const hashedRefreshToken=hashToken(refreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await db.unsafe(`
|
|
27
27
|
INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
|
|
28
28
|
VALUES ($1, $2, false, $3, ${appNow()})
|
|
29
29
|
`,[accessToken.id,hashedRefreshToken,sqlDateTime(refreshExpiresAt)]);else await db.unsafe(`
|
|
@@ -53,7 +53,7 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
|
|
|
53
53
|
VALUES (?, ?, ?, ?, ?, 0, ?, ${appNow()}, ${appNow()})
|
|
54
54
|
`,[refreshRow.user_id,refreshRow.oauth_client_id,hashedToken,refreshRow.name,refreshRow.scopes,sqlDateTime(expiresAt)]);const row=(await trx.unsafe(`
|
|
55
55
|
SELECT * FROM oauth_access_tokens WHERE token = ${param(1)} LIMIT 1
|
|
56
|
-
`,[hashedToken]))[0],accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name,scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:new Date(row.updated_at)};if(isIssuedBeforePasswordChange(row.created_at,await getPasswordChangedAt(refreshRow.user_id,trx)))throw new HttpError(401,"Invalid or expired refresh token");const newRefreshTokenPlain=generateSecureToken(40),newHashedRefreshToken=hashToken(newRefreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await trx.unsafe(`
|
|
56
|
+
`,[hashedToken]))[0];if(!row)throw new HttpError(500,"Failed to read back the access token that was just created.");const accessToken={id:row.id,userId:row.user_id,clientId:row.oauth_client_id,name:row.name||"access-token",scopes:parseScopes(row.scopes),revoked:!1,expiresAt,createdAt:new Date(row.created_at),updatedAt:row.updated_at?new Date(row.updated_at):new Date(row.created_at)};if(isIssuedBeforePasswordChange(row.created_at,await getPasswordChangedAt(refreshRow.user_id,trx)))throw new HttpError(401,"Invalid or expired refresh token");const newRefreshTokenPlain=generateSecureToken(40),newHashedRefreshToken=hashToken(newRefreshTokenPlain),refreshExpiresAt=new Date;refreshExpiresAt.setDate(refreshExpiresAt.getDate()+refreshExpiresInDays);if(isPostgres)await trx.unsafe(`
|
|
57
57
|
INSERT INTO oauth_refresh_tokens (access_token_id, token, revoked, expires_at, created_at)
|
|
58
58
|
VALUES ($1, $2, false, $3, ${appNow()})
|
|
59
59
|
`,[accessToken.id,newHashedRefreshToken,sqlDateTime(refreshExpiresAt)]);else await trx.unsafe(`
|
|
@@ -75,13 +75,13 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
|
|
|
75
75
|
WHERE access_token_id IN (
|
|
76
76
|
SELECT id FROM oauth_access_tokens WHERE user_id = ${param(1)}
|
|
77
77
|
)
|
|
78
|
-
`,[userId])}export async function deleteExpiredRefreshTokens(){const
|
|
78
|
+
`,[userId])}export async function deleteExpiredRefreshTokens(){const written=await db.unsafe(`
|
|
79
79
|
DELETE FROM oauth_refresh_tokens
|
|
80
80
|
WHERE expires_at < ${appNow()}
|
|
81
|
-
`);return
|
|
81
|
+
`);return Number(written?.changes??written?.rowCount??0)}export async function deleteRevokedRefreshTokens(daysOld=7){const cutoffDate=new Date;cutoffDate.setDate(cutoffDate.getDate()-daysOld);const written=await db.unsafe(`
|
|
82
82
|
DELETE FROM oauth_refresh_tokens
|
|
83
83
|
WHERE revoked = ${boolTrue} AND created_at < ${param(1)}
|
|
84
|
-
`,[sqlDateTime(cutoffDate)]);return
|
|
84
|
+
`,[sqlDateTime(cutoffDate)]);return Number(written?.changes??written?.rowCount??0)}export async function revokeToken(plainTextToken){const hashedToken=bearerLookupHash(plainTextToken);await db.unsafe(`
|
|
85
85
|
UPDATE oauth_refresh_tokens
|
|
86
86
|
SET revoked = ${boolTrue}
|
|
87
87
|
WHERE access_token_id IN (
|
|
@@ -128,18 +128,18 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
|
|
|
128
128
|
WHERE access_token_id IN (
|
|
129
129
|
SELECT id FROM oauth_access_tokens WHERE expires_at < ${appNow()}
|
|
130
130
|
)
|
|
131
|
-
`);const
|
|
131
|
+
`);const written=await db.unsafe(`
|
|
132
132
|
DELETE FROM oauth_access_tokens
|
|
133
133
|
WHERE expires_at < ${appNow()}
|
|
134
|
-
`);return
|
|
134
|
+
`);return Number(written?.changes??written?.rowCount??0)}export async function deleteRevokedTokens(daysOld=7){const cutoffDate=new Date;cutoffDate.setDate(cutoffDate.getDate()-daysOld);await db.unsafe(`
|
|
135
135
|
DELETE FROM oauth_refresh_tokens
|
|
136
136
|
WHERE access_token_id IN (
|
|
137
137
|
SELECT id FROM oauth_access_tokens WHERE revoked = ${boolTrue} AND updated_at < ${param(1)}
|
|
138
138
|
)
|
|
139
|
-
`,[sqlDateTime(cutoffDate)]);const
|
|
139
|
+
`,[sqlDateTime(cutoffDate)]);const written=await db.unsafe(`
|
|
140
140
|
DELETE FROM oauth_access_tokens
|
|
141
141
|
WHERE revoked = ${boolTrue} AND updated_at < ${param(1)}
|
|
142
|
-
`,[sqlDateTime(cutoffDate)]);return
|
|
142
|
+
`,[sqlDateTime(cutoffDate)]);return Number(written?.changes??written?.rowCount??0)}export async function clients(userId){return(await db.unsafe(`
|
|
143
143
|
SELECT * FROM oauth_clients
|
|
144
144
|
WHERE user_id = ${param(1)} AND revoked = ${boolFalse}
|
|
145
145
|
ORDER BY created_at DESC
|
|
@@ -151,9 +151,9 @@ import{createHash,randomBytes}from"node:crypto";import{db}from"@stacksjs/databas
|
|
|
151
151
|
`,[options.name,secret,options.redirect,options.personalAccessClient||!1,options.passwordClient||!1]);else await db.unsafe(`
|
|
152
152
|
INSERT INTO oauth_clients (name, secret, provider, redirect, personal_access_client, password_client, revoked, created_at)
|
|
153
153
|
VALUES (?, ?, 'local', ?, ?, ?, 0, ${appNow()})
|
|
154
|
-
`,[options.name,secret,options.redirect,options.personalAccessClient?1:0,options.passwordClient?1:0]);const
|
|
154
|
+
`,[options.name,secret,options.redirect,options.personalAccessClient?1:0,options.passwordClient?1:0]);const createdClient=(await db.unsafe(`
|
|
155
155
|
SELECT * FROM oauth_clients WHERE secret = ${param(1)} LIMIT 1
|
|
156
|
-
`,[secret]);return{client:mapToOAuthClient(
|
|
156
|
+
`,[secret]))[0];if(!createdClient)throw new HttpError(500,"Failed to read back the OAuth client that was just created.");return{client:mapToOAuthClient(createdClient),plainTextSecret:secret}}export async function revokeClient(clientId){await db.unsafe(`
|
|
157
157
|
UPDATE oauth_clients
|
|
158
158
|
SET revoked = ${boolTrue}, updated_at = ${appNow()}
|
|
159
159
|
WHERE id = ${param(1)}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/auth",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.73.0",
|
|
6
6
|
"description": "A more simplistic way to authenticate.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"better-dx": "^0.2.24",
|
|
65
|
-
"@stacksjs/error-handling": "0.
|
|
66
|
-
"@stacksjs/router": "0.
|
|
65
|
+
"@stacksjs/error-handling": "0.73.0",
|
|
66
|
+
"@stacksjs/router": "0.73.0"
|
|
67
67
|
}
|
|
68
68
|
}
|