@draftlab/auth 0.0.3 → 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.
Files changed (74) hide show
  1. package/dist/allow.d.ts +58 -1
  2. package/dist/allow.js +61 -2
  3. package/dist/client.d.ts +2 -3
  4. package/dist/client.js +2 -2
  5. package/dist/core.d.ts +128 -8
  6. package/dist/core.js +496 -12
  7. package/dist/error.d.ts +242 -1
  8. package/dist/error.js +235 -1
  9. package/dist/index.d.ts +1 -8
  10. package/dist/index.js +1 -12
  11. package/dist/keys.d.ts +1 -1
  12. package/dist/keys.js +138 -3
  13. package/dist/pkce.js +160 -1
  14. package/dist/provider/code.d.ts +211 -3
  15. package/dist/provider/code.js +1 -1
  16. package/dist/provider/facebook.d.ts +2 -3
  17. package/dist/provider/facebook.js +1 -5
  18. package/dist/provider/github.d.ts +2 -3
  19. package/dist/provider/github.js +1 -5
  20. package/dist/provider/google.d.ts +2 -3
  21. package/dist/provider/google.js +1 -5
  22. package/dist/provider/oauth2.d.ts +175 -3
  23. package/dist/provider/oauth2.js +153 -5
  24. package/dist/provider/password.d.ts +384 -3
  25. package/dist/provider/password.js +4 -4
  26. package/dist/provider/provider.d.ts +226 -2
  27. package/dist/random.js +85 -1
  28. package/dist/storage/memory.d.ts +1 -1
  29. package/dist/storage/memory.js +1 -1
  30. package/dist/storage/storage.d.ts +161 -1
  31. package/dist/storage/storage.js +60 -1
  32. package/dist/storage/turso.d.ts +1 -1
  33. package/dist/storage/turso.js +1 -1
  34. package/dist/storage/unstorage.d.ts +1 -1
  35. package/dist/storage/unstorage.js +1 -1
  36. package/dist/subject.d.ts +61 -2
  37. package/dist/themes/theme.d.ts +208 -1
  38. package/dist/themes/theme.js +118 -1
  39. package/dist/ui/base.js +420 -2
  40. package/dist/ui/code.d.ts +1 -3
  41. package/dist/ui/code.js +3 -4
  42. package/dist/ui/form.js +59 -1
  43. package/dist/ui/icon.js +190 -1
  44. package/dist/ui/password.d.ts +1 -3
  45. package/dist/ui/password.js +2 -3
  46. package/dist/ui/select.js +278 -4
  47. package/dist/util.d.ts +71 -1
  48. package/dist/util.js +106 -1
  49. package/package.json +2 -2
  50. package/dist/allow-CixonwTW.d.ts +0 -59
  51. package/dist/allow-DX5cehSc.js +0 -63
  52. package/dist/base-DRutbxgL.js +0 -422
  53. package/dist/code-DJxdFR7p.d.ts +0 -212
  54. package/dist/core-BZHEAefX.d.ts +0 -129
  55. package/dist/core-CDM5o4rs.js +0 -498
  56. package/dist/error-CWAdNAzm.d.ts +0 -243
  57. package/dist/error-DgAKK7b2.js +0 -237
  58. package/dist/form-6XKM_cOk.js +0 -61
  59. package/dist/icon-Ci5uqGB_.js +0 -192
  60. package/dist/keys-EEfxEGfO.js +0 -140
  61. package/dist/oauth2-B7-6Z7Lc.js +0 -155
  62. package/dist/oauth2-CXHukHf2.d.ts +0 -176
  63. package/dist/password-C4KLmO0O.d.ts +0 -385
  64. package/dist/pkce-276Za_rZ.js +0 -162
  65. package/dist/provider-tndlqCzp.d.ts +0 -227
  66. package/dist/random-SXMYlaVr.js +0 -87
  67. package/dist/select-BjySLL8I.js +0 -280
  68. package/dist/storage-BEaqEPNQ.js +0 -62
  69. package/dist/storage-CxKerLlc.d.ts +0 -162
  70. package/dist/subject-DMIMVtaT.d.ts +0 -62
  71. package/dist/theme-C9by7VXf.d.ts +0 -209
  72. package/dist/theme-CswaLtbW.js +0 -120
  73. package/dist/util-CSdHUFOo.js +0 -108
  74. package/dist/util-DbSKG1Xm.d.ts +0 -72
package/dist/pkce.js CHANGED
@@ -1,3 +1,162 @@
1
- import { generatePKCE, validatePKCE } from "./pkce-276Za_rZ.js";
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 };
@@ -1,4 +1,212 @@
1
- import "../storage-CxKerLlc.js";
2
- import "../provider-tndlqCzp.js";
3
- import { CodeProvider, CodeProviderConfig, CodeProviderError, CodeProviderOptions, CodeProviderState, CodeUserData } from "../code-DJxdFR7p.js";
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 };
@@ -1,4 +1,4 @@
1
- import { generateUnbiasedDigits, timingSafeCompare } from "../random-SXMYlaVr.js";
1
+ import { generateUnbiasedDigits, timingSafeCompare } from "../random.js";
2
2
 
3
3
  //#region src/provider/code.ts
4
4
  /**
@@ -1,6 +1,5 @@
1
- import "../storage-CxKerLlc.js";
2
- import { Provider } from "../provider-tndlqCzp.js";
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 "../util-CSdHUFOo.js";
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 "../storage-CxKerLlc.js";
2
- import { Provider } from "../provider-tndlqCzp.js";
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
 
@@ -1,8 +1,4 @@
1
- import "../util-CSdHUFOo.js";
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 "../storage-CxKerLlc.js";
2
- import { Provider } from "../provider-tndlqCzp.js";
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
 
@@ -1,8 +1,4 @@
1
- import "../util-CSdHUFOo.js";
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 "../storage-CxKerLlc.js";
2
- import "../provider-tndlqCzp.js";
3
- import { Oauth2Config, Oauth2Provider, Oauth2Token, Oauth2UserData, Oauth2WrappedConfig } from "../oauth2-CXHukHf2.js";
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 };