@stacksjs/auth 0.74.31 → 0.74.32
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/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/referrals.d.ts +26 -0
- package/dist/referrals.js +1 -0
- package/dist/register.d.ts +1 -1
- package/dist/register.js +1 -1
- package/package.json +13 -13
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./authentication";export*from"./authenticator";export*from"./client";export*from"./middleware";export*from"./rate-limiter";export*from"./magic-link";export*from"./passkey";export*from"./password/reset";export*from"./register";export*from"./user";export*from"./tokens";export*from"./gate";export*from"./policy";export*from"./authorizable";export*from"./permissions";export*from"./rbac";export{createBqbRbacStore}from"./rbac-store-bqb";export{DEFAULT_ROLE_PACKS,seedDefaultRoles}from"./rbac-seed";export*from"./email-verification";export*from"./session-auth";export*from"./cookie-auth";export*from"./request-token";export*from"./page-gate";export*from"./socials";export{generateTOTP,verifyTOTP,generateTOTPSecret,totpKeyUri}from"@stacksjs/ts-auth";export*from"./two-factor";export*from"./team";
|
|
1
|
+
export*from"./authentication";export*from"./authenticator";export*from"./client";export*from"./middleware";export*from"./rate-limiter";export*from"./magic-link";export*from"./passkey";export*from"./password/reset";export*from"./register";export*from"./user";export*from"./tokens";export*from"./gate";export*from"./policy";export*from"./authorizable";export*from"./permissions";export*from"./rbac";export{createBqbRbacStore}from"./rbac-store-bqb";export{DEFAULT_ROLE_PACKS,seedDefaultRoles}from"./rbac-seed";export*from"./email-verification";export*from"./session-auth";export*from"./cookie-auth";export*from"./request-token";export*from"./page-gate";export*from"./socials";export{generateTOTP,verifyTOTP,generateTOTPSecret,totpKeyUri}from"@stacksjs/ts-auth";export*from"./two-factor";export*from"./team";export*from"./referrals";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare function normalizeReferralCode(value: unknown): string | null;
|
|
2
|
+
/** Stable, unguessable share code. The unique user constraint handles concurrent creation. */
|
|
3
|
+
export declare function createReferralCode(ownerId: number, database?: ReferralDatabase): Promise<string>;
|
|
4
|
+
/**
|
|
5
|
+
* First attribution wins. Call only from trusted new-account creation, never a
|
|
6
|
+
* public claim endpoint. Pass the registration transaction to commit together.
|
|
7
|
+
* Invalid, unknown, repeated, and self referrals do not disrupt registration.
|
|
8
|
+
*/
|
|
9
|
+
export declare function attributeReferral(newUserId: number, input: unknown, database?: ReferralDatabase): Promise<boolean>;
|
|
10
|
+
/** Server-only conversion hook. Replays preserve the first qualification time. */
|
|
11
|
+
export declare function qualifyReferral(referredUserId: number, database?: ReferralDatabase): Promise<void>;
|
|
12
|
+
/** Aggregate only: a referrer never receives another account's email or profile. */
|
|
13
|
+
export declare function referralSummary(ownerId: number, database?: ReferralDatabase): Promise<ReferralSummary>;
|
|
14
|
+
/** Minimal database contract, also accepted by transaction-scoped connections. */
|
|
15
|
+
export declare interface ReferralDatabase {
|
|
16
|
+
unsafe: (sql: string, bindings?: unknown[]) => { execute: () => Promise<unknown> }
|
|
17
|
+
}
|
|
18
|
+
export declare interface ReferralCode {
|
|
19
|
+
code: string
|
|
20
|
+
user_id: number
|
|
21
|
+
}
|
|
22
|
+
export declare interface ReferralSummary {
|
|
23
|
+
code: string | null
|
|
24
|
+
referred: number
|
|
25
|
+
qualified: number
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{randomBytes}from"node:crypto";export function normalizeReferralCode(value){if(typeof value!=="string")return null;const code=value.trim().toLowerCase();return/^[a-f0-9]{24}$/.test(code)?code:null}function userId(value){if(!Number.isSafeInteger(value)||value<1)throw TypeError("A positive integer user ID is required.")}async function connection(database){return database??(await import("@stacksjs/database")).db}async function rows(database,sql,bindings){const result=await database.unsafe(sql,bindings).execute();if(Array.isArray(result))return result;if(result&&typeof result==="object"&&"rows"in result&&Array.isArray(result.rows))return result.rows;throw Error("The referral query returned an unsupported result.")}async function duplicate(error){const{isUniqueViolation}=await import("@stacksjs/orm");return isUniqueViolation(error)}export async function createReferralCode(ownerId,database){userId(ownerId);const db=await connection(database);for(let attempt=0;attempt<5;attempt++){const existing=await rows(db,"SELECT code, user_id FROM referral_codes WHERE user_id = ?",[ownerId]);if(existing[0])return existing[0].code;const code=randomBytes(12).toString("hex");try{await db.unsafe("INSERT INTO referral_codes (user_id, code) VALUES (?, ?)",[ownerId,code]).execute();return code}catch(error){if(!await duplicate(error))throw error}}throw Error("Could not allocate a referral code. Please retry.")}export async function attributeReferral(newUserId,input,database){userId(newUserId);const code=normalizeReferralCode(input);if(!code)return!1;const db=await connection(database),owner=(await rows(db,"SELECT code, user_id FROM referral_codes WHERE code = ?",[code]))[0];if(!owner||Number(owner.user_id)===newUserId)return!1;try{await db.unsafe("INSERT INTO referrals (referrer_id, referred_user_id, code, status) VALUES (?, ?, ?, ?)",[Number(owner.user_id),newUserId,code,"registered"]).execute();return!0}catch(error){if(await duplicate(error))return!1;throw error}}export async function qualifyReferral(referredUserId,database){userId(referredUserId);const db=await connection(database),now=new Date().toISOString().slice(0,19).replace("T"," ");await db.unsafe("UPDATE referrals SET status = ?, qualified_at = ?, updated_at = ? WHERE referred_user_id = ? AND status = ?",["qualified",now,now,referredUserId,"registered"]).execute()}export async function referralSummary(ownerId,database){userId(ownerId);const db=await connection(database),codes=await rows(db,"SELECT code, user_id FROM referral_codes WHERE user_id = ?",[ownerId]),counts=await rows(db,"SELECT COUNT(*) AS total, SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) AS qualified FROM referrals WHERE referrer_id = ?",["qualified",ownerId]);return{code:codes[0]?.code??null,referred:Number(counts[0]?.total??0),qualified:Number(counts[0]?.qualified??0)}}
|
package/dist/register.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ import type { NewUser } from '@stacksjs/orm';
|
|
|
14
14
|
*
|
|
15
15
|
* Additive, so `const { token } = await register(...)` is unaffected.
|
|
16
16
|
*/
|
|
17
|
-
export declare function register(credentials: NewUser): Promise<RegistrationResult>;
|
|
17
|
+
export declare function register(credentials: NewUser & { referralCode?: string }): Promise<RegistrationResult>;
|
|
18
18
|
/**
|
|
19
19
|
* What a successful registration hands back: a complete session, matching
|
|
20
20
|
* `Auth.loginUsingId()`.
|
package/dist/register.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{config}from"@stacksjs/config";import{db}from"@stacksjs/database";import{HttpError}from"@stacksjs/error-handling";import{User}from"@stacksjs/orm";import{makeHash}from"@stacksjs/security";import{Auth}from"./authentication";import{isUniqueViolation}from"./rbac-store-bqb";function duplicateEmailError(){if(config.auth?.registration?.preventEnumeration===!1)return new HttpError(409,"Email already exists");return new HttpError(422,"Registration could not be completed. Please check your details and try again.")}const EMAIL_RE=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;export async function register(credentials){const{email,password,name}=credentials;if(typeof email!=="string"||email.length>254||!EMAIL_RE.test(email))throw new HttpError(422,"Email address is invalid");if(typeof password!=="string"||password.length<8)throw new HttpError(422,"Password must be at least 8 characters");const hashedPassword=await makeHash(password,{algorithm:"bcrypt"}),userId=await db.transaction(async(rawTrx)=>{const trx=rawTrx;if(await trx.selectFrom("users").where("email","=",email).selectAll().executeTakeFirst())throw duplicateEmailError();try{await trx.insertInto("users").values({email,password:hashedPassword,name}).execute()}catch(err){if(isUniqueViolation(err))throw duplicateEmailError();throw err}const created=await trx.selectFrom("users").where("email","=",email).selectAll().executeTakeFirst();if(!created)throw Error("Failed to retrieve created user");return Number(created.id)}),user=await User.find(userId);if(!user)throw Error("Failed to retrieve created user");const{plainTextToken,refreshToken,expiresIn}=await Auth.createTokenForUser(user,{name:"user-auth-token"});return{token:plainTextToken,refreshToken,expiresIn}}
|
|
1
|
+
import{config}from"@stacksjs/config";import{db}from"@stacksjs/database";import{HttpError}from"@stacksjs/error-handling";import{User}from"@stacksjs/orm";import{makeHash}from"@stacksjs/security";import{Auth}from"./authentication";import{isUniqueViolation}from"./rbac-store-bqb";import{attributeReferral}from"./referrals";function duplicateEmailError(){if(config.auth?.registration?.preventEnumeration===!1)return new HttpError(409,"Email already exists");return new HttpError(422,"Registration could not be completed. Please check your details and try again.")}const EMAIL_RE=/^[^\s@]+@[^\s@]+\.[^\s@]+$/;export async function register(credentials){const{email,password,name}=credentials;if(typeof email!=="string"||email.length>254||!EMAIL_RE.test(email))throw new HttpError(422,"Email address is invalid");if(typeof password!=="string"||password.length<8)throw new HttpError(422,"Password must be at least 8 characters");const hashedPassword=await makeHash(password,{algorithm:"bcrypt"}),userId=await db.transaction(async(rawTrx)=>{const trx=rawTrx;if(await trx.selectFrom("users").where("email","=",email).selectAll().executeTakeFirst())throw duplicateEmailError();try{await trx.insertInto("users").values({email,password:hashedPassword,name}).execute()}catch(err){if(isUniqueViolation(err))throw duplicateEmailError();throw err}const created=await trx.selectFrom("users").where("email","=",email).selectAll().executeTakeFirst();if(!created)throw Error("Failed to retrieve created user");if(credentials.referralCode)await attributeReferral(Number(created.id),credentials.referralCode,trx);return Number(created.id)}),user=await User.find(userId);if(!user)throw Error("Failed to retrieve created user");const{plainTextToken,refreshToken,expiresIn}=await Auth.createTokenForUser(user,{name:"user-auth-token"});return{token:plainTextToken,refreshToken,expiresIn}}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/auth",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.74.
|
|
5
|
+
"version": "0.74.32",
|
|
6
6
|
"description": "A more simplistic way to authenticate.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -58,18 +58,18 @@
|
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"@stacksjs/bun-router": "^0.1.11",
|
|
61
|
-
"@stacksjs/cache": "0.74.
|
|
62
|
-
"@stacksjs/config": "0.74.
|
|
63
|
-
"@stacksjs/database": "0.74.
|
|
64
|
-
"@stacksjs/email": "0.74.
|
|
65
|
-
"@stacksjs/env": "0.74.
|
|
66
|
-
"@stacksjs/error-handling": "0.74.
|
|
67
|
-
"@stacksjs/logging": "0.74.
|
|
68
|
-
"@stacksjs/orm": "0.74.
|
|
69
|
-
"@stacksjs/path": "0.74.
|
|
70
|
-
"@stacksjs/router": "0.74.
|
|
71
|
-
"@stacksjs/security": "0.74.
|
|
72
|
-
"@stacksjs/storage": "0.74.
|
|
61
|
+
"@stacksjs/cache": "0.74.32",
|
|
62
|
+
"@stacksjs/config": "0.74.32",
|
|
63
|
+
"@stacksjs/database": "0.74.32",
|
|
64
|
+
"@stacksjs/email": "0.74.32",
|
|
65
|
+
"@stacksjs/env": "0.74.32",
|
|
66
|
+
"@stacksjs/error-handling": "0.74.32",
|
|
67
|
+
"@stacksjs/logging": "0.74.32",
|
|
68
|
+
"@stacksjs/orm": "0.74.32",
|
|
69
|
+
"@stacksjs/path": "0.74.32",
|
|
70
|
+
"@stacksjs/router": "0.74.32",
|
|
71
|
+
"@stacksjs/security": "0.74.32",
|
|
72
|
+
"@stacksjs/storage": "0.74.32",
|
|
73
73
|
"@stacksjs/ts-auth": "^0.4.4",
|
|
74
74
|
"ts-qr-codes": "^0.1.8"
|
|
75
75
|
},
|