@draftlab/auth 0.0.2 → 0.0.4
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/allow.d.ts +58 -1
- package/dist/allow.js +61 -2
- package/dist/client.d.ts +2 -3
- package/dist/client.js +2 -2
- package/dist/core.d.ts +128 -8
- package/dist/core.js +496 -12
- package/dist/error.d.ts +242 -1
- package/dist/error.js +235 -1
- package/dist/index.d.ts +1 -8
- package/dist/index.js +1 -12
- package/dist/keys.d.ts +1 -1
- package/dist/keys.js +138 -3
- package/dist/pkce.js +160 -1
- package/dist/provider/code.d.ts +211 -3
- package/dist/provider/code.js +1 -1
- package/dist/provider/facebook.d.ts +2 -3
- package/dist/provider/facebook.js +1 -5
- package/dist/provider/github.d.ts +2 -3
- package/dist/provider/github.js +1 -5
- package/dist/provider/google.d.ts +2 -3
- package/dist/provider/google.js +1 -5
- package/dist/provider/oauth2.d.ts +175 -3
- package/dist/provider/oauth2.js +153 -5
- package/dist/provider/password.d.ts +384 -3
- package/dist/provider/password.js +4 -4
- package/dist/provider/provider.d.ts +226 -2
- package/dist/random.js +85 -1
- package/dist/storage/memory.d.ts +1 -1
- package/dist/storage/memory.js +1 -1
- package/dist/storage/storage.d.ts +161 -1
- package/dist/storage/storage.js +60 -1
- package/dist/storage/turso.d.ts +1 -1
- package/dist/storage/turso.js +1 -1
- package/dist/storage/unstorage.d.ts +1 -1
- package/dist/storage/unstorage.js +1 -1
- package/dist/subject.d.ts +61 -2
- package/dist/themes/theme.d.ts +208 -1
- package/dist/themes/theme.js +118 -1
- package/dist/ui/base.js +420 -2
- package/dist/ui/code.d.ts +1 -3
- package/dist/ui/code.js +3 -4
- package/dist/ui/form.js +59 -1
- package/dist/ui/icon.js +190 -1
- package/dist/ui/password.d.ts +1 -3
- package/dist/ui/password.js +2 -3
- package/dist/ui/select.js +278 -4
- package/dist/util.d.ts +71 -1
- package/dist/util.js +106 -1
- package/package.json +2 -4
- package/dist/allow-CixonwTW.d.ts +0 -59
- package/dist/allow-DX5cehSc.js +0 -63
- package/dist/base-DRutbxgL.js +0 -422
- package/dist/code-DJxdFR7p.d.ts +0 -212
- package/dist/core-BZHEAefX.d.ts +0 -129
- package/dist/core-CDM5o4rs.js +0 -498
- package/dist/error-CWAdNAzm.d.ts +0 -243
- package/dist/error-DgAKK7b2.js +0 -237
- package/dist/form-6XKM_cOk.js +0 -61
- package/dist/icon-Ci5uqGB_.js +0 -192
- package/dist/keys-EEfxEGfO.js +0 -140
- package/dist/oauth2-B7-6Z7Lc.js +0 -155
- package/dist/oauth2-CXHukHf2.d.ts +0 -176
- package/dist/password-C4KLmO0O.d.ts +0 -385
- package/dist/pkce-276Za_rZ.js +0 -162
- package/dist/provider-tndlqCzp.d.ts +0 -227
- package/dist/random-SXMYlaVr.js +0 -87
- package/dist/select-BjySLL8I.js +0 -280
- package/dist/storage-BEaqEPNQ.js +0 -62
- package/dist/storage-CxKerLlc.d.ts +0 -162
- package/dist/subject-DMIMVtaT.d.ts +0 -62
- package/dist/theme-C9by7VXf.d.ts +0 -209
- package/dist/theme-CswaLtbW.js +0 -120
- package/dist/util-CSdHUFOo.js +0 -108
- package/dist/util-DbSKG1Xm.d.ts +0 -72
package/dist/pkce.js
CHANGED
|
@@ -1,3 +1,162 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { base64url } from "jose";
|
|
2
2
|
|
|
3
|
+
//#region src/pkce.ts
|
|
4
|
+
/**
|
|
5
|
+
* Performs a timing-safe comparison of two strings to prevent timing attacks.
|
|
6
|
+
* This implementation is platform-agnostic, uses a constant-time algorithm,
|
|
7
|
+
* and correctly handles all Unicode characters by operating on their UTF-8 byte representation.
|
|
8
|
+
* It always takes a time proportional to the length of the expected string,
|
|
9
|
+
* regardless of where the strings differ, making it safe for comparing sensitive values.
|
|
10
|
+
*
|
|
11
|
+
* @param a - The first string to compare (often the expected, secret value).
|
|
12
|
+
* @param b - The second string to compare (often the user-provided value).
|
|
13
|
+
* @returns True if the strings are identical, false otherwise.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* // Safe for comparing sensitive values like PKCE verifiers or tokens
|
|
18
|
+
* const isValid = await timingSafeCompare(receivedVerifier, expectedChallenge);
|
|
19
|
+
*
|
|
20
|
+
* // Safe for password hash verification
|
|
21
|
+
* const isValidPassword = timingSafeCompare(hashedInput, storedHash);
|
|
22
|
+
*
|
|
23
|
+
* // Returns false for different types or lengths without leaking timing info
|
|
24
|
+
* timingSafeCompare("abc", 123 as any); // false
|
|
25
|
+
* timingSafeCompare("abc", "abcd"); // false
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
const timingSafeCompare = (a, b) => {
|
|
29
|
+
if (typeof a !== "string" || typeof b !== "string") return false;
|
|
30
|
+
const encoder = new TextEncoder();
|
|
31
|
+
const aBytes = encoder.encode(a);
|
|
32
|
+
const bBytes = encoder.encode(b);
|
|
33
|
+
let diff = aBytes.length ^ bBytes.length;
|
|
34
|
+
for (const [i, aByte] of aBytes.entries()) diff |= aByte ^ (bBytes[i] ?? 0);
|
|
35
|
+
return diff === 0;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Generates a cryptographically secure code verifier for PKCE.
|
|
39
|
+
* The verifier is a URL-safe base64-encoded string of random bytes.
|
|
40
|
+
*
|
|
41
|
+
* @param length - Length of the random buffer in bytes
|
|
42
|
+
* @returns Base64url-encoded verifier string
|
|
43
|
+
*/
|
|
44
|
+
const generateVerifier = (length) => {
|
|
45
|
+
const buffer = new Uint8Array(length);
|
|
46
|
+
crypto.getRandomValues(buffer);
|
|
47
|
+
return base64url.encode(buffer);
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Generates a code challenge from a verifier using the specified method.
|
|
51
|
+
* For 'S256', applies SHA-256 hash then base64url encoding.
|
|
52
|
+
* For 'plain', returns the verifier unchanged (not recommended for production).
|
|
53
|
+
*
|
|
54
|
+
* @param verifier - The code verifier string
|
|
55
|
+
* @param method - Challenge generation method
|
|
56
|
+
* @returns Promise resolving to the code challenge string
|
|
57
|
+
*/
|
|
58
|
+
const generateChallenge = async (verifier, method) => {
|
|
59
|
+
if (method === "plain") return verifier;
|
|
60
|
+
const encoder = new TextEncoder();
|
|
61
|
+
const data = encoder.encode(verifier);
|
|
62
|
+
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
63
|
+
return base64url.encode(new Uint8Array(hash));
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Generates a complete PKCE challenge for OAuth authorization requests.
|
|
67
|
+
* Creates a cryptographically secure verifier and corresponding S256 challenge.
|
|
68
|
+
* Validates that the generated verifier meets standard requirements (43-128 characters).
|
|
69
|
+
*
|
|
70
|
+
* @param length - Length of the random buffer in bytes (32-96 range to generate 43-128 character verifier)
|
|
71
|
+
* @returns Promise resolving to PKCE challenge data
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* const pkce = await generatePKCE()
|
|
76
|
+
*
|
|
77
|
+
* // Use challenge in authorization URL
|
|
78
|
+
* authUrl.searchParams.set('code_challenge', pkce.challenge)
|
|
79
|
+
* authUrl.searchParams.set('code_challenge_method', pkce.method)
|
|
80
|
+
*
|
|
81
|
+
* // Store verifier for token exchange
|
|
82
|
+
* sessionStorage.setItem('code_verifier', pkce.verifier)
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @throws {RangeError} If length is outside valid range or generated verifier doesn't meet requirements
|
|
86
|
+
*/
|
|
87
|
+
const generatePKCE = async (length = 48) => {
|
|
88
|
+
if (!Number.isInteger(length) || length < 32 || length > 96) throw new RangeError("Random buffer length must be between 32 and 96 bytes (generates 43-128 character verifier)");
|
|
89
|
+
const verifier = generateVerifier(length);
|
|
90
|
+
if (verifier.length < 43 || verifier.length > 128) throw new Error("Generated verifier does not meet requirements");
|
|
91
|
+
if (!/^[A-Za-z0-9_-]+$/.test(verifier)) throw new Error("Generated verifier is not valid base64url format");
|
|
92
|
+
const challenge = await generateChallenge(verifier, "S256");
|
|
93
|
+
return {
|
|
94
|
+
verifier,
|
|
95
|
+
challenge,
|
|
96
|
+
method: "S256"
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Validates a PKCE code verifier against a previously generated challenge.
|
|
101
|
+
* Uses timing-safe comparison and timing normalization to prevent timing attacks.
|
|
102
|
+
* All validation paths take the same computational time regardless of input validity,
|
|
103
|
+
* making it resistant to timing-based side-channel attacks.
|
|
104
|
+
*
|
|
105
|
+
* @param verifier - The code verifier received from the client
|
|
106
|
+
* @param challenge - The code challenge stored during authorization
|
|
107
|
+
* @param method - The challenge method used during generation
|
|
108
|
+
* @returns Promise resolving to true if verifier matches challenge
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* // During token exchange
|
|
113
|
+
* const isValid = await validatePKCE(
|
|
114
|
+
* receivedVerifier,
|
|
115
|
+
* storedChallenge,
|
|
116
|
+
* 'S256'
|
|
117
|
+
* )
|
|
118
|
+
*
|
|
119
|
+
* if (!isValid) {
|
|
120
|
+
* throw new Error('Invalid PKCE verifier')
|
|
121
|
+
* }
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
const validatePKCE = async (verifier, challenge, method = "S256") => {
|
|
125
|
+
const MIN_PROCESSING_TIME = 50;
|
|
126
|
+
const RANDOM_JITTER_MAX = 20;
|
|
127
|
+
const startTime = performance.now();
|
|
128
|
+
let isValid = false;
|
|
129
|
+
let hasEarlyFailure = false;
|
|
130
|
+
const normalizedVerifier = String(verifier || "");
|
|
131
|
+
const normalizedChallenge = String(challenge || "");
|
|
132
|
+
const validations = [
|
|
133
|
+
typeof verifier === "string" && typeof challenge === "string" && verifier && challenge,
|
|
134
|
+
normalizedVerifier.length >= 43 && normalizedVerifier.length <= 128,
|
|
135
|
+
normalizedChallenge.length >= 43 && normalizedChallenge.length <= 128,
|
|
136
|
+
/^[A-Za-z0-9_-]+$/.test(normalizedVerifier),
|
|
137
|
+
/^[A-Za-z0-9_-]+$/.test(normalizedChallenge)
|
|
138
|
+
];
|
|
139
|
+
hasEarlyFailure = !validations.every(Boolean);
|
|
140
|
+
const verifierToUse = hasEarlyFailure ? "dummyverifier_".repeat(6) : normalizedVerifier;
|
|
141
|
+
try {
|
|
142
|
+
const generatedChallenge = await generateChallenge(verifierToUse, method);
|
|
143
|
+
const challengeToCompare = hasEarlyFailure ? "dummychallenge_".repeat(6) : normalizedChallenge;
|
|
144
|
+
const comparisonResult = timingSafeCompare(generatedChallenge, challengeToCompare);
|
|
145
|
+
isValid = !hasEarlyFailure && comparisonResult;
|
|
146
|
+
} catch {
|
|
147
|
+
isValid = false;
|
|
148
|
+
}
|
|
149
|
+
const elapsed = performance.now() - startTime;
|
|
150
|
+
const remainingTime = Math.max(0, MIN_PROCESSING_TIME - elapsed);
|
|
151
|
+
if (remainingTime > 0 || elapsed < MIN_PROCESSING_TIME) {
|
|
152
|
+
const jitterArray = new Uint32Array(1);
|
|
153
|
+
crypto.getRandomValues(jitterArray);
|
|
154
|
+
const jitter = (jitterArray[0] ?? 0) / 4294967295 * RANDOM_JITTER_MAX;
|
|
155
|
+
const totalDelay = Math.max(remainingTime, MIN_PROCESSING_TIME - elapsed) + jitter;
|
|
156
|
+
await new Promise((resolve) => setTimeout(resolve, totalDelay));
|
|
157
|
+
}
|
|
158
|
+
return isValid;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
//#endregion
|
|
3
162
|
export { generatePKCE, validatePKCE };
|
package/dist/provider/code.d.ts
CHANGED
|
@@ -1,4 +1,212 @@
|
|
|
1
|
-
import "
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Provider } from "./provider.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
|
|
4
212
|
export { CodeProvider, CodeProviderConfig, CodeProviderError, CodeProviderOptions, CodeProviderState, CodeUserData };
|
package/dist/provider/code.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import {
|
|
3
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "../oauth2-CXHukHf2.js";
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
|
|
4
3
|
|
|
5
4
|
//#region src/provider/facebook.d.ts
|
|
6
5
|
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import "../error-DgAKK7b2.js";
|
|
3
|
-
import "../pkce-276Za_rZ.js";
|
|
4
|
-
import "../random-SXMYlaVr.js";
|
|
5
|
-
import { Oauth2Provider } from "../oauth2-B7-6Z7Lc.js";
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.js";
|
|
6
2
|
|
|
7
3
|
//#region src/provider/facebook.ts
|
|
8
4
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import {
|
|
3
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "../oauth2-CXHukHf2.js";
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
|
|
4
3
|
|
|
5
4
|
//#region src/provider/github.d.ts
|
|
6
5
|
|
package/dist/provider/github.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import "../error-DgAKK7b2.js";
|
|
3
|
-
import "../pkce-276Za_rZ.js";
|
|
4
|
-
import "../random-SXMYlaVr.js";
|
|
5
|
-
import { Oauth2Provider } from "../oauth2-B7-6Z7Lc.js";
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.js";
|
|
6
2
|
|
|
7
3
|
//#region src/provider/github.ts
|
|
8
4
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import {
|
|
3
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "../oauth2-CXHukHf2.js";
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
|
|
4
3
|
|
|
5
4
|
//#region src/provider/google.d.ts
|
|
6
5
|
|
package/dist/provider/google.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import "
|
|
2
|
-
import "../error-DgAKK7b2.js";
|
|
3
|
-
import "../pkce-276Za_rZ.js";
|
|
4
|
-
import "../random-SXMYlaVr.js";
|
|
5
|
-
import { Oauth2Provider } from "../oauth2-B7-6Z7Lc.js";
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.js";
|
|
6
2
|
|
|
7
3
|
//#region src/provider/google.ts
|
|
8
4
|
/**
|
|
@@ -1,4 +1,176 @@
|
|
|
1
|
-
import "
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
|
|
3
|
+
//#region src/provider/oauth2.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for the OAuth 2.0 provider.
|
|
7
|
+
*/
|
|
8
|
+
interface Oauth2Config {
|
|
9
|
+
/**
|
|
10
|
+
* Provider type identifier for internal use.
|
|
11
|
+
* @internal
|
|
12
|
+
* @default "oauth2"
|
|
13
|
+
*/
|
|
14
|
+
readonly type?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The client ID registered with the OAuth 2.0 provider.
|
|
17
|
+
* This public identifier is used in authorization requests.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* {
|
|
22
|
+
* clientID: "github-app-12345"
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
readonly clientID: string;
|
|
27
|
+
/**
|
|
28
|
+
* The client secret for authenticating with the OAuth 2.0 provider.
|
|
29
|
+
* This private credential must be kept secure and not exposed to clients.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* {
|
|
34
|
+
* clientSecret: process.env.OAUTH_CLIENT_SECRET
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
readonly clientSecret: string;
|
|
39
|
+
/**
|
|
40
|
+
* OAuth 2.0 endpoint URLs for the authorization and token flows.
|
|
41
|
+
*/
|
|
42
|
+
readonly endpoint: {
|
|
43
|
+
/**
|
|
44
|
+
* The authorization endpoint where users are redirected for authentication.
|
|
45
|
+
*
|
|
46
|
+
* @example "https://github.com/login/oauth/authorize"
|
|
47
|
+
*/
|
|
48
|
+
readonly authorization: string;
|
|
49
|
+
/**
|
|
50
|
+
* The token endpoint for exchanging authorization codes for access tokens.
|
|
51
|
+
*
|
|
52
|
+
* @example "https://github.com/login/oauth/access_token"
|
|
53
|
+
*/
|
|
54
|
+
readonly token: string;
|
|
55
|
+
/**
|
|
56
|
+
* Optional JWKS endpoint for verifying ID tokens.
|
|
57
|
+
* Required only if the provider returns ID tokens that need verification.
|
|
58
|
+
*
|
|
59
|
+
* @example "https://provider.com/.well-known/jwks.json"
|
|
60
|
+
*/
|
|
61
|
+
readonly jwks?: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* OAuth 2.0 scopes to request during authorization.
|
|
65
|
+
* Scopes define the level of access being requested.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* {
|
|
70
|
+
* scopes: ["user:email", "read:user", "repo"]
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
readonly scopes: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Whether to use PKCE (Proof Key for Code Exchange) for enhanced security.
|
|
77
|
+
* Recommended for public clients and required by some providers.
|
|
78
|
+
*
|
|
79
|
+
* @default false
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* {
|
|
84
|
+
* pkce: true // Required for Twitter/X, recommended for mobile apps
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
readonly pkce?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Additional query parameters to include in the authorization request.
|
|
91
|
+
* Useful for provider-specific parameters or customizing the auth flow.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* {
|
|
96
|
+
* query: {
|
|
97
|
+
* access_type: "offline", // Request refresh token
|
|
98
|
+
* prompt: "consent", // Force consent screen
|
|
99
|
+
* hd: "mycompany.com" // Google Workspace domain
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
readonly query?: Record<string, string>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* OAuth 2.0 configuration without endpoint-specific fields.
|
|
108
|
+
* Used internally for provider wrapping.
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
type Oauth2WrappedConfig = Omit<Oauth2Config, "endpoint" | "name">;
|
|
112
|
+
/**
|
|
113
|
+
* OAuth 2.0 token response containing access tokens and metadata.
|
|
114
|
+
* Provides a structured interface for token data with lazy property access.
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
interface Oauth2Token {
|
|
118
|
+
/** Access token for making authenticated API requests */
|
|
119
|
+
readonly access: string;
|
|
120
|
+
/** Refresh token for obtaining new access tokens (if provided) */
|
|
121
|
+
readonly refresh: string;
|
|
122
|
+
/** Token expiration time in seconds (if provided) */
|
|
123
|
+
readonly expiry: number;
|
|
124
|
+
/** Raw token response from the provider */
|
|
125
|
+
readonly raw: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* User data returned by successful OAuth 2.0 authentication.
|
|
129
|
+
*/
|
|
130
|
+
interface Oauth2UserData {
|
|
131
|
+
/** Token set containing access token, refresh token, and metadata */
|
|
132
|
+
readonly tokenset: Oauth2Token;
|
|
133
|
+
/** Client ID used for this authentication */
|
|
134
|
+
readonly clientID: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Creates an OAuth 2.0 authentication provider.
|
|
138
|
+
* Implements the Authorization Code Grant flow with optional PKCE support.
|
|
139
|
+
*
|
|
140
|
+
* @param config - OAuth 2.0 provider configuration
|
|
141
|
+
* @returns Provider instance implementing OAuth 2.0 authentication
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* // GitHub provider with basic configuration
|
|
146
|
+
* const githubProvider = Oauth2Provider({
|
|
147
|
+
* clientID: process.env.GITHUB_CLIENT_ID,
|
|
148
|
+
* clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
149
|
+
* endpoint: {
|
|
150
|
+
* authorization: "https://github.com/login/oauth/authorize",
|
|
151
|
+
* token: "https://github.com/login/oauth/access_token"
|
|
152
|
+
* },
|
|
153
|
+
* scopes: ["user:email", "read:user"]
|
|
154
|
+
* })
|
|
155
|
+
*
|
|
156
|
+
* // Provider with PKCE and custom parameters
|
|
157
|
+
* const customProvider = Oauth2Provider({
|
|
158
|
+
* clientID: "my-client-id",
|
|
159
|
+
* clientSecret: "my-client-secret",
|
|
160
|
+
* endpoint: {
|
|
161
|
+
* authorization: "https://provider.com/oauth/authorize",
|
|
162
|
+
* token: "https://provider.com/oauth/token",
|
|
163
|
+
* jwks: "https://provider.com/.well-known/jwks.json"
|
|
164
|
+
* },
|
|
165
|
+
* scopes: ["read", "write"],
|
|
166
|
+
* pkce: true,
|
|
167
|
+
* query: {
|
|
168
|
+
* prompt: "consent",
|
|
169
|
+
* access_type: "offline"
|
|
170
|
+
* }
|
|
171
|
+
* })
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
declare const Oauth2Provider: (config: Oauth2Config) => Provider<Oauth2UserData>;
|
|
175
|
+
//#endregion
|
|
4
176
|
export { Oauth2Config, Oauth2Provider, Oauth2Token, Oauth2UserData, Oauth2WrappedConfig };
|