@draftlab/auth 0.0.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/adapters/node.d.ts +18 -0
- package/dist/adapters/node.js +71 -0
- package/dist/allow-CixonwTW.d.ts +59 -0
- package/dist/allow-DX5cehSc.js +63 -0
- package/dist/allow.d.ts +2 -0
- package/dist/allow.js +4 -0
- package/dist/base-DRutbxgL.js +422 -0
- package/dist/client.d.ts +413 -0
- package/dist/client.js +209 -0
- package/dist/code-l_uvMR1j.d.ts +212 -0
- package/dist/core-8WTqfnb4.d.ts +129 -0
- package/dist/core-CncE5rPg.js +498 -0
- package/dist/core.d.ts +9 -0
- package/dist/core.js +14 -0
- package/dist/error-CWAdNAzm.d.ts +243 -0
- package/dist/error-DgAKK7b2.js +237 -0
- package/dist/error.d.ts +2 -0
- package/dist/error.js +3 -0
- package/dist/form-6XKM_cOk.js +61 -0
- package/dist/icon-Ci5uqGB_.js +192 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +14 -0
- package/dist/keys-EEfxEGfO.js +140 -0
- package/dist/keys.d.ts +67 -0
- package/dist/keys.js +5 -0
- package/dist/oauth2-B7-6Z7Lc.js +155 -0
- package/dist/oauth2-DtKwtl8p.d.ts +176 -0
- package/dist/password-Cm0dRMwa.d.ts +385 -0
- package/dist/pkce-276Za_rZ.js +162 -0
- package/dist/pkce.d.ts +72 -0
- package/dist/pkce.js +3 -0
- package/dist/provider/code.d.ts +4 -0
- package/dist/provider/code.js +145 -0
- package/dist/provider/facebook.d.ts +137 -0
- package/dist/provider/facebook.js +85 -0
- package/dist/provider/github.d.ts +141 -0
- package/dist/provider/github.js +88 -0
- package/dist/provider/google.d.ts +113 -0
- package/dist/provider/google.js +62 -0
- package/dist/provider/oauth2.d.ts +4 -0
- package/dist/provider/oauth2.js +7 -0
- package/dist/provider/password.d.ts +4 -0
- package/dist/provider/password.js +366 -0
- package/dist/provider/provider.d.ts +3 -0
- package/dist/provider/provider.js +44 -0
- package/dist/provider-CwWMG-1l.d.ts +227 -0
- package/dist/random-SXMYlaVr.js +87 -0
- package/dist/random.d.ts +66 -0
- package/dist/random.js +3 -0
- package/dist/select-BjySLL8I.js +280 -0
- package/dist/storage/memory.d.ts +82 -0
- package/dist/storage/memory.js +127 -0
- package/dist/storage/storage.d.ts +2 -0
- package/dist/storage/storage.js +3 -0
- package/dist/storage/turso.d.ts +31 -0
- package/dist/storage/turso.js +117 -0
- package/dist/storage/unstorage.d.ts +38 -0
- package/dist/storage/unstorage.js +97 -0
- package/dist/storage-BEaqEPNQ.js +62 -0
- package/dist/storage-CxKerLlc.d.ts +162 -0
- package/dist/subject-DiQdRWGt.d.ts +62 -0
- package/dist/subject.d.ts +3 -0
- package/dist/subject.js +36 -0
- package/dist/theme-C9by7VXf.d.ts +209 -0
- package/dist/theme-CswaLtbW.js +120 -0
- package/dist/themes/theme.d.ts +2 -0
- package/dist/themes/theme.js +3 -0
- package/dist/types.d.ts +94 -0
- package/dist/types.js +0 -0
- package/dist/ui/base.d.ts +43 -0
- package/dist/ui/base.js +4 -0
- package/dist/ui/code.d.ts +158 -0
- package/dist/ui/code.js +197 -0
- package/dist/ui/form.d.ts +31 -0
- package/dist/ui/form.js +3 -0
- package/dist/ui/icon.d.ts +98 -0
- package/dist/ui/icon.js +3 -0
- package/dist/ui/password.d.ts +54 -0
- package/dist/ui/password.js +300 -0
- package/dist/ui/select.d.ts +233 -0
- package/dist/ui/select.js +6 -0
- package/dist/util-CSdHUFOo.js +108 -0
- package/dist/util-ChlgVqPN.d.ts +72 -0
- package/dist/util.d.ts +2 -0
- package/dist/util.js +3 -0
- package/package.json +63 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { Provider } from "./provider-CwWMG-1l.js";
|
|
2
|
+
|
|
3
|
+
//#region src/provider/code.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for the PIN code authentication provider.
|
|
7
|
+
*
|
|
8
|
+
* @template Claims - Type of claims collected during authentication (email, phone, etc.)
|
|
9
|
+
*/
|
|
10
|
+
interface CodeProviderConfig<Claims extends Record<string, string> = Record<string, string>> {
|
|
11
|
+
/**
|
|
12
|
+
* The length of the generated PIN code.
|
|
13
|
+
* Common values are 4, 6, or 8 digits.
|
|
14
|
+
*
|
|
15
|
+
* @default 6
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* {
|
|
20
|
+
* length: 4 // 4-digit PIN for easier entry
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
readonly length?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Request handler for rendering the authentication UI.
|
|
27
|
+
* Handles both the initial claim collection and PIN code entry screens.
|
|
28
|
+
*
|
|
29
|
+
* @param req - The HTTP request object
|
|
30
|
+
* @param state - Current authentication state (start or code verification)
|
|
31
|
+
* @param form - Form data from POST requests (if any)
|
|
32
|
+
* @param error - Authentication error to display (if any)
|
|
33
|
+
* @returns Promise resolving to the authentication page response
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* request: async (req, state, form, error) => {
|
|
38
|
+
* if (state.type === 'start') {
|
|
39
|
+
* return new Response(renderClaimForm(form, error))
|
|
40
|
+
* } else {
|
|
41
|
+
* return new Response(renderCodeForm(state.claims.email, error))
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
request: (req: Request, state: CodeProviderState, form?: FormData, error?: CodeProviderError) => Promise<Response>;
|
|
47
|
+
/**
|
|
48
|
+
* Callback for sending PIN codes to users via their preferred method.
|
|
49
|
+
* Should handle delivery via email, SMS, or other communication channels.
|
|
50
|
+
*
|
|
51
|
+
* @param claims - User claims containing contact information
|
|
52
|
+
* @param code - The generated PIN code to send
|
|
53
|
+
* @returns Promise resolving to undefined on success, or error object on failure
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* sendCode: async (claims, code) => {
|
|
58
|
+
* try {
|
|
59
|
+
* if (claims.email) {
|
|
60
|
+
* await emailService.send({
|
|
61
|
+
* to: claims.email,
|
|
62
|
+
* subject: 'Your verification code',
|
|
63
|
+
* text: `Your PIN code is: ${code}`
|
|
64
|
+
* })
|
|
65
|
+
* } else if (claims.phone) {
|
|
66
|
+
* await smsService.send(claims.phone, `PIN: ${code}`)
|
|
67
|
+
* } else {
|
|
68
|
+
* return {
|
|
69
|
+
* type: "invalid_claim",
|
|
70
|
+
* key: "contact",
|
|
71
|
+
* value: "No email or phone provided"
|
|
72
|
+
* }
|
|
73
|
+
* }
|
|
74
|
+
* } catch (error) {
|
|
75
|
+
* return {
|
|
76
|
+
* type: "invalid_claim",
|
|
77
|
+
* key: "delivery",
|
|
78
|
+
* value: "Failed to send code"
|
|
79
|
+
* }
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
sendCode: (claims: Claims, code: string) => Promise<CodeProviderError | undefined>;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Authentication flow states for the PIN code provider.
|
|
88
|
+
* The provider transitions between these states during authentication.
|
|
89
|
+
*/
|
|
90
|
+
type CodeProviderState = {
|
|
91
|
+
/** Initial state: user enters their claims (email, phone, etc.) */
|
|
92
|
+
readonly type: "start";
|
|
93
|
+
} | {
|
|
94
|
+
/** Code verification state: user enters the PIN code */
|
|
95
|
+
readonly type: "code";
|
|
96
|
+
/** Whether this is a code resend request */
|
|
97
|
+
readonly resend?: boolean;
|
|
98
|
+
/** The generated PIN code for verification */
|
|
99
|
+
readonly code: string;
|
|
100
|
+
/** User claims collected during the start phase */
|
|
101
|
+
readonly claims: Record<string, string>;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Possible errors during PIN code authentication.
|
|
105
|
+
*/
|
|
106
|
+
type CodeProviderError = {
|
|
107
|
+
/** The entered PIN code is incorrect */
|
|
108
|
+
readonly type: "invalid_code";
|
|
109
|
+
} | {
|
|
110
|
+
/** A user claim is invalid or missing */
|
|
111
|
+
readonly type: "invalid_claim";
|
|
112
|
+
/** The claim field that failed validation */
|
|
113
|
+
readonly key: string;
|
|
114
|
+
/** The invalid value or error description */
|
|
115
|
+
readonly value: string;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* User data returned by successful PIN code authentication.
|
|
119
|
+
*
|
|
120
|
+
* @template Claims - Type of claims collected during authentication
|
|
121
|
+
*/
|
|
122
|
+
interface CodeUserData<Claims extends Record<string, string> = Record<string, string>> {
|
|
123
|
+
/** The verified claims collected during authentication */
|
|
124
|
+
readonly claims: Claims;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Creates a PIN code authentication provider.
|
|
128
|
+
* Implements a flexible claim-based authentication flow with PIN verification.
|
|
129
|
+
*
|
|
130
|
+
* @template Claims - Type of claims to collect (email, phone, username, etc.)
|
|
131
|
+
* @param config - PIN code provider configuration
|
|
132
|
+
* @returns Provider instance implementing PIN code authentication
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* // Email-based PIN authentication
|
|
137
|
+
* const emailCodeProvider = CodeProvider<{ email: string }>({
|
|
138
|
+
* length: 6,
|
|
139
|
+
* request: async (req, state, form, error) => {
|
|
140
|
+
* if (state.type === 'start') {
|
|
141
|
+
* return new Response(renderEmailForm(form?.get('email'), error))
|
|
142
|
+
* } else {
|
|
143
|
+
* return new Response(renderPinForm(state.claims.email, error, state.resend))
|
|
144
|
+
* }
|
|
145
|
+
* },
|
|
146
|
+
* sendCode: async (claims, code) => {
|
|
147
|
+
* if (!claims.email || !isValidEmail(claims.email)) {
|
|
148
|
+
* return {
|
|
149
|
+
* type: "invalid_claim",
|
|
150
|
+
* key: "email",
|
|
151
|
+
* value: "Invalid email address"
|
|
152
|
+
* }
|
|
153
|
+
* }
|
|
154
|
+
*
|
|
155
|
+
* await emailService.send(claims.email, `Your verification code: ${code}`)
|
|
156
|
+
* }
|
|
157
|
+
* })
|
|
158
|
+
*
|
|
159
|
+
* // Multi-channel PIN authentication (email or phone)
|
|
160
|
+
* const flexibleCodeProvider = CodeProvider<{ email?: string; phone?: string }>({
|
|
161
|
+
* length: 4,
|
|
162
|
+
* request: async (req, state, form, error) => {
|
|
163
|
+
* if (state.type === 'start') {
|
|
164
|
+
* return new Response(renderContactForm(form, error))
|
|
165
|
+
* } else {
|
|
166
|
+
* const contact = state.claims.email || state.claims.phone
|
|
167
|
+
* return new Response(renderPinForm(contact, error))
|
|
168
|
+
* }
|
|
169
|
+
* },
|
|
170
|
+
* sendCode: async (claims, code) => {
|
|
171
|
+
* if (claims.email) {
|
|
172
|
+
* await emailService.send(claims.email, `PIN: ${code}`)
|
|
173
|
+
* } else if (claims.phone) {
|
|
174
|
+
* await smsService.send(claims.phone, `PIN: ${code}`)
|
|
175
|
+
* } else {
|
|
176
|
+
* return {
|
|
177
|
+
* type: "invalid_claim",
|
|
178
|
+
* key: "contact",
|
|
179
|
+
* value: "Provide either email or phone number"
|
|
180
|
+
* }
|
|
181
|
+
* }
|
|
182
|
+
* }
|
|
183
|
+
* })
|
|
184
|
+
*
|
|
185
|
+
* // Usage in issuer
|
|
186
|
+
* export default issuer({
|
|
187
|
+
* providers: {
|
|
188
|
+
* email: emailCodeProvider,
|
|
189
|
+
* flexible: flexibleCodeProvider
|
|
190
|
+
* },
|
|
191
|
+
* success: async (ctx, value) => {
|
|
192
|
+
* if (value.provider === "code") {
|
|
193
|
+
* const email = value.claims.email
|
|
194
|
+
* const phone = value.claims.phone
|
|
195
|
+
*
|
|
196
|
+
* // Look up or create user based on verified claims
|
|
197
|
+
* const userId = await findOrCreateUser({ email, phone })
|
|
198
|
+
*
|
|
199
|
+
* return ctx.subject("user", { userId, email, phone })
|
|
200
|
+
* }
|
|
201
|
+
* }
|
|
202
|
+
* })
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
declare const CodeProvider: <Claims extends Record<string, string> = Record<string, string>>(config: CodeProviderConfig<Claims>) => Provider<CodeUserData<Claims>>;
|
|
206
|
+
/**
|
|
207
|
+
* Type helper for CodeProvider configuration options.
|
|
208
|
+
* @internal
|
|
209
|
+
*/
|
|
210
|
+
type CodeProviderOptions = Parameters<typeof CodeProvider>[0];
|
|
211
|
+
//#endregion
|
|
212
|
+
export { CodeProvider, CodeProviderConfig, CodeProviderError, CodeProviderOptions, CodeProviderState, CodeUserData };
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { AllowCheckInput } from "./allow-CixonwTW.js";
|
|
2
|
+
import { UnknownStateError } from "./error-CWAdNAzm.js";
|
|
3
|
+
import { Prettify } from "./util-ChlgVqPN.js";
|
|
4
|
+
import { SubjectPayload, SubjectSchema } from "./subject-DiQdRWGt.js";
|
|
5
|
+
import { StorageAdapter } from "./storage-CxKerLlc.js";
|
|
6
|
+
import { Provider } from "./provider-CwWMG-1l.js";
|
|
7
|
+
import { Theme } from "./theme-C9by7VXf.js";
|
|
8
|
+
import { Router } from "@draftlab/router";
|
|
9
|
+
|
|
10
|
+
//#region src/core.d.ts
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Sets the subject payload in the JWT token and returns the response.
|
|
14
|
+
*/
|
|
15
|
+
interface OnSuccessResponder<T extends {
|
|
16
|
+
type: string;
|
|
17
|
+
properties: unknown;
|
|
18
|
+
}> {
|
|
19
|
+
subject<Type extends T["type"]>(type: Type, properties: Extract<T, {
|
|
20
|
+
type: Type;
|
|
21
|
+
}>["properties"], opts?: {
|
|
22
|
+
ttl?: {
|
|
23
|
+
access?: number;
|
|
24
|
+
refresh?: number;
|
|
25
|
+
};
|
|
26
|
+
subject?: string;
|
|
27
|
+
}): Promise<Response>;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Authorization state for OAuth 2.0 flows.
|
|
31
|
+
*/
|
|
32
|
+
interface AuthorizationState {
|
|
33
|
+
/** OAuth redirect URI */
|
|
34
|
+
redirect_uri: string;
|
|
35
|
+
/** OAuth response type */
|
|
36
|
+
response_type: string;
|
|
37
|
+
/** OAuth state parameter for CSRF protection */
|
|
38
|
+
state: string;
|
|
39
|
+
/** OAuth client identifier */
|
|
40
|
+
client_id: string;
|
|
41
|
+
/** OAuth audience parameter */
|
|
42
|
+
audience: string;
|
|
43
|
+
/** PKCE challenge data for code verification */
|
|
44
|
+
pkce?: {
|
|
45
|
+
challenge: string;
|
|
46
|
+
method: "S256";
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Main issuer input configuration interface.
|
|
51
|
+
*/
|
|
52
|
+
interface IssuerInput<Providers extends Record<string, Provider<unknown>>, Subjects extends SubjectSchema, Result = { [Key in keyof Providers]: Prettify<{
|
|
53
|
+
provider: Key;
|
|
54
|
+
} & (Providers[Key] extends Provider<infer T> ? T : Record<string, unknown>)> }[keyof Providers]> {
|
|
55
|
+
/** The storage adapter for persisting tokens and sessions */
|
|
56
|
+
storage: StorageAdapter;
|
|
57
|
+
/** Auth providers configuration */
|
|
58
|
+
providers: Providers;
|
|
59
|
+
/** Subject schemas for token validation */
|
|
60
|
+
subjects: Subjects;
|
|
61
|
+
/** Base path for embedded scenarios */
|
|
62
|
+
basePath?: string;
|
|
63
|
+
/** Success callback for completed authentication */
|
|
64
|
+
success(response: OnSuccessResponder<SubjectPayload<Subjects>>, input: Result, req: Request, clientID: string): Promise<Response>;
|
|
65
|
+
/** Theme configuration for UI */
|
|
66
|
+
theme?: Theme;
|
|
67
|
+
/** TTL configuration for tokens and sessions */
|
|
68
|
+
ttl?: {
|
|
69
|
+
access?: number;
|
|
70
|
+
refresh?: number;
|
|
71
|
+
reuse?: number;
|
|
72
|
+
retention?: number;
|
|
73
|
+
};
|
|
74
|
+
/** Provider selection UI function */
|
|
75
|
+
select?(providers: Record<string, string>, req: Request): Promise<Response>;
|
|
76
|
+
/** Optional start callback */
|
|
77
|
+
start?(req: Request): Promise<void>;
|
|
78
|
+
/** Error handling callback */
|
|
79
|
+
error?(error: UnknownStateError, req: Request): Promise<Response>;
|
|
80
|
+
/** Client authorization check function */
|
|
81
|
+
allow?(input: AllowCheckInput, req: Request): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Refresh callback for updating user claims.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* refresh: async (payload, req) => {
|
|
88
|
+
* const user = await getUserBySubject(payload.subject)
|
|
89
|
+
* if (!user || !user.active) {
|
|
90
|
+
* return undefined // Revoke the token
|
|
91
|
+
* }
|
|
92
|
+
*
|
|
93
|
+
* return {
|
|
94
|
+
* type: payload.type,
|
|
95
|
+
* properties: {
|
|
96
|
+
* userID: user.id,
|
|
97
|
+
* role: user.role,
|
|
98
|
+
* permissions: user.permissions,
|
|
99
|
+
* lastLogin: new Date().toISOString()
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
refresh?(payload: {
|
|
106
|
+
type: SubjectPayload<Subjects>["type"];
|
|
107
|
+
properties: SubjectPayload<Subjects>["properties"];
|
|
108
|
+
subject: string;
|
|
109
|
+
clientID: string;
|
|
110
|
+
scopes?: string[];
|
|
111
|
+
}, req: Request): Promise<{
|
|
112
|
+
type: SubjectPayload<Subjects>["type"];
|
|
113
|
+
properties: SubjectPayload<Subjects>["properties"];
|
|
114
|
+
subject?: string;
|
|
115
|
+
scopes?: string[];
|
|
116
|
+
} | undefined>;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Create an Draft Auth server, a Router app that handles OAuth 2.0 flows.
|
|
120
|
+
*/
|
|
121
|
+
declare const issuer: <Providers extends Record<string, Provider<unknown>>, Subjects extends SubjectSchema, Result = { [key in keyof Providers]: {
|
|
122
|
+
provider: key;
|
|
123
|
+
} & (Providers[key] extends Provider<infer T> ? T : Record<string, unknown>) }[keyof Providers]>(input: IssuerInput<Providers, Subjects, Result>) => Router<{
|
|
124
|
+
Variables: {
|
|
125
|
+
authorization: AuthorizationState;
|
|
126
|
+
};
|
|
127
|
+
}>;
|
|
128
|
+
//#endregion
|
|
129
|
+
export { AuthorizationState, OnSuccessResponder, issuer };
|