@draftlab/auth 0.15.0 → 0.16.0
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/esm/allow.js +26 -0
- package/dist/esm/client.js +254 -0
- package/dist/esm/core.js +597 -0
- package/dist/esm/css.d.js +0 -0
- package/dist/esm/error.js +88 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/keys.js +126 -0
- package/dist/esm/mutex.js +53 -0
- package/dist/esm/pkce.js +87 -0
- package/dist/esm/provider/apple.js +15 -0
- package/dist/esm/provider/code.js +62 -0
- package/dist/esm/provider/discord.js +15 -0
- package/dist/esm/provider/facebook.js +15 -0
- package/dist/esm/provider/github.js +15 -0
- package/dist/esm/provider/gitlab.js +15 -0
- package/dist/esm/provider/google.js +16 -0
- package/dist/esm/provider/linkedin.js +15 -0
- package/dist/esm/provider/magiclink.js +83 -0
- package/dist/esm/provider/microsoft.js +15 -0
- package/dist/esm/provider/oauth2.js +130 -0
- package/dist/esm/provider/password.js +331 -0
- package/dist/esm/provider/provider.js +18 -0
- package/dist/esm/provider/reddit.js +15 -0
- package/dist/esm/provider/slack.js +15 -0
- package/dist/esm/provider/spotify.js +15 -0
- package/dist/esm/provider/twitch.js +15 -0
- package/dist/esm/provider/vercel.js +17 -0
- package/dist/esm/random.js +40 -0
- package/dist/esm/revocation.js +27 -0
- package/dist/esm/storage/memory.js +110 -0
- package/dist/esm/storage/storage.js +56 -0
- package/dist/esm/storage/turso.js +93 -0
- package/dist/esm/storage/unstorage.js +78 -0
- package/dist/esm/subject.js +7 -0
- package/dist/esm/themes/theme.js +115 -0
- package/dist/esm/toolkit/client.js +119 -0
- package/dist/esm/toolkit/index.js +25 -0
- package/dist/esm/toolkit/providers/facebook.js +11 -0
- package/dist/esm/toolkit/providers/github.js +11 -0
- package/dist/esm/toolkit/providers/google.js +11 -0
- package/dist/esm/toolkit/providers/strategy.js +0 -0
- package/dist/esm/toolkit/storage.js +81 -0
- package/dist/esm/toolkit/utils.js +18 -0
- package/dist/esm/types.js +0 -0
- package/dist/esm/ui/base.js +478 -0
- package/dist/esm/ui/code.js +186 -0
- package/dist/esm/ui/form.js +46 -0
- package/dist/esm/ui/icon.js +242 -0
- package/dist/esm/ui/magiclink.js +158 -0
- package/dist/esm/ui/password.js +435 -0
- package/dist/esm/ui/select.js +102 -0
- package/dist/esm/util.js +59 -0
- package/dist/{allow.d.mts → types/allow.d.ts} +9 -11
- package/dist/types/allow.d.ts.map +1 -0
- package/dist/types/client.d.ts +462 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/core.d.ts +113 -0
- package/dist/types/core.d.ts.map +1 -0
- package/dist/{error.d.mts → types/error.d.ts} +95 -97
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/{keys.d.mts → types/keys.d.ts} +20 -24
- package/dist/types/keys.d.ts.map +1 -0
- package/dist/types/mutex.d.ts +42 -0
- package/dist/types/mutex.d.ts.map +1 -0
- package/dist/{pkce.d.mts → types/pkce.d.ts} +10 -11
- package/dist/types/pkce.d.ts.map +1 -0
- package/dist/types/provider/apple.d.ts +197 -0
- package/dist/types/provider/apple.d.ts.map +1 -0
- package/dist/types/provider/code.d.ts +288 -0
- package/dist/types/provider/code.d.ts.map +1 -0
- package/dist/types/provider/discord.d.ts +206 -0
- package/dist/types/provider/discord.d.ts.map +1 -0
- package/dist/types/provider/facebook.d.ts +200 -0
- package/dist/types/provider/facebook.d.ts.map +1 -0
- package/dist/types/provider/github.d.ts +220 -0
- package/dist/types/provider/github.d.ts.map +1 -0
- package/dist/types/provider/gitlab.d.ts +180 -0
- package/dist/types/provider/gitlab.d.ts.map +1 -0
- package/dist/types/provider/google.d.ts +158 -0
- package/dist/types/provider/google.d.ts.map +1 -0
- package/dist/types/provider/linkedin.d.ts +190 -0
- package/dist/types/provider/linkedin.d.ts.map +1 -0
- package/dist/types/provider/magiclink.d.ts +141 -0
- package/dist/types/provider/magiclink.d.ts.map +1 -0
- package/dist/types/provider/microsoft.d.ts +247 -0
- package/dist/types/provider/microsoft.d.ts.map +1 -0
- package/dist/types/provider/oauth2.d.ts +229 -0
- package/dist/types/provider/oauth2.d.ts.map +1 -0
- package/dist/types/provider/password.d.ts +408 -0
- package/dist/types/provider/password.d.ts.map +1 -0
- package/dist/types/provider/provider.d.ts +226 -0
- package/dist/types/provider/provider.d.ts.map +1 -0
- package/dist/types/provider/reddit.d.ts +159 -0
- package/dist/types/provider/reddit.d.ts.map +1 -0
- package/dist/types/provider/slack.d.ts +171 -0
- package/dist/types/provider/slack.d.ts.map +1 -0
- package/dist/types/provider/spotify.d.ts +168 -0
- package/dist/types/provider/spotify.d.ts.map +1 -0
- package/dist/types/provider/twitch.d.ts +163 -0
- package/dist/types/provider/twitch.d.ts.map +1 -0
- package/dist/types/provider/vercel.d.ts +294 -0
- package/dist/types/provider/vercel.d.ts.map +1 -0
- package/dist/{random.d.mts → types/random.d.ts} +4 -6
- package/dist/types/random.d.ts.map +1 -0
- package/dist/types/revocation.d.ts +76 -0
- package/dist/types/revocation.d.ts.map +1 -0
- package/dist/{storage/memory.d.mts → types/storage/memory.d.ts} +17 -21
- package/dist/types/storage/memory.d.ts.map +1 -0
- package/dist/types/storage/storage.d.ts +177 -0
- package/dist/types/storage/storage.d.ts.map +1 -0
- package/dist/{storage/turso.d.mts → types/storage/turso.d.ts} +4 -8
- package/dist/types/storage/turso.d.ts.map +1 -0
- package/dist/{storage/unstorage.d.mts → types/storage/unstorage.d.ts} +12 -11
- package/dist/types/storage/unstorage.d.ts.map +1 -0
- package/dist/types/subject.d.ts +115 -0
- package/dist/types/subject.d.ts.map +1 -0
- package/dist/types/themes/theme.d.ts +207 -0
- package/dist/types/themes/theme.d.ts.map +1 -0
- package/dist/types/toolkit/client.d.ts +235 -0
- package/dist/types/toolkit/client.d.ts.map +1 -0
- package/dist/types/toolkit/index.d.ts +45 -0
- package/dist/types/toolkit/index.d.ts.map +1 -0
- package/dist/types/toolkit/providers/facebook.d.ts +8 -0
- package/dist/types/toolkit/providers/facebook.d.ts.map +1 -0
- package/dist/types/toolkit/providers/github.d.ts +8 -0
- package/dist/types/toolkit/providers/github.d.ts.map +1 -0
- package/dist/types/toolkit/providers/google.d.ts +8 -0
- package/dist/types/toolkit/providers/google.d.ts.map +1 -0
- package/dist/types/toolkit/providers/strategy.d.ts +38 -0
- package/dist/types/toolkit/providers/strategy.d.ts.map +1 -0
- package/dist/{toolkit/storage.d.mts → types/toolkit/storage.d.ts} +37 -39
- package/dist/types/toolkit/storage.d.ts.map +1 -0
- package/dist/{toolkit/utils.d.mts → types/toolkit/utils.d.ts} +2 -4
- package/dist/types/toolkit/utils.d.ts.map +1 -0
- package/dist/types/types.d.ts +92 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/ui/base.d.ts +18 -0
- package/dist/types/ui/base.d.ts.map +1 -0
- package/dist/types/ui/code.d.ts +43 -0
- package/dist/types/ui/code.d.ts.map +1 -0
- package/dist/types/ui/form.d.ts +24 -0
- package/dist/types/ui/form.d.ts.map +1 -0
- package/dist/types/ui/icon.d.ts +60 -0
- package/dist/types/ui/icon.d.ts.map +1 -0
- package/dist/types/ui/magiclink.d.ts +41 -0
- package/dist/types/ui/magiclink.d.ts.map +1 -0
- package/dist/types/ui/password.d.ts +43 -0
- package/dist/types/ui/password.d.ts.map +1 -0
- package/dist/types/ui/select.d.ts +33 -0
- package/dist/types/ui/select.d.ts.map +1 -0
- package/dist/{util.d.mts → types/util.d.ts} +11 -13
- package/dist/types/util.d.ts.map +1 -0
- package/package.json +10 -16
- package/dist/adapters/node.d.mts +0 -18
- package/dist/adapters/node.mjs +0 -69
- package/dist/allow.mjs +0 -63
- package/dist/client.d.mts +0 -456
- package/dist/client.mjs +0 -283
- package/dist/core.d.mts +0 -110
- package/dist/core.mjs +0 -595
- package/dist/error.mjs +0 -237
- package/dist/index.d.mts +0 -2
- package/dist/index.mjs +0 -3
- package/dist/keys.mjs +0 -146
- package/dist/mutex.d.mts +0 -44
- package/dist/mutex.mjs +0 -110
- package/dist/pkce.mjs +0 -157
- package/dist/provider/apple.d.mts +0 -111
- package/dist/provider/apple.mjs +0 -164
- package/dist/provider/code.d.mts +0 -228
- package/dist/provider/code.mjs +0 -246
- package/dist/provider/discord.d.mts +0 -146
- package/dist/provider/discord.mjs +0 -156
- package/dist/provider/facebook.d.mts +0 -142
- package/dist/provider/facebook.mjs +0 -150
- package/dist/provider/github.d.mts +0 -140
- package/dist/provider/github.mjs +0 -169
- package/dist/provider/gitlab.d.mts +0 -106
- package/dist/provider/gitlab.mjs +0 -147
- package/dist/provider/google.d.mts +0 -112
- package/dist/provider/google.mjs +0 -109
- package/dist/provider/linkedin.d.mts +0 -132
- package/dist/provider/linkedin.mjs +0 -142
- package/dist/provider/magiclink.d.mts +0 -89
- package/dist/provider/magiclink.mjs +0 -143
- package/dist/provider/microsoft.d.mts +0 -178
- package/dist/provider/microsoft.mjs +0 -177
- package/dist/provider/oauth2.d.mts +0 -176
- package/dist/provider/oauth2.mjs +0 -222
- package/dist/provider/passkey.d.mts +0 -104
- package/dist/provider/passkey.mjs +0 -320
- package/dist/provider/password.d.mts +0 -412
- package/dist/provider/password.mjs +0 -363
- package/dist/provider/provider.d.mts +0 -227
- package/dist/provider/provider.mjs +0 -44
- package/dist/provider/reddit.d.mts +0 -107
- package/dist/provider/reddit.mjs +0 -127
- package/dist/provider/slack.d.mts +0 -114
- package/dist/provider/slack.mjs +0 -138
- package/dist/provider/spotify.d.mts +0 -113
- package/dist/provider/spotify.mjs +0 -135
- package/dist/provider/totp.d.mts +0 -112
- package/dist/provider/totp.mjs +0 -191
- package/dist/provider/twitch.d.mts +0 -108
- package/dist/provider/twitch.mjs +0 -131
- package/dist/provider/vercel.d.mts +0 -177
- package/dist/provider/vercel.mjs +0 -230
- package/dist/random.mjs +0 -86
- package/dist/revocation.d.mts +0 -55
- package/dist/revocation.mjs +0 -63
- package/dist/router/context.d.mts +0 -21
- package/dist/router/context.mjs +0 -193
- package/dist/router/cookies.d.mts +0 -8
- package/dist/router/cookies.mjs +0 -13
- package/dist/router/index.d.mts +0 -21
- package/dist/router/index.mjs +0 -107
- package/dist/router/matcher.d.mts +0 -15
- package/dist/router/matcher.mjs +0 -76
- package/dist/router/middleware/cors.d.mts +0 -15
- package/dist/router/middleware/cors.mjs +0 -114
- package/dist/router/safe-request.d.mts +0 -52
- package/dist/router/safe-request.mjs +0 -160
- package/dist/router/types.d.mts +0 -67
- package/dist/router/types.mjs +0 -1
- package/dist/router/variables.d.mts +0 -12
- package/dist/router/variables.mjs +0 -20
- package/dist/storage/memory.mjs +0 -125
- package/dist/storage/storage.d.mts +0 -179
- package/dist/storage/storage.mjs +0 -104
- package/dist/storage/turso.mjs +0 -117
- package/dist/storage/unstorage.mjs +0 -103
- package/dist/subject.d.mts +0 -62
- package/dist/subject.mjs +0 -36
- package/dist/themes/theme.d.mts +0 -209
- package/dist/themes/theme.mjs +0 -120
- package/dist/toolkit/client.d.mts +0 -169
- package/dist/toolkit/client.mjs +0 -209
- package/dist/toolkit/index.d.mts +0 -9
- package/dist/toolkit/index.mjs +0 -9
- package/dist/toolkit/providers/facebook.d.mts +0 -12
- package/dist/toolkit/providers/facebook.mjs +0 -16
- package/dist/toolkit/providers/github.d.mts +0 -12
- package/dist/toolkit/providers/github.mjs +0 -16
- package/dist/toolkit/providers/google.d.mts +0 -12
- package/dist/toolkit/providers/google.mjs +0 -20
- package/dist/toolkit/providers/strategy.d.mts +0 -40
- package/dist/toolkit/providers/strategy.mjs +0 -1
- package/dist/toolkit/storage.mjs +0 -157
- package/dist/toolkit/utils.mjs +0 -30
- package/dist/types.d.mts +0 -94
- package/dist/types.mjs +0 -1
- package/dist/ui/base.d.mts +0 -30
- package/dist/ui/base.mjs +0 -407
- package/dist/ui/code.d.mts +0 -43
- package/dist/ui/code.mjs +0 -173
- package/dist/ui/form.d.mts +0 -32
- package/dist/ui/form.mjs +0 -49
- package/dist/ui/icon.d.mts +0 -58
- package/dist/ui/icon.mjs +0 -247
- package/dist/ui/magiclink.d.mts +0 -41
- package/dist/ui/magiclink.mjs +0 -152
- package/dist/ui/passkey.d.mts +0 -27
- package/dist/ui/passkey.mjs +0 -323
- package/dist/ui/password.d.mts +0 -42
- package/dist/ui/password.mjs +0 -402
- package/dist/ui/select.d.mts +0 -34
- package/dist/ui/select.mjs +0 -98
- package/dist/ui/totp.d.mts +0 -34
- package/dist/ui/totp.mjs +0 -270
- package/dist/util.mjs +0 -128
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PIN code authentication provider for Draft Auth.
|
|
3
|
+
* Supports flexible claim-based authentication via email, phone, or custom identifiers.
|
|
4
|
+
*
|
|
5
|
+
* ## Quick Setup
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { CodeUI } from "@draftlab/auth/ui/code"
|
|
9
|
+
* import { CodeProvider } from "@draftlab/auth/provider/code"
|
|
10
|
+
*
|
|
11
|
+
* export default issuer({
|
|
12
|
+
* providers: {
|
|
13
|
+
* code: CodeProvider(
|
|
14
|
+
* CodeUI({
|
|
15
|
+
* copy: {
|
|
16
|
+
* code_info: "We'll send a PIN code to your email"
|
|
17
|
+
* },
|
|
18
|
+
* sendCode: async (claims, code) => {
|
|
19
|
+
* try {
|
|
20
|
+
* await sendEmail(claims.email, `Your code: ${code}`)
|
|
21
|
+
* } catch {
|
|
22
|
+
* return { type: "invalid_claim", key: "delivery", value: "Failed to send code" }
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* })
|
|
26
|
+
* )
|
|
27
|
+
* }
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* ## Custom Configuration
|
|
32
|
+
*
|
|
33
|
+
* ```ts
|
|
34
|
+
* const customCodeProvider = CodeProvider({
|
|
35
|
+
* length: 4, // 4-digit PIN instead of default 6
|
|
36
|
+
* request: async (req, state, form, error) => {
|
|
37
|
+
* return new Response(renderCodePage(state, form, error))
|
|
38
|
+
* },
|
|
39
|
+
* sendCode: async (claims, code) => {
|
|
40
|
+
* try {
|
|
41
|
+
* if (claims.email) {
|
|
42
|
+
* await emailService.send(claims.email, code)
|
|
43
|
+
* } else if (claims.phone) {
|
|
44
|
+
* await smsService.send(claims.phone, code)
|
|
45
|
+
* } else {
|
|
46
|
+
* return { type: "invalid_claim", key: "email", value: "Email or phone number is required" }
|
|
47
|
+
* }
|
|
48
|
+
* } catch {
|
|
49
|
+
* return { type: "invalid_claim", key: "delivery", value: "Failed to send code" }
|
|
50
|
+
* }
|
|
51
|
+
* }
|
|
52
|
+
* })
|
|
53
|
+
* ```
|
|
54
|
+
*
|
|
55
|
+
* ## Features
|
|
56
|
+
*
|
|
57
|
+
* - **Flexible claims**: Support any claim type (email, phone, username, etc.)
|
|
58
|
+
* - **Configurable PIN length**: 4-6 digit codes typically
|
|
59
|
+
* - **Resend functionality**: Built-in code resend capability
|
|
60
|
+
* - **Custom UI**: Full control over the authentication interface
|
|
61
|
+
* - **Error handling**: Comprehensive error states for different failure modes
|
|
62
|
+
*
|
|
63
|
+
* ## Flow States
|
|
64
|
+
*
|
|
65
|
+
* The provider manages a two-step authentication flow:
|
|
66
|
+
*
|
|
67
|
+
* 1. **Start**: User enters their claim (email, phone, etc.)
|
|
68
|
+
* 2. **Code**: User enters the PIN code sent to their claim
|
|
69
|
+
*
|
|
70
|
+
* ## User Data
|
|
71
|
+
*
|
|
72
|
+
* ```ts
|
|
73
|
+
* success: async (ctx, value) => {
|
|
74
|
+
* if (value.provider === "code") {
|
|
75
|
+
* // User's email: value.claims.email
|
|
76
|
+
* // User's phone (if provided): value.claims.phone
|
|
77
|
+
* // Any other claims collected during the flow
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @packageDocumentation
|
|
83
|
+
*/
|
|
84
|
+
import type { Provider } from "./provider";
|
|
85
|
+
/**
|
|
86
|
+
* Configuration options for the PIN code authentication provider.
|
|
87
|
+
*
|
|
88
|
+
* @template Claims - Type of claims collected during authentication (email, phone, etc.)
|
|
89
|
+
*/
|
|
90
|
+
export interface CodeProviderConfig<Claims extends Record<string, string> = Record<string, string>> {
|
|
91
|
+
/**
|
|
92
|
+
* The length of the generated PIN code.
|
|
93
|
+
* Common values are 4, 6, or 8 digits.
|
|
94
|
+
*
|
|
95
|
+
* @default 6
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* {
|
|
100
|
+
* length: 4 // 4-digit PIN for easier entry
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
readonly length?: number;
|
|
105
|
+
/**
|
|
106
|
+
* Request handler for rendering the authentication UI.
|
|
107
|
+
* Handles both the initial claim collection and PIN code entry screens.
|
|
108
|
+
*
|
|
109
|
+
* @param req - The HTTP request object
|
|
110
|
+
* @param state - Current authentication state (start or code verification)
|
|
111
|
+
* @param form - Form data from POST requests (if any)
|
|
112
|
+
* @param error - Authentication error to display (if any)
|
|
113
|
+
* @returns Promise resolving to the authentication page response
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* request: async (req, state, form, error) => {
|
|
118
|
+
* if (state.type === 'start') {
|
|
119
|
+
* return new Response(renderClaimForm(form, error))
|
|
120
|
+
* } else {
|
|
121
|
+
* return new Response(renderCodeForm(state.claims.email, error))
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
request: (req: Request, state: CodeProviderState, form?: FormData, error?: CodeProviderError) => Promise<Response>;
|
|
127
|
+
/**
|
|
128
|
+
* Callback for sending PIN codes to users via their preferred method.
|
|
129
|
+
* Should handle delivery via email, SMS, or other communication channels.
|
|
130
|
+
*
|
|
131
|
+
* @param claims - User claims containing contact information
|
|
132
|
+
* @param code - The generated PIN code to send
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```ts
|
|
136
|
+
* sendCode: async (claims, code) => {
|
|
137
|
+
* await emailService.send({
|
|
138
|
+
* to: claims.email,
|
|
139
|
+
* subject: 'Your verification code',
|
|
140
|
+
* text: `Your PIN code is: ${code}`
|
|
141
|
+
* })
|
|
142
|
+
* }
|
|
143
|
+
* ```
|
|
144
|
+
*/
|
|
145
|
+
sendCode: (claims: Claims, code: string) => Promise<void>;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Authentication flow states for the PIN code provider.
|
|
149
|
+
* The provider transitions between these states during authentication.
|
|
150
|
+
*/
|
|
151
|
+
export type CodeProviderState = {
|
|
152
|
+
/** Initial state: user enters their claims (email, phone, etc.) */
|
|
153
|
+
readonly type: "start";
|
|
154
|
+
} | {
|
|
155
|
+
/** Code verification state: user enters the PIN code */
|
|
156
|
+
readonly type: "code";
|
|
157
|
+
/** Whether this is a code resend request */
|
|
158
|
+
readonly resend?: boolean;
|
|
159
|
+
/** The generated PIN code for verification */
|
|
160
|
+
readonly code: string;
|
|
161
|
+
/** User claims collected during the start phase */
|
|
162
|
+
readonly claims: Record<string, string>;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Possible errors during PIN code authentication.
|
|
166
|
+
*/
|
|
167
|
+
export type CodeProviderError = {
|
|
168
|
+
/** The entered PIN code is incorrect */
|
|
169
|
+
readonly type: "invalid_code";
|
|
170
|
+
} | {
|
|
171
|
+
/** A user claim is invalid or missing */
|
|
172
|
+
readonly type: "invalid_claim";
|
|
173
|
+
/** The claim field that failed validation */
|
|
174
|
+
readonly key: string;
|
|
175
|
+
/** The invalid value or error description */
|
|
176
|
+
readonly value: string;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* User data returned by successful PIN code authentication.
|
|
180
|
+
*
|
|
181
|
+
* @template Claims - Type of claims collected during authentication
|
|
182
|
+
*/
|
|
183
|
+
export interface CodeUserData<Claims extends Record<string, string> = Record<string, string>> {
|
|
184
|
+
/** The verified claims collected during authentication */
|
|
185
|
+
readonly claims: Claims;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Creates a PIN code authentication provider.
|
|
189
|
+
* Implements a flexible claim-based authentication flow with PIN verification.
|
|
190
|
+
*
|
|
191
|
+
* @template Claims - Type of claims to collect (email, phone, username, etc.)
|
|
192
|
+
* @param config - PIN code provider configuration
|
|
193
|
+
* @returns Provider instance implementing PIN code authentication
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* // Email-based PIN authentication
|
|
198
|
+
* const emailCodeProvider = CodeProvider<{ email: string }>({
|
|
199
|
+
* length: 6,
|
|
200
|
+
* request: async (req, state, form, error) => {
|
|
201
|
+
* if (state.type === 'start') {
|
|
202
|
+
* return new Response(renderEmailForm(form?.get('email'), error))
|
|
203
|
+
* } else {
|
|
204
|
+
* return new Response(renderPinForm(state.claims.email, error, state.resend))
|
|
205
|
+
* }
|
|
206
|
+
* },
|
|
207
|
+
* sendCode: async (claims, code) => {
|
|
208
|
+
* if (!claims.email || !isValidEmail(claims.email)) {
|
|
209
|
+
* return {
|
|
210
|
+
* type: "invalid_claim",
|
|
211
|
+
* key: "email",
|
|
212
|
+
* value: "Invalid email address"
|
|
213
|
+
* }
|
|
214
|
+
* }
|
|
215
|
+
*
|
|
216
|
+
* try {
|
|
217
|
+
* await emailService.send(claims.email, `Your verification code: ${code}`)
|
|
218
|
+
* } catch {
|
|
219
|
+
* return {
|
|
220
|
+
* type: "invalid_claim",
|
|
221
|
+
* key: "delivery",
|
|
222
|
+
* value: "Failed to send code"
|
|
223
|
+
* }
|
|
224
|
+
* }
|
|
225
|
+
* }
|
|
226
|
+
* })
|
|
227
|
+
*
|
|
228
|
+
* // Multi-channel PIN authentication (email or phone)
|
|
229
|
+
* const flexibleCodeProvider = CodeProvider<{ email?: string; phone?: string }>({
|
|
230
|
+
* length: 4,
|
|
231
|
+
* request: async (req, state, form, error) => {
|
|
232
|
+
* if (state.type === 'start') {
|
|
233
|
+
* return new Response(renderContactForm(form, error))
|
|
234
|
+
* } else {
|
|
235
|
+
* const contact = state.claims.email || state.claims.phone
|
|
236
|
+
* return new Response(renderPinForm(contact, error))
|
|
237
|
+
* }
|
|
238
|
+
* },
|
|
239
|
+
* sendCode: async (claims, code) => {
|
|
240
|
+
* try {
|
|
241
|
+
* if (claims.email) {
|
|
242
|
+
* await emailService.send(claims.email, `PIN: ${code}`)
|
|
243
|
+
* } else if (claims.phone) {
|
|
244
|
+
* await smsService.send(claims.phone, `PIN: ${code}`)
|
|
245
|
+
* } else {
|
|
246
|
+
* return {
|
|
247
|
+
* type: "invalid_claim",
|
|
248
|
+
* key: "email",
|
|
249
|
+
* value: "Provide either email or phone number"
|
|
250
|
+
* }
|
|
251
|
+
* }
|
|
252
|
+
* } catch {
|
|
253
|
+
* return {
|
|
254
|
+
* type: "invalid_claim",
|
|
255
|
+
* key: "delivery",
|
|
256
|
+
* value: "Failed to send code"
|
|
257
|
+
* }
|
|
258
|
+
* }
|
|
259
|
+
* }
|
|
260
|
+
* })
|
|
261
|
+
*
|
|
262
|
+
* // Usage in issuer
|
|
263
|
+
* export default issuer({
|
|
264
|
+
* providers: {
|
|
265
|
+
* email: emailCodeProvider,
|
|
266
|
+
* flexible: flexibleCodeProvider
|
|
267
|
+
* },
|
|
268
|
+
* success: async (ctx, value) => {
|
|
269
|
+
* if (value.provider === "code") {
|
|
270
|
+
* const email = value.claims.email
|
|
271
|
+
* const phone = value.claims.phone
|
|
272
|
+
*
|
|
273
|
+
* // Look up or create user based on verified claims
|
|
274
|
+
* const userId = await findOrCreateUser({ email, phone })
|
|
275
|
+
*
|
|
276
|
+
* return ctx.subject("user", { userId, email, phone })
|
|
277
|
+
* }
|
|
278
|
+
* }
|
|
279
|
+
* })
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
export declare const CodeProvider: <Claims extends Record<string, string> = Record<string, string>>(config: CodeProviderConfig<Claims>) => Provider<CodeUserData<Claims>>;
|
|
283
|
+
/**
|
|
284
|
+
* Type helper for CodeProvider configuration options.
|
|
285
|
+
* @internal
|
|
286
|
+
*/
|
|
287
|
+
export type CodeProviderOptions = Parameters<typeof CodeProvider>[0];
|
|
288
|
+
//# sourceMappingURL=code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.d.ts","sourceRoot":"","sources":["../../../src/provider/code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkFG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C;;;;GAIG;AACH,MAAM,WAAW,kBAAkB,CAClC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAE9D;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IAExB;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,EAAE,CACR,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,iBAAiB,EACxB,IAAI,CAAC,EAAE,QAAQ,EACf,KAAK,CAAC,EAAE,iBAAiB,KACrB,OAAO,CAAC,QAAQ,CAAC,CAAA;IAEtB;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACzD;AAED;;;GAGG;AACH,MAAM,MAAM,iBAAiB,GAC1B;IACA,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CACrB,GACD;IACA,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,4CAA4C;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,mDAAmD;IACnD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACtC,CAAA;AAEJ;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAC1B;IACA,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAA;CAC5B,GACD;IACA,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,6CAA6C;IAC7C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACrB,CAAA;AAEJ;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;IAC3F,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8FG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1F,QAAQ,kBAAkB,CAAC,MAAM,CAAC,KAChC,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CA6G/B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discord OAuth 2.0 authentication provider for Draft Auth.
|
|
3
|
+
* Provides access tokens for calling Discord APIs on behalf of users.
|
|
4
|
+
*
|
|
5
|
+
* ## Quick Setup
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { DiscordProvider } from "@draftlab/auth/provider/discord"
|
|
9
|
+
*
|
|
10
|
+
* export default issuer({
|
|
11
|
+
* basePath: "/auth", // Important for callback URL
|
|
12
|
+
* providers: {
|
|
13
|
+
* discord: DiscordProvider({
|
|
14
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
15
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
16
|
+
* scopes: ["identify", "email", "guilds"]
|
|
17
|
+
* })
|
|
18
|
+
* }
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
23
|
+
* - Development: `http://localhost:3000/auth/discord/callback`
|
|
24
|
+
* - Production: `https://yourapp.com/auth/discord/callback`
|
|
25
|
+
*
|
|
26
|
+
* Register this URL in your Discord Developer Portal.
|
|
27
|
+
*
|
|
28
|
+
* ## Common Scopes
|
|
29
|
+
*
|
|
30
|
+
* - `identify` - Access to user's basic account information
|
|
31
|
+
* - `email` - Access to user's email address
|
|
32
|
+
* - `guilds` - Access to user's guilds (servers)
|
|
33
|
+
* - `guilds.join` - Ability to join user to guilds
|
|
34
|
+
* - `gdm.join` - Ability to join user to group DMs
|
|
35
|
+
* - `connections` - Access to user's connections (Steam, YouTube, etc.)
|
|
36
|
+
* - `guilds.members.read` - Read guild member information
|
|
37
|
+
* - `bot` - For bot applications (requires additional setup)
|
|
38
|
+
*
|
|
39
|
+
* ## User Data Access
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* success: async (ctx, value) => {
|
|
43
|
+
* if (value.provider === "discord") {
|
|
44
|
+
* const accessToken = value.tokenset.access
|
|
45
|
+
*
|
|
46
|
+
* // Fetch user information
|
|
47
|
+
* const userResponse = await fetch('https://discord.com/api/users/@me', {
|
|
48
|
+
* headers: { Authorization: `Bearer ${accessToken}` }
|
|
49
|
+
* })
|
|
50
|
+
* const user = await userResponse.json()
|
|
51
|
+
*
|
|
52
|
+
* // Fetch user guilds (requires guilds scope)
|
|
53
|
+
* const guildsResponse = await fetch('https://discord.com/api/users/@me/guilds', {
|
|
54
|
+
* headers: { Authorization: `Bearer ${accessToken}` }
|
|
55
|
+
* })
|
|
56
|
+
* const guilds = await guildsResponse.json()
|
|
57
|
+
*
|
|
58
|
+
* // User info: user.username + user.discriminator
|
|
59
|
+
* // Avatar: `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* @packageDocumentation
|
|
65
|
+
*/
|
|
66
|
+
import { type Oauth2WrappedConfig } from "./oauth2";
|
|
67
|
+
/**
|
|
68
|
+
* Configuration options for Discord OAuth 2.0 provider.
|
|
69
|
+
* Extends the base OAuth 2.0 configuration with Discord-specific documentation.
|
|
70
|
+
*/
|
|
71
|
+
export interface DiscordConfig extends Oauth2WrappedConfig {
|
|
72
|
+
/**
|
|
73
|
+
* Discord OAuth 2.0 client ID from Discord Developer Portal.
|
|
74
|
+
* Found in your Discord application settings.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* {
|
|
79
|
+
* clientID: "1234567890123456789"
|
|
80
|
+
* }
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
readonly clientID: string;
|
|
84
|
+
/**
|
|
85
|
+
* Discord OAuth 2.0 client secret from Discord Developer Portal.
|
|
86
|
+
* Keep this secure and never expose it to client-side code.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* {
|
|
91
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
92
|
+
* }
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
readonly clientSecret: string;
|
|
96
|
+
/**
|
|
97
|
+
* Discord OAuth scopes to request access for.
|
|
98
|
+
* Determines what data and actions your app can access.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* {
|
|
103
|
+
* scopes: [
|
|
104
|
+
* "identify", // Basic user information
|
|
105
|
+
* "email", // Email address
|
|
106
|
+
* "guilds", // User's Discord servers
|
|
107
|
+
* "connections" // Connected accounts (Steam, etc.)
|
|
108
|
+
* ]
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
readonly scopes: string[];
|
|
113
|
+
/**
|
|
114
|
+
* Additional query parameters for Discord OAuth authorization.
|
|
115
|
+
* Useful for Discord-specific options like permissions.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```ts
|
|
119
|
+
* {
|
|
120
|
+
* query: {
|
|
121
|
+
* permissions: "8", // Administrator permission
|
|
122
|
+
* guild_id: "123456789", // Pre-select specific guild
|
|
123
|
+
* disable_guild_select: "true" // Disable guild selection
|
|
124
|
+
* }
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*/
|
|
128
|
+
readonly query?: Record<string, string>;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Creates a Discord OAuth 2.0 authentication provider.
|
|
132
|
+
* Use this when you need access tokens to call Discord APIs on behalf of the user.
|
|
133
|
+
*
|
|
134
|
+
* @param config - Discord OAuth 2.0 configuration
|
|
135
|
+
* @returns OAuth 2.0 provider configured for Discord
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* // Basic Discord authentication
|
|
140
|
+
* const basicDiscord = DiscordProvider({
|
|
141
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
142
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
143
|
+
* })
|
|
144
|
+
*
|
|
145
|
+
* // Discord with specific scopes
|
|
146
|
+
* const discordWithScopes = DiscordProvider({
|
|
147
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
148
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
149
|
+
* scopes: [
|
|
150
|
+
* "identify",
|
|
151
|
+
* "email",
|
|
152
|
+
* "guilds",
|
|
153
|
+
* "connections"
|
|
154
|
+
* ]
|
|
155
|
+
* })
|
|
156
|
+
*
|
|
157
|
+
* // Discord bot integration
|
|
158
|
+
* const discordBot = DiscordProvider({
|
|
159
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
160
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
161
|
+
* scopes: ["bot", "guilds"],
|
|
162
|
+
* query: {
|
|
163
|
+
* permissions: "2048" // Send Messages permission
|
|
164
|
+
* }
|
|
165
|
+
* })
|
|
166
|
+
*
|
|
167
|
+
* // Using the access token to fetch data
|
|
168
|
+
* export default issuer({
|
|
169
|
+
* providers: { discord: discordWithScopes },
|
|
170
|
+
* success: async (ctx, value) => {
|
|
171
|
+
* if (value.provider === "discord") {
|
|
172
|
+
* const token = value.tokenset.access
|
|
173
|
+
*
|
|
174
|
+
* // Get user profile
|
|
175
|
+
* const userRes = await fetch('https://discord.com/api/users/@me', {
|
|
176
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
177
|
+
* })
|
|
178
|
+
* const user = await userRes.json()
|
|
179
|
+
*
|
|
180
|
+
* // Get user guilds (if guilds scope granted)
|
|
181
|
+
* const guildsRes = await fetch('https://discord.com/api/users/@me/guilds', {
|
|
182
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
183
|
+
* })
|
|
184
|
+
* const guilds = await guildsRes.json()
|
|
185
|
+
*
|
|
186
|
+
* return ctx.subject("user", {
|
|
187
|
+
* discordId: user.id,
|
|
188
|
+
* username: user.username,
|
|
189
|
+
* discriminator: user.discriminator,
|
|
190
|
+
* email: user.email,
|
|
191
|
+
* avatar: user.avatar ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png` : null,
|
|
192
|
+
* guildCount: guilds.length
|
|
193
|
+
* })
|
|
194
|
+
* }
|
|
195
|
+
* }
|
|
196
|
+
* })
|
|
197
|
+
* ```
|
|
198
|
+
*
|
|
199
|
+
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
200
|
+
* - Development: `http://localhost:3000/auth/discord/callback`
|
|
201
|
+
* - Production: `https://yourapp.com/auth/discord/callback`
|
|
202
|
+
*
|
|
203
|
+
* Register this URL in your Discord Developer Portal.
|
|
204
|
+
*/
|
|
205
|
+
export declare const DiscordProvider: (config: DiscordConfig) => import("./provider").Provider<import("./oauth2").Oauth2UserData>;
|
|
206
|
+
//# sourceMappingURL=discord.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discord.d.ts","sourceRoot":"","sources":["../../../src/provider/discord.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AAEH,OAAO,EAAkB,KAAK,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAEnE;;;GAGG;AACH,MAAM,WAAW,aAAc,SAAQ,mBAAmB;IACzD;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IAEzB;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAE7B;;;;;;;;;;;;;;;OAeG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAA;IAEzB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACvC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,aAAa,qEASpD,CAAA"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Facebook OAuth 2.0 authentication provider for Draft Auth.
|
|
3
|
+
* Provides access tokens for calling Facebook Graph API on behalf of users.
|
|
4
|
+
*
|
|
5
|
+
* ## Quick Setup
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { FacebookProvider } from "@draftlab/auth/provider/facebook"
|
|
9
|
+
*
|
|
10
|
+
* export default issuer({
|
|
11
|
+
* basePath: "/auth", // Important for callback URL
|
|
12
|
+
* providers: {
|
|
13
|
+
* facebook: FacebookProvider({
|
|
14
|
+
* clientID: process.env.FACEBOOK_APP_ID,
|
|
15
|
+
* clientSecret: process.env.FACEBOOK_APP_SECRET,
|
|
16
|
+
* scopes: ["email", "public_profile", "user_friends"]
|
|
17
|
+
* })
|
|
18
|
+
* }
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
23
|
+
* - Development: `http://localhost:3000/auth/facebook/callback`
|
|
24
|
+
* - Production: `https://yourapp.com/auth/facebook/callback`
|
|
25
|
+
*
|
|
26
|
+
* Register this URL in your Facebook App Dashboard.
|
|
27
|
+
*
|
|
28
|
+
* ## Configuration Options
|
|
29
|
+
*
|
|
30
|
+
* - Access tokens for Facebook Graph API calls
|
|
31
|
+
* - Support for various Facebook permissions
|
|
32
|
+
* - Access to user data, posts, friends, etc.
|
|
33
|
+
*
|
|
34
|
+
* ## Common Facebook Permissions
|
|
35
|
+
*
|
|
36
|
+
* - `public_profile` - Basic profile information (name, picture, etc.)
|
|
37
|
+
* - `email` - User's email address
|
|
38
|
+
* - `user_friends` - List of user's friends who also use your app
|
|
39
|
+
* - `user_posts` - User's posts on their timeline
|
|
40
|
+
* - `user_photos` - User's photos and albums
|
|
41
|
+
* - `pages_read_engagement` - Read engagement data for Pages
|
|
42
|
+
*
|
|
43
|
+
* ## User Data Access
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* success: async (ctx, value) => {
|
|
47
|
+
* if (value.provider === "facebook") {
|
|
48
|
+
* const accessToken = value.tokenset.access
|
|
49
|
+
*
|
|
50
|
+
* // Fetch user profile from Graph API
|
|
51
|
+
* const profileResponse = await fetch(
|
|
52
|
+
* `https://graph.facebook.com/me?fields=id,name,email,picture&access_token=${accessToken}`
|
|
53
|
+
* )
|
|
54
|
+
* const profile = await profileResponse.json()
|
|
55
|
+
*
|
|
56
|
+
* // User info: `${profile.name} (${profile.email})`
|
|
57
|
+
* // Facebook ID: profile.id
|
|
58
|
+
* }
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @packageDocumentation
|
|
63
|
+
*/
|
|
64
|
+
import { type Oauth2WrappedConfig } from "./oauth2";
|
|
65
|
+
/**
|
|
66
|
+
* Configuration options for Facebook OAuth 2.0 provider.
|
|
67
|
+
* Extends the base OAuth 2.0 configuration with Facebook-specific documentation.
|
|
68
|
+
*/
|
|
69
|
+
export interface FacebookConfig extends Oauth2WrappedConfig {
|
|
70
|
+
/**
|
|
71
|
+
* Facebook App ID from your Facebook App Dashboard.
|
|
72
|
+
* This is the public identifier for your Facebook application.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* {
|
|
77
|
+
* clientID: "1234567890123456"
|
|
78
|
+
* }
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
readonly clientID: string;
|
|
82
|
+
/**
|
|
83
|
+
* Facebook App Secret from your Facebook App Dashboard.
|
|
84
|
+
* Keep this secure and never expose it to client-side code.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* {
|
|
89
|
+
* clientSecret: process.env.FACEBOOK_APP_SECRET
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
readonly clientSecret: string;
|
|
94
|
+
/**
|
|
95
|
+
* Facebook permissions to request during login.
|
|
96
|
+
* Determines what data your app can access from the user's Facebook account.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* {
|
|
101
|
+
* scopes: [
|
|
102
|
+
* "email", // User's email address
|
|
103
|
+
* "public_profile", // Basic profile info
|
|
104
|
+
* "user_friends", // User's friends list
|
|
105
|
+
* "user_posts" // User's timeline posts
|
|
106
|
+
* ]
|
|
107
|
+
* }
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
readonly scopes: string[];
|
|
111
|
+
/**
|
|
112
|
+
* Additional query parameters for Facebook OAuth authorization.
|
|
113
|
+
* Useful for Facebook-specific options like response type or display mode.
|
|
114
|
+
*
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* {
|
|
118
|
+
* query: {
|
|
119
|
+
* display: "popup", // Show login in popup
|
|
120
|
+
* auth_type: "rerequest", // Force permission re-request
|
|
121
|
+
* state: "custom-state" // Custom state parameter
|
|
122
|
+
* }
|
|
123
|
+
* }
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
readonly query?: Record<string, string>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Creates a Facebook OAuth 2.0 authentication provider.
|
|
130
|
+
* Use this when you need access tokens to call Facebook Graph API on behalf of the user.
|
|
131
|
+
*
|
|
132
|
+
* @param config - Facebook OAuth 2.0 configuration
|
|
133
|
+
* @returns OAuth 2.0 provider configured for Facebook
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* // Basic Facebook authentication
|
|
138
|
+
* const basicFacebook = FacebookProvider({
|
|
139
|
+
* clientID: process.env.FACEBOOK_APP_ID,
|
|
140
|
+
* clientSecret: process.env.FACEBOOK_APP_SECRET,
|
|
141
|
+
* scopes: ["email", "public_profile"]
|
|
142
|
+
* })
|
|
143
|
+
*
|
|
144
|
+
* // Facebook with extended permissions
|
|
145
|
+
* const extendedFacebook = FacebookProvider({
|
|
146
|
+
* clientID: process.env.FACEBOOK_APP_ID,
|
|
147
|
+
* clientSecret: process.env.FACEBOOK_APP_SECRET,
|
|
148
|
+
* scopes: [
|
|
149
|
+
* "email",
|
|
150
|
+
* "public_profile",
|
|
151
|
+
* "user_friends",
|
|
152
|
+
* "user_posts",
|
|
153
|
+
* "user_photos"
|
|
154
|
+
* ],
|
|
155
|
+
* query: {
|
|
156
|
+
* display: "popup",
|
|
157
|
+
* auth_type: "rerequest" // Force permission approval
|
|
158
|
+
* }
|
|
159
|
+
* })
|
|
160
|
+
*
|
|
161
|
+
* // Using the access token for Graph API calls
|
|
162
|
+
* export default issuer({
|
|
163
|
+
* providers: { facebook: extendedFacebook },
|
|
164
|
+
* success: async (ctx, value) => {
|
|
165
|
+
* if (value.provider === "facebook") {
|
|
166
|
+
* const token = value.tokenset.access
|
|
167
|
+
*
|
|
168
|
+
* // Get user profile with custom fields
|
|
169
|
+
* const profileRes = await fetch(
|
|
170
|
+
* `https://graph.facebook.com/me?fields=id,name,email,picture.width(200),friends&access_token=${token}`
|
|
171
|
+
* )
|
|
172
|
+
* const profile = await profileRes.json()
|
|
173
|
+
*
|
|
174
|
+
* // Get user's posts (if permission granted)
|
|
175
|
+
* const postsRes = await fetch(
|
|
176
|
+
* `https://graph.facebook.com/me/posts?access_token=${token}`
|
|
177
|
+
* )
|
|
178
|
+
* const posts = await postsRes.json()
|
|
179
|
+
*
|
|
180
|
+
* return ctx.subject("user", {
|
|
181
|
+
* facebookId: profile.id,
|
|
182
|
+
* name: profile.name,
|
|
183
|
+
* email: profile.email,
|
|
184
|
+
* picture: profile.picture?.data?.url,
|
|
185
|
+
* friendsCount: profile.friends?.summary?.total_count || 0,
|
|
186
|
+
* postsCount: posts.data?.length || 0
|
|
187
|
+
* })
|
|
188
|
+
* }
|
|
189
|
+
* }
|
|
190
|
+
* })
|
|
191
|
+
* ```
|
|
192
|
+
*
|
|
193
|
+
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
194
|
+
* - Development: `http://localhost:3000/auth/facebook/callback`
|
|
195
|
+
* - Production: `https://yourapp.com/auth/facebook/callback`
|
|
196
|
+
*
|
|
197
|
+
* Register this URL in your Facebook App Dashboard.
|
|
198
|
+
*/
|
|
199
|
+
export declare const FacebookProvider: (config: FacebookConfig) => import("./provider").Provider<import("./oauth2").Oauth2UserData>;
|
|
200
|
+
//# sourceMappingURL=facebook.d.ts.map
|