@cauth/core 0.0.10 → 0.1.1
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 +95 -73
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,68 +1,6 @@
|
|
|
1
1
|
import z$1, { z } from "zod";
|
|
2
2
|
import ms from "ms";
|
|
3
3
|
|
|
4
|
-
//#region src/errors/errors.d.ts
|
|
5
|
-
declare class CError {
|
|
6
|
-
static type: string;
|
|
7
|
-
code: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* @description Error thrown when the credentials provided do not match.
|
|
11
|
-
*/
|
|
12
|
-
declare class CredentialMismatchError extends Error implements CError {
|
|
13
|
-
code: string;
|
|
14
|
-
static type: string;
|
|
15
|
-
constructor();
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* @description Error thrown when the data provided is invalid.
|
|
19
|
-
*/
|
|
20
|
-
declare class InvalidDataError extends Error implements CError {
|
|
21
|
-
code: string;
|
|
22
|
-
static type: string;
|
|
23
|
-
constructor(reason: string);
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* @description Error thrown when the account is not found.
|
|
27
|
-
*/
|
|
28
|
-
declare class AccountNotFoundError extends Error implements CError {
|
|
29
|
-
code: string;
|
|
30
|
-
static type: string;
|
|
31
|
-
constructor();
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* @description Error thrown when the role provided is invalid.
|
|
35
|
-
*/
|
|
36
|
-
declare class InvalidRoleError extends Error implements CError {
|
|
37
|
-
code: string;
|
|
38
|
-
static type: string;
|
|
39
|
-
constructor(roles: string[]);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* @description Error thrown when an invalid or expired refresh token is provided
|
|
43
|
-
*/
|
|
44
|
-
declare class InvalidRefreshTokenError extends Error implements CError {
|
|
45
|
-
code: string;
|
|
46
|
-
static type: string;
|
|
47
|
-
constructor();
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* @description Error thrown when trying to create an account that already exists
|
|
51
|
-
*/
|
|
52
|
-
declare class DuplicateAccountError extends Error implements CError {
|
|
53
|
-
code: string;
|
|
54
|
-
static type: string;
|
|
55
|
-
constructor();
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* @description Error thrown when an invalid or expired OTP is provided
|
|
59
|
-
*/
|
|
60
|
-
declare class InvalidOTPCode extends Error implements CError {
|
|
61
|
-
code: string;
|
|
62
|
-
static type: string;
|
|
63
|
-
constructor();
|
|
64
|
-
}
|
|
65
|
-
//#endregion
|
|
66
4
|
//#region src/types/auth.t.d.ts
|
|
67
5
|
type Account = {
|
|
68
6
|
id: string;
|
|
@@ -90,10 +28,68 @@ declare const AuthModelSchema: z$1.ZodObject<{
|
|
|
90
28
|
}, z$1.z.core.$strip>;
|
|
91
29
|
type AuthModel = z$1.infer<typeof AuthModelSchema>;
|
|
92
30
|
//#endregion
|
|
31
|
+
//#region src/errors/errors.d.ts
|
|
32
|
+
type CAuthErrorShape = {
|
|
33
|
+
type: string;
|
|
34
|
+
message: string;
|
|
35
|
+
code: string | number;
|
|
36
|
+
name: string;
|
|
37
|
+
};
|
|
38
|
+
declare const CAuthErrors: {
|
|
39
|
+
/** @description Error thrown when the credentials provided do not match. */
|
|
40
|
+
CredentialMismatchError: {
|
|
41
|
+
type: "credential-error";
|
|
42
|
+
message: "Credential mismatch. Please check your credentials and try again.";
|
|
43
|
+
code: "credential-mismatch";
|
|
44
|
+
name: "CredentialMismatchError";
|
|
45
|
+
};
|
|
46
|
+
/** @description Error thrown when the data provided is invalid. */
|
|
47
|
+
InvalidDataError: (reason: string) => CAuthErrorShape;
|
|
48
|
+
/** @description Error thrown when the account is not found. */
|
|
49
|
+
AccountNotFoundError: {
|
|
50
|
+
type: "invalid-data-error";
|
|
51
|
+
message: "Account not found";
|
|
52
|
+
code: "account-not-found";
|
|
53
|
+
name: "AccountNotFoundError";
|
|
54
|
+
};
|
|
55
|
+
/** @description Error thrown when the role provided is invalid. */
|
|
56
|
+
InvalidRoleError: (roles: string[]) => CAuthErrorShape;
|
|
57
|
+
/** @description Error thrown when an invalid or expired refresh token is provided */
|
|
58
|
+
InvalidRefreshTokenError: {
|
|
59
|
+
type: "validation-error";
|
|
60
|
+
message: "Invalid refresh token";
|
|
61
|
+
code: "invalid-refresh-token";
|
|
62
|
+
name: "InvalidRefreshTokenError";
|
|
63
|
+
};
|
|
64
|
+
/** @description Error thrown when trying to create an account that already exists */
|
|
65
|
+
DuplicateAccountError: {
|
|
66
|
+
type: "validation-error";
|
|
67
|
+
message: "Account with this credentials already exists";
|
|
68
|
+
code: "account-already-exists";
|
|
69
|
+
name: "DuplicateAccountError";
|
|
70
|
+
};
|
|
71
|
+
/** @description Error thrown when an invalid or expired OTP is provided */
|
|
72
|
+
InvalidOTPCode: {
|
|
73
|
+
type: "validation-error";
|
|
74
|
+
message: "Invalid Otp. Please check and try again";
|
|
75
|
+
code: "invalid-otp";
|
|
76
|
+
name: "InvalidOTPCode";
|
|
77
|
+
};
|
|
78
|
+
/** @description Error thrown when CAuth Schema error */
|
|
79
|
+
SchemaInvalidError: {
|
|
80
|
+
type: "validation-error";
|
|
81
|
+
message: "Your database error is not is sync with CAuth Spec";
|
|
82
|
+
code: "schema-validation";
|
|
83
|
+
name: string;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
type CAuthErrorObject = ReturnType<Extract<(typeof CAuthErrors)[keyof typeof CAuthErrors], (...args: any) => any>> | Extract<(typeof CAuthErrors)[keyof typeof CAuthErrors], object>;
|
|
87
|
+
declare function isCAuthError(err: unknown, name: keyof typeof CAuthErrors): err is CAuthErrorObject;
|
|
88
|
+
declare function is(err: unknown, name: keyof typeof CAuthErrors): boolean;
|
|
89
|
+
//#endregion
|
|
93
90
|
//#region src/types/result.t.d.ts
|
|
94
91
|
type FNError = {
|
|
95
|
-
|
|
96
|
-
error: Error;
|
|
92
|
+
error: CAuthErrorShape;
|
|
97
93
|
};
|
|
98
94
|
/**
|
|
99
95
|
* @description Core Result type.
|
|
@@ -250,6 +246,38 @@ declare const LoginSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
250
246
|
password: z.ZodOptional<z.ZodString>;
|
|
251
247
|
}, z.core.$strip>]>;
|
|
252
248
|
type LoginSchemaType = z.infer<typeof LoginSchema>;
|
|
249
|
+
declare const OTPCodeUnion: z.ZodUnion<readonly [z.ZodObject<{
|
|
250
|
+
email: z.ZodEmail;
|
|
251
|
+
phoneNumber: z.ZodOptional<z.ZodNever>;
|
|
252
|
+
code: z.ZodString;
|
|
253
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
254
|
+
phoneNumber: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
255
|
+
email: z.ZodOptional<z.ZodNever>;
|
|
256
|
+
code: z.ZodString;
|
|
257
|
+
}, z.core.$strip>]>;
|
|
258
|
+
type OTPLogin = z.infer<typeof OTPCodeUnion>;
|
|
259
|
+
declare const RequestOTPCodeSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
260
|
+
otpPurpose: z.ZodEnum<{
|
|
261
|
+
LOGIN: "LOGIN";
|
|
262
|
+
RESET_PASSWORD: "RESET_PASSWORD";
|
|
263
|
+
ACTION: "ACTION";
|
|
264
|
+
}>;
|
|
265
|
+
usePassword: z.ZodDefault<z.ZodBoolean>;
|
|
266
|
+
password: z.ZodOptional<z.ZodString>;
|
|
267
|
+
phoneNumber: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
268
|
+
email: z.ZodNever;
|
|
269
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
270
|
+
otpPurpose: z.ZodEnum<{
|
|
271
|
+
LOGIN: "LOGIN";
|
|
272
|
+
RESET_PASSWORD: "RESET_PASSWORD";
|
|
273
|
+
ACTION: "ACTION";
|
|
274
|
+
}>;
|
|
275
|
+
usePassword: z.ZodDefault<z.ZodBoolean>;
|
|
276
|
+
password: z.ZodOptional<z.ZodString>;
|
|
277
|
+
phoneNumber: z.ZodNever;
|
|
278
|
+
email: z.ZodString;
|
|
279
|
+
}, z.core.$strip>]>;
|
|
280
|
+
type RequestOTP = z.infer<typeof RequestOTPCodeSchema>;
|
|
253
281
|
declare const RegisterSchema: z.ZodObject<{
|
|
254
282
|
phoneNumber: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
255
283
|
email: z.ZodOptional<z.ZodEmail>;
|
|
@@ -322,17 +350,11 @@ declare class _CAuth<T extends string[], TContractor extends RoutesContract<any>
|
|
|
322
350
|
}: ChangePasswordSchemaType) => Promise<Result<unknown>>;
|
|
323
351
|
RequestOTPCode: ({
|
|
324
352
|
...args
|
|
325
|
-
}:
|
|
326
|
-
password?: string;
|
|
327
|
-
usePassword?: boolean;
|
|
328
|
-
otpPurpose: OtpPurpose;
|
|
329
|
-
}) => Promise<Result<{
|
|
353
|
+
}: RequestOTP) => Promise<Result<{
|
|
330
354
|
id: string;
|
|
331
355
|
code: string;
|
|
332
356
|
}>>;
|
|
333
|
-
LoginWithOTP: (args:
|
|
334
|
-
code: string;
|
|
335
|
-
}) => Promise<Result<{
|
|
357
|
+
LoginWithOTP: (args: OTPLogin) => Promise<Result<{
|
|
336
358
|
account: Account;
|
|
337
359
|
tokens: Tokens;
|
|
338
360
|
}>>;
|
|
@@ -363,4 +385,4 @@ declare function CAuth<const T extends string[], const TContractor extends Route
|
|
|
363
385
|
routeContractor: TContractor;
|
|
364
386
|
}): _CAuth<T, TContractor>;
|
|
365
387
|
//#endregion
|
|
366
|
-
export {
|
|
388
|
+
export { CAuth, CAuthErrors, type CAuthOptions, type DatabaseContract, type RoutesContract, is, isCAuthError };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import e,{z as t}from"zod";import{parsePhoneNumberFromString as n}from"libphonenumber-js";import r from"bcrypt";import i from"jsonwebtoken";var a=class{static ValidationError=`validation-error`;static CredentialError=`credential-error`;static UnKnownError=`unknown-error`;static InvalidDataError=`invalid-data-error`},o=class{static ServerError=`internal-server-error`;static ServerErrorMessage=`Internal server error. We are working to fix this, please try again later`;static InvalidToken=`invalid-token`;static InvalidTokenMessage=`Invalid Token`;static ForbiddenResource=`forbidden-resource`;static ForbiddenResourceMessage=`You don't have sufficient permission for this action`;static InvalidOtp=`invalid-otp`;static InvalidOtpMessage=`Invalid Otp. Please check and try again`;static CredentialMismatch=`credential-mismatch`;static CredentialMismatchMessage=`Credential mismatch. Please check your credentials and try again.`;static InvalidData=`invalid-data`;static InvalidDataMessage=e=>`Invalid Body: ${e}`;static AccountNotFound=`account-not-found`;static AccountNotFoundMessage=`Account not found`;static InvalidRole=`invalid-role`;static InvalidRoleMessage=e=>`Role is invalid, please use one of the following roles: ${e.join(`, `)}`;static InvalidRefreshToken=`invalid-refresh-token`;static InvalidRefreshTokenMessage=`Invalid refresh token`;static DuplicateAccount=`account-already-exists`;static DuplicateAccountMessage=`Account with this credentials already exists`},s=class extends Error{code;static type=a.CredentialError;constructor(){super(o.CredentialMismatchMessage),this.code=o.CredentialMismatch,this.name=`CredentialMismatch`}},c=class extends Error{code;static type=a.ValidationError;constructor(e){super(o.InvalidDataMessage(e)),this.code=o.InvalidData,this.name=`InvalidDataError`}},l=class extends Error{code;static type=a.InvalidDataError;constructor(){super(o.AccountNotFoundMessage),this.code=o.AccountNotFound,this.name=`AccountNotFoundError`}},u=class extends Error{code;static type=a.ValidationError;constructor(e){super(o.InvalidRoleMessage(e)),this.code=o.InvalidRole,this.name=`InvalidRoleError`}},d=class extends Error{code;static type=a.ValidationError;constructor(){super(o.InvalidRefreshTokenMessage),this.code=o.InvalidRefreshToken,this.name=`InvalidRefreshTokenError`}},f=class extends Error{code;static type=a.ValidationError;constructor(){super(o.DuplicateAccountMessage),this.code=o.DuplicateAccount,this.name=`DuplicateAccountError`}},p=class extends Error{code;static type=a.ValidationError;constructor(){super(o.InvalidOtpMessage),this.code=o.InvalidOtp,this.name=`InvalidOTPCode`}},m=class{static LoginPurpose=`LOGIN`;static ResetPasswordPurpose=`RESET_PASSWORD`;static ActionPurpose=`ACTION`};const h=t.string().trim().refine(e=>{let t=n(e);return!!t&&t.isValid()},{message:`Invalid phone number`}).transform(e=>n(e)?.format(`E.164`)??e),g=t.object({email:t.email(),phoneNumber:t.never().optional(),password:t.string().min(6).optional()}),_=t.object({phoneNumber:h,email:t.never().optional(),password:t.string().min(6).optional()}),v=t.union([g,_]).superRefine((e,n)=>{e.email&&e.phoneNumber&&n.addIssue({code:t.ZodIssueCode.custom,message:`Provide either email or phoneNumber`,path:[`email`,`phoneNumber`]})}),y=t.object({phoneNumber:h.optional(),email:t.email().optional(),role:t.string(),password:t.string().optional()}).superRefine((e,n)=>{!e.email&&!e.phoneNumber&&n.addIssue({code:t.ZodIssueCode.custom,message:`Provide either email or phoneNumber`,path:[`email`,`phoneNumber`]})}),b=t.object({refreshToken:t.string()}),x=t.object({refreshToken:t.string()}),S=t.object({accountId:t.string(),oldPassword:t.string(),newPassword:t.string()});function C(e){return`${e?.error?.issues[0].path}: ${e?.error?.issues[0].message}`}function w(e){return{success:!0,value:e}}function T(...e){return{success:!1,errors:e}}async function E({config:e},t){let n=v.safeParse({email:t.email,phoneNumber:t.phoneNumber});if(!n.success)return T({type:c.type,error:new c(C(n))});let i=await e.dbContractor.findAccountWithCredential({phoneNumber:t.phoneNumber,email:t.email});if(!i||t.usePassword&&!await r.compare(String(t.password),String(i?.passwordHash)))return T({type:s.type,error:new s});let a=await e.dbContractor.createOTP({config:e},{id:i.id,purpose:t.otpPurpose});return w({id:i.id,code:a.code})}async function D({config:e,tokens:t},n){let r=v.safeParse({email:n.email,phoneNumber:n.phoneNumber});if(!r.success)return T({type:c.type,error:new c(C(r))});let i=await e.dbContractor.findAccountWithCredential({email:n.email,phoneNumber:n.phoneNumber});if(!i)return T({type:s.type,error:new s});if(!(await e.dbContractor.verifyOTP({id:i.id,code:n.code,purpose:m.LoginPurpose})).isValid)return T({type:p.type,error:new p});let a=await t.GenerateTokenPairs({id:i.id,role:i.role});return await e.dbContractor.updateAccountLogin({id:i.id,refreshToken:a.refreshToken}),delete i.passwordHash,delete i.refreshTokens,w({account:i,tokens:a})}async function O({config:e},t){return w({isValid:(await e.dbContractor.verifyOTP({id:t.id,code:t.code,purpose:t.otpPurpose})).isValid})}async function k({config:e},{...t}){let n=S.safeParse(t);if(!n.success)return T({type:c.type,error:new c(C(n))});let i=await e.dbContractor.findAccountById({id:t.accountId});if(!i)return T({type:l.type,error:new l});if(!r.compare(t.oldPassword,String(i.passwordHash)))return T({type:s.type,error:new s});let a=await r.hash(t.newPassword,10);return await e.dbContractor.updateAccount({id:i.id,data:{passwordHash:a}}),w({})}async function A({config:e,tokens:t},{...n}){let i=v.safeParse(n);if(!i.success)return T({type:c.type,error:new c(C(i))});let a=await e.dbContractor.findAccountWithCredential({email:n.email,phoneNumber:n.phoneNumber});if(!a||!await r.compare(String(n.password),String(a?.passwordHash)))return T({type:s.type,error:new s});let o=await t.GenerateTokenPairs({id:a.id,role:a.role});return await e.dbContractor.updateAccountLogin({id:a.id,refreshToken:o.refreshToken}),delete a.passwordHash,delete a.refreshTokens,w({account:a,tokens:o})}async function j(e){try{return{data:await e,error:null}}catch(e){return{data:null,error:e}}}async function M({config:e,tokens:t},{...n}){let r=x.safeParse(n);if(!r.success)return T({type:c.type,error:new c(C(r))});let i=await j(t.VerifyRefreshToken(n.refreshToken));return i.error||!i?T({type:d.type,error:new d}):(await e.dbContractor.removeAndAddRefreshToken({id:String(i.data?.id),refreshToken:n.refreshToken}),w({}))}async function N({config:e,tokens:t},{...n}){let r=b.safeParse(n);if(!r.success)return T({type:c.type,error:new c(C(r))});let i=await j(t.VerifyRefreshToken(n.refreshToken));if(i.error)return T({type:d.type,error:new d});let a=await e.dbContractor.findAccountById({id:String(i.data?.id)});if(!a)return T({type:l.type,error:new l});if(!a?.refreshTokens?.includes(n.refreshToken))return T({type:d.type,error:new d});let o=await t.GenerateTokenPairs({id:a.id,role:a.role});return await e.dbContractor.removeAndAddRefreshToken({id:a.id,refreshToken:n.refreshToken,newRefreshToken:o.refreshToken}),delete a.refreshTokens,delete a.passwordHash,w({account:a,tokens:o})}async function P({config:e,tokens:t},{...n}){let i=y.safeParse(n);if(!i.success)return T({type:c.type,error:new c(C(i))});if(!e.roles?.includes(n.role))return T({type:u.type,error:new u(e.roles)});if(await e.dbContractor.findAccountWithCredential({email:n.email,phoneNumber:n.phoneNumber}))return T({type:f.type,error:new f});let a=await r.hash(n.password,10),o=await e.dbContractor.createAccount({data:{email:n.email,phoneNumber:n.phoneNumber,passwordHash:a,role:n.role,lastLogin:new Date}}),s=await t.GenerateTokenPairs({id:o.id,role:o.role});return await e.dbContractor.updateAccountLogin({id:o.id,refreshToken:s.refreshToken}),w({account:o,tokens:s})}async function F({...e}){return i.sign(e.payload,e.config.jwtConfig.accessTokenSecret,{expiresIn:e.config.jwtConfig?.accessTokenLifeSpan??`15m`})}async function I({...e}){return i.sign(e.payload,e.config.jwtConfig.refreshTokenSecret,{expiresIn:e.config.jwtConfig?.refreshTokenLifeSpan??`30d`})}async function L({...e}){return{accessToken:i.sign(e.payload,e.config.jwtConfig.accessTokenSecret,{expiresIn:e.config.jwtConfig?.accessTokenLifeSpan??`15m`}),refreshToken:i.sign(e.payload,e.config.jwtConfig.refreshTokenSecret,{expiresIn:e.config.jwtConfig?.refreshTokenLifeSpan??`30d`})}}async function R({...e}){let t=i.verify(e.token,e.config.jwtConfig.refreshTokenSecret);return t instanceof String?null:t}async function z({...e}){let t=i.verify(e.token,e.config.jwtConfig.accessTokenSecret);return t instanceof String?null:t}const B=e.custom(()=>!0,{message:`Invalid dbContractor: must implement Database Contract interface`}),V=e.custom(()=>!0,{message:`Invalid routeContractor: must implement RoutesContract interface`}),H=e.custom(),U=e.object({dbContractor:B,routeContractor:V,roles:e.array(e.string()).min(1),jwtConfig:e.object({refreshTokenSecret:e.string(),accessTokenSecret:e.string(),accessTokenLifeSpan:H.optional(),refreshTokenLifeSpan:H.optional()}),otpConfig:e.object({expiresIn:e.number().optional(),length:e.number().min(4).max(8).optional()})});var W=class{#config;constructor(e){if(!U.safeParse(e).success)throw Error(`❌ Failed to initiate CAuth. You provided an invalid config!`);this.#config=e}get RoleType(){return null}Guard=e=>this.#config.routeContractor.Guard({config:this.#config,tokens:this.Tokens,roles:e});Routes={Register:()=>this.#config.routeContractor.Register({config:this.#config,tokens:this.Tokens}),Login:()=>this.#config.routeContractor.Login({config:this.#config,tokens:this.Tokens}),Logout:()=>this.#config.routeContractor.Logout({config:this.#config,tokens:this.Tokens}),Refresh:()=>this.#config.routeContractor.Refresh({config:this.#config,tokens:this.Tokens}),ChangePassword:e=>this.#config.routeContractor.ChangePassword({config:this.#config,tokens:this.Tokens,userId:e})};FN={Login:({...e})=>A({config:this.#config,tokens:this.Tokens},e),Register:({...e})=>P({config:this.#config,tokens:this.Tokens},e),Logout:({...e})=>M({config:this.#config,tokens:this.Tokens},e),Refresh:({...e})=>N({config:this.#config,tokens:this.Tokens},e),ChangePassword:({...e})=>k({config:this.#config,tokens:this.Tokens},e),RequestOTPCode:({...e})=>E({config:this.#config,tokens:this.Tokens},e),LoginWithOTP:e=>D({config:this.#config,tokens:this.Tokens},e),VerifyOTP:e=>O({config:this.#config,tokens:this.Tokens},e)};Tokens={GenerateRefreshToken:e=>I({payload:e,config:this.#config}),GenerateAccessToken:e=>F({payload:e,config:this.#config}),GenerateTokenPairs:e=>L({payload:e,config:this.#config}),VerifyRefreshToken:e=>R({token:e,config:this.#config}),VerifyAccessToken:e=>z({token:e,config:this.#config})}};function G(e){return new W(e)}export{l as AccountNotFoundError,G as CAuth,s as CredentialMismatchError,f as DuplicateAccountError,c as InvalidDataError,p as InvalidOTPCode,d as InvalidRefreshTokenError,u as InvalidRoleError};
|
|
1
|
+
import e,{z as t}from"zod";import{parsePhoneNumberFromString as n}from"libphonenumber-js";import r from"bcrypt";import i from"jsonwebtoken";var a=class{static LoginPurpose=`LOGIN`;static ResetPasswordPurpose=`RESET_PASSWORD`;static ActionPurpose=`ACTION`};const o=t.string().trim().refine(e=>{let t=n(e);return!!t&&t.isValid()},{message:`Invalid phone number`}).transform(e=>n(e)?.format(`E.164`)??e),s=t.enum([`LOGIN`,`RESET_PASSWORD`,`ACTION`]),c=t.object({email:t.email(),phoneNumber:t.never().optional(),password:t.string().min(6).optional()}),l=t.object({phoneNumber:o,email:t.never().optional(),password:t.string().min(6).optional()}),u=t.union([c,l]).superRefine((e,n)=>{e.email&&e.phoneNumber&&n.addIssue({code:t.ZodIssueCode.custom,message:`Provide either email or phoneNumber`,path:[`email`,`phoneNumber`]})}),d=t.object({phoneNumber:o,email:t.never().optional(),code:t.string().min(4).max(8)}),f=t.object({email:t.email(),phoneNumber:t.never().optional(),code:t.string().min(4).max(8)}),p=t.union([f,d]),m=t.object({otpPurpose:s,usePassword:t.boolean().default(!1),password:t.string().optional()}),h=m.extend({phoneNumber:o,email:t.never()}),g=m.extend({phoneNumber:t.never(),email:t.string().email()});t.union([h,g]).refine(e=>e.usePassword?!!e.password:!e.password,{message:`Password required only if usePassword is true`,path:[`password`]});const _=t.object({phoneNumber:o.optional(),email:t.email().optional(),role:t.string(),password:t.string().optional()}).superRefine((e,n)=>{!e.email&&!e.phoneNumber&&n.addIssue({code:t.ZodIssueCode.custom,message:`Provide either email or phoneNumber`,path:[`email`,`phoneNumber`]})}),v=t.object({refreshToken:t.string()}),y=t.object({refreshToken:t.string()}),b=t.object({accountId:t.string(),oldPassword:t.string(),newPassword:t.string()});var x=class{static ValidationError=`validation-error`;static CredentialError=`credential-error`;static UnKnownError=`unknown-error`;static InvalidDataError=`invalid-data-error`},S=class{static ServerError=`internal-server-error`;static ServerErrorMessage=`Internal server error. We are working to fix this, please try again later`;static InvalidToken=`invalid-token`;static InvalidTokenMessage=`Invalid Token`;static ForbiddenResource=`forbidden-resource`;static ForbiddenResourceMessage=`You don't have sufficient permission for this action`;static InvalidOtp=`invalid-otp`;static InvalidOtpMessage=`Invalid Otp. Please check and try again`;static CredentialMismatch=`credential-mismatch`;static CredentialMismatchMessage=`Credential mismatch. Please check your credentials and try again.`;static InvalidData=`invalid-data`;static InvalidDataMessage=e=>`Invalid Body: ${e}`;static AccountNotFound=`account-not-found`;static AccountNotFoundMessage=`Account not found`;static InvalidRole=`invalid-role`;static InvalidRoleMessage=e=>`Role is invalid, please use one of the following roles: ${e.join(`, `)}`;static InvalidRefreshToken=`invalid-refresh-token`;static InvalidRefreshTokenMessage=`Invalid refresh token`;static DuplicateAccount=`account-already-exists`;static DuplicateAccountMessage=`Account with this credentials already exists`;static SchemaValidationError=`schema-validation`;static SchemaValidationMessage=`Your database error is not is sync with CAuth Spec`};const C={CredentialMismatchError:{type:x.CredentialError,message:S.CredentialMismatchMessage,code:S.CredentialMismatch,name:`CredentialMismatchError`},InvalidDataError:e=>({type:x.ValidationError,message:S.InvalidDataMessage(e),code:S.InvalidData,name:`InvalidDataError`}),AccountNotFoundError:{type:x.InvalidDataError,message:S.AccountNotFoundMessage,code:S.AccountNotFound,name:`AccountNotFoundError`},InvalidRoleError:e=>({type:x.ValidationError,message:S.InvalidRoleMessage(e),code:S.InvalidRole,name:`InvalidRoleError`}),InvalidRefreshTokenError:{type:x.ValidationError,message:S.InvalidRefreshTokenMessage,code:S.InvalidRefreshToken,name:`InvalidRefreshTokenError`},DuplicateAccountError:{type:x.ValidationError,message:S.DuplicateAccountMessage,code:S.DuplicateAccount,name:`DuplicateAccountError`},InvalidOTPCode:{type:x.ValidationError,message:S.InvalidOtpMessage,code:S.InvalidOtp,name:`InvalidOTPCode`},SchemaInvalidError:{type:x.ValidationError,message:S.SchemaValidationMessage,code:S.SchemaValidationError,name:`SchemaInvalidError`}};function w(e,t){return typeof e==`object`&&!!e&&`name`in e&&e.name===t}function T(e,t){return w(e,t)}function E(e){return`${e?.error?.issues[0].path}: ${e?.error?.issues[0].message}`}function D(e){return{success:!0,value:e}}function O(...e){return{success:!1,errors:e}}async function k({config:e},t){let n=u.safeParse({email:t.email,phoneNumber:t.phoneNumber});if(!n.success)return O({error:C.InvalidDataError(E(n))});let i=await e.dbContractor.findAccountWithCredential({phoneNumber:t.phoneNumber,email:t.email});if(!i||t.usePassword&&!await r.compare(String(t.password),String(i?.passwordHash)))return O({error:C.CredentialMismatchError});let a=await e.dbContractor.createOTP({config:e},{id:i.id,purpose:t.otpPurpose});return D({id:i.id,code:a.code})}async function A({config:e,tokens:t},n){let r=p.safeParse({phoneNumber:n.phoneNumber,email:n.email,code:n.code});if(!r.success)return O({error:C.InvalidDataError(E(r))});let i=await e.dbContractor.findAccountWithCredential({email:n.email,phoneNumber:n.phoneNumber});if(!i)return O({error:C.CredentialMismatchError});if(!(await e.dbContractor.verifyOTP({id:i.id,code:n.code,purpose:a.LoginPurpose})).isValid)return O({error:C.InvalidOTPCode});let o=await t.GenerateTokenPairs({id:i.id,role:i.role});return await e.dbContractor.updateAccountLogin({id:i.id,refreshToken:o.refreshToken}),delete i.passwordHash,delete i.refreshTokens,D({account:i,tokens:o})}async function j({config:e},t){return D({isValid:(await e.dbContractor.verifyOTP({id:t.id,code:t.code,purpose:t.otpPurpose})).isValid})}async function M({config:e},{...t}){let n=b.safeParse(t);if(!n.success)return O({error:C.InvalidDataError(E(n))});let i=await e.dbContractor.findAccountById({id:t.accountId});if(!i||!r.compare(t.oldPassword,String(i.passwordHash)))return O({error:C.CredentialMismatchError});let a=await r.hash(t.newPassword,10);return await e.dbContractor.updateAccount({id:i.id,data:{passwordHash:a}}),D({})}async function N({config:e,tokens:t},{...n}){let i=u.safeParse(n);if(!i.success)return O({error:C.InvalidDataError(E(i))});let a=await e.dbContractor.findAccountWithCredential({email:n.email,phoneNumber:n.phoneNumber});if(!a||!await r.compare(String(n.password),String(a?.passwordHash)))return O({error:C.CredentialMismatchError});let o=await t.GenerateTokenPairs({id:a.id,role:a.role});return await e.dbContractor.updateAccountLogin({id:a.id,refreshToken:o.refreshToken}),delete a.passwordHash,delete a.refreshTokens,D({account:a,tokens:o})}async function P(e){try{return{data:await e,error:null}}catch(e){return{data:null,error:e}}}async function F({config:e,tokens:t},{...n}){let r=y.safeParse(n);if(!r.success)return O({error:C.InvalidDataError(E(r))});let i=await P(t.VerifyRefreshToken(n.refreshToken));return i.error||!i?O({error:C.InvalidRefreshTokenError}):(await e.dbContractor.removeAndAddRefreshToken({id:String(i.data?.id),refreshToken:n.refreshToken}),D({}))}async function I({config:e,tokens:t},{...n}){let r=v.safeParse(n);if(!r.success)return O({error:C.InvalidDataError(E(r))});let i=await P(t.VerifyRefreshToken(n.refreshToken));if(i.error)return O({error:C.InvalidRefreshTokenError});let a=await e.dbContractor.findAccountById({id:String(i.data?.id)});if(!a)return O({error:C.AccountNotFoundError});if(!a?.refreshTokens?.includes(n.refreshToken))return O({error:C.InvalidRefreshTokenError});let o=await t.GenerateTokenPairs({id:a.id,role:a.role});return await e.dbContractor.removeAndAddRefreshToken({id:a.id,refreshToken:n.refreshToken,newRefreshToken:o.refreshToken}),delete a.refreshTokens,delete a.passwordHash,D({account:a,tokens:o})}async function L({config:e,tokens:t},{...n}){let i=_.safeParse(n);if(!i.success)return O({error:C.InvalidDataError(E(i))});if(!e.roles?.includes(n.role))return O({error:C.InvalidRoleError(e.roles)});if(await e.dbContractor.findAccountWithCredential({email:n.email,phoneNumber:n.phoneNumber}))return O({error:C.DuplicateAccountError});let a=await r.hash(String(n.password),10),o=await e.dbContractor.createAccount({data:{email:n.email,phoneNumber:n.phoneNumber,passwordHash:a,role:n.role,lastLogin:new Date}}),s=await t.GenerateTokenPairs({id:o.id,role:o.role});return await e.dbContractor.updateAccountLogin({id:o.id,refreshToken:s.refreshToken}),D({account:o,tokens:s})}async function R({...e}){return i.sign(e.payload,e.config.jwtConfig.accessTokenSecret,{expiresIn:e.config.jwtConfig?.accessTokenLifeSpan??`15m`})}async function z({...e}){return i.sign(e.payload,e.config.jwtConfig.refreshTokenSecret,{expiresIn:e.config.jwtConfig?.refreshTokenLifeSpan??`30d`})}async function B({...e}){return{accessToken:i.sign(e.payload,e.config.jwtConfig.accessTokenSecret,{expiresIn:e.config.jwtConfig?.accessTokenLifeSpan??`15m`}),refreshToken:i.sign(e.payload,e.config.jwtConfig.refreshTokenSecret,{expiresIn:e.config.jwtConfig?.refreshTokenLifeSpan??`30d`})}}async function V({...e}){let t=i.verify(e.token,e.config.jwtConfig.refreshTokenSecret);return t instanceof String?null:t}async function H({...e}){let t=i.verify(e.token,e.config.jwtConfig.accessTokenSecret);return t instanceof String?null:t}const U=e.custom(()=>!0,{message:`Invalid dbContractor: must implement Database Contract interface`}),W=e.custom(()=>!0,{message:`Invalid routeContractor: must implement RoutesContract interface`}),G=e.custom(),K=e.object({dbContractor:U,routeContractor:W,roles:e.array(e.string()).min(1),jwtConfig:e.object({refreshTokenSecret:e.string(),accessTokenSecret:e.string(),accessTokenLifeSpan:G.optional(),refreshTokenLifeSpan:G.optional()}),otpConfig:e.object({expiresIn:e.number().optional(),length:e.number().min(4).max(8).optional()})});var q=class{#config;constructor(e){if(!K.safeParse(e).success)throw Error(`❌ Failed to initiate CAuth. You provided an invalid config!`);this.#config=e}get RoleType(){return null}Guard=e=>this.#config.routeContractor.Guard({config:this.#config,tokens:this.Tokens,roles:e});Routes={Register:()=>this.#config.routeContractor.Register({config:this.#config,tokens:this.Tokens}),Login:()=>this.#config.routeContractor.Login({config:this.#config,tokens:this.Tokens}),Logout:()=>this.#config.routeContractor.Logout({config:this.#config,tokens:this.Tokens}),Refresh:()=>this.#config.routeContractor.Refresh({config:this.#config,tokens:this.Tokens}),ChangePassword:e=>this.#config.routeContractor.ChangePassword({config:this.#config,tokens:this.Tokens,userId:e})};FN={Login:({...e})=>N({config:this.#config,tokens:this.Tokens},e),Register:({...e})=>L({config:this.#config,tokens:this.Tokens},e),Logout:({...e})=>F({config:this.#config,tokens:this.Tokens},e),Refresh:({...e})=>I({config:this.#config,tokens:this.Tokens},e),ChangePassword:({...e})=>M({config:this.#config,tokens:this.Tokens},e),RequestOTPCode:({...e})=>k({config:this.#config,tokens:this.Tokens},e),LoginWithOTP:e=>A({config:this.#config,tokens:this.Tokens},e),VerifyOTP:e=>j({config:this.#config,tokens:this.Tokens},e)};Tokens={GenerateRefreshToken:e=>z({payload:e,config:this.#config}),GenerateAccessToken:e=>R({payload:e,config:this.#config}),GenerateTokenPairs:e=>B({payload:e,config:this.#config}),VerifyRefreshToken:e=>V({token:e,config:this.#config}),VerifyAccessToken:e=>H({token:e,config:this.#config})}};function J(e){return new q(e)}export{J as CAuth,C as CAuthErrors,T as is,w as isCAuthError};
|