@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
package/dist/provider/code.mjs
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
import { generateUnbiasedDigits, timingSafeCompare } from "../random.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/provider/code.ts
|
|
4
|
-
/**
|
|
5
|
-
* PIN code authentication provider for Draft Auth.
|
|
6
|
-
* Supports flexible claim-based authentication via email, phone, or custom identifiers.
|
|
7
|
-
*
|
|
8
|
-
* ## Quick Setup
|
|
9
|
-
*
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { CodeUI } from "@draftlab/auth/ui/code"
|
|
12
|
-
* import { CodeProvider } from "@draftlab/auth/provider/code"
|
|
13
|
-
*
|
|
14
|
-
* export default issuer({
|
|
15
|
-
* providers: {
|
|
16
|
-
* code: CodeProvider(
|
|
17
|
-
* CodeUI({
|
|
18
|
-
* copy: {
|
|
19
|
-
* code_info: "We'll send a PIN code to your email"
|
|
20
|
-
* },
|
|
21
|
-
* sendCode: async (claims, code) => {
|
|
22
|
-
* try {
|
|
23
|
-
* await sendEmail(claims.email, `Your code: ${code}`)
|
|
24
|
-
* } catch {
|
|
25
|
-
* return { type: "invalid_claim", key: "delivery", value: "Failed to send code" }
|
|
26
|
-
* }
|
|
27
|
-
* }
|
|
28
|
-
* })
|
|
29
|
-
* )
|
|
30
|
-
* }
|
|
31
|
-
* })
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
* ## Custom Configuration
|
|
35
|
-
*
|
|
36
|
-
* ```ts
|
|
37
|
-
* const customCodeProvider = CodeProvider({
|
|
38
|
-
* length: 4, // 4-digit PIN instead of default 6
|
|
39
|
-
* request: async (req, state, form, error) => {
|
|
40
|
-
* return new Response(renderCodePage(state, form, error))
|
|
41
|
-
* },
|
|
42
|
-
* sendCode: async (claims, code) => {
|
|
43
|
-
* try {
|
|
44
|
-
* if (claims.email) {
|
|
45
|
-
* await emailService.send(claims.email, code)
|
|
46
|
-
* } else if (claims.phone) {
|
|
47
|
-
* await smsService.send(claims.phone, code)
|
|
48
|
-
* } else {
|
|
49
|
-
* return { type: "invalid_claim", key: "email", value: "Email or phone number is required" }
|
|
50
|
-
* }
|
|
51
|
-
* } catch {
|
|
52
|
-
* return { type: "invalid_claim", key: "delivery", value: "Failed to send code" }
|
|
53
|
-
* }
|
|
54
|
-
* }
|
|
55
|
-
* })
|
|
56
|
-
* ```
|
|
57
|
-
*
|
|
58
|
-
* ## Features
|
|
59
|
-
*
|
|
60
|
-
* - **Flexible claims**: Support any claim type (email, phone, username, etc.)
|
|
61
|
-
* - **Configurable PIN length**: 4-6 digit codes typically
|
|
62
|
-
* - **Resend functionality**: Built-in code resend capability
|
|
63
|
-
* - **Custom UI**: Full control over the authentication interface
|
|
64
|
-
* - **Error handling**: Comprehensive error states for different failure modes
|
|
65
|
-
*
|
|
66
|
-
* ## Flow States
|
|
67
|
-
*
|
|
68
|
-
* The provider manages a two-step authentication flow:
|
|
69
|
-
*
|
|
70
|
-
* 1. **Start**: User enters their claim (email, phone, etc.)
|
|
71
|
-
* 2. **Code**: User enters the PIN code sent to their claim
|
|
72
|
-
*
|
|
73
|
-
* ## User Data
|
|
74
|
-
*
|
|
75
|
-
* ```ts
|
|
76
|
-
* success: async (ctx, value) => {
|
|
77
|
-
* if (value.provider === "code") {
|
|
78
|
-
* // User's email: value.claims.email
|
|
79
|
-
* // User's phone (if provided): value.claims.phone
|
|
80
|
-
* // Any other claims collected during the flow
|
|
81
|
-
* }
|
|
82
|
-
* }
|
|
83
|
-
* ```
|
|
84
|
-
*
|
|
85
|
-
* @packageDocumentation
|
|
86
|
-
*/
|
|
87
|
-
/**
|
|
88
|
-
* Creates a PIN code authentication provider.
|
|
89
|
-
* Implements a flexible claim-based authentication flow with PIN verification.
|
|
90
|
-
*
|
|
91
|
-
* @template Claims - Type of claims to collect (email, phone, username, etc.)
|
|
92
|
-
* @param config - PIN code provider configuration
|
|
93
|
-
* @returns Provider instance implementing PIN code authentication
|
|
94
|
-
*
|
|
95
|
-
* @example
|
|
96
|
-
* ```ts
|
|
97
|
-
* // Email-based PIN authentication
|
|
98
|
-
* const emailCodeProvider = CodeProvider<{ email: string }>({
|
|
99
|
-
* length: 6,
|
|
100
|
-
* request: async (req, state, form, error) => {
|
|
101
|
-
* if (state.type === 'start') {
|
|
102
|
-
* return new Response(renderEmailForm(form?.get('email'), error))
|
|
103
|
-
* } else {
|
|
104
|
-
* return new Response(renderPinForm(state.claims.email, error, state.resend))
|
|
105
|
-
* }
|
|
106
|
-
* },
|
|
107
|
-
* sendCode: async (claims, code) => {
|
|
108
|
-
* if (!claims.email || !isValidEmail(claims.email)) {
|
|
109
|
-
* return {
|
|
110
|
-
* type: "invalid_claim",
|
|
111
|
-
* key: "email",
|
|
112
|
-
* value: "Invalid email address"
|
|
113
|
-
* }
|
|
114
|
-
* }
|
|
115
|
-
*
|
|
116
|
-
* try {
|
|
117
|
-
* await emailService.send(claims.email, `Your verification code: ${code}`)
|
|
118
|
-
* } catch {
|
|
119
|
-
* return {
|
|
120
|
-
* type: "invalid_claim",
|
|
121
|
-
* key: "delivery",
|
|
122
|
-
* value: "Failed to send code"
|
|
123
|
-
* }
|
|
124
|
-
* }
|
|
125
|
-
* }
|
|
126
|
-
* })
|
|
127
|
-
*
|
|
128
|
-
* // Multi-channel PIN authentication (email or phone)
|
|
129
|
-
* const flexibleCodeProvider = CodeProvider<{ email?: string; phone?: string }>({
|
|
130
|
-
* length: 4,
|
|
131
|
-
* request: async (req, state, form, error) => {
|
|
132
|
-
* if (state.type === 'start') {
|
|
133
|
-
* return new Response(renderContactForm(form, error))
|
|
134
|
-
* } else {
|
|
135
|
-
* const contact = state.claims.email || state.claims.phone
|
|
136
|
-
* return new Response(renderPinForm(contact, error))
|
|
137
|
-
* }
|
|
138
|
-
* },
|
|
139
|
-
* sendCode: async (claims, code) => {
|
|
140
|
-
* try {
|
|
141
|
-
* if (claims.email) {
|
|
142
|
-
* await emailService.send(claims.email, `PIN: ${code}`)
|
|
143
|
-
* } else if (claims.phone) {
|
|
144
|
-
* await smsService.send(claims.phone, `PIN: ${code}`)
|
|
145
|
-
* } else {
|
|
146
|
-
* return {
|
|
147
|
-
* type: "invalid_claim",
|
|
148
|
-
* key: "email",
|
|
149
|
-
* value: "Provide either email or phone number"
|
|
150
|
-
* }
|
|
151
|
-
* }
|
|
152
|
-
* } catch {
|
|
153
|
-
* return {
|
|
154
|
-
* type: "invalid_claim",
|
|
155
|
-
* key: "delivery",
|
|
156
|
-
* value: "Failed to send code"
|
|
157
|
-
* }
|
|
158
|
-
* }
|
|
159
|
-
* }
|
|
160
|
-
* })
|
|
161
|
-
*
|
|
162
|
-
* // Usage in issuer
|
|
163
|
-
* export default issuer({
|
|
164
|
-
* providers: {
|
|
165
|
-
* email: emailCodeProvider,
|
|
166
|
-
* flexible: flexibleCodeProvider
|
|
167
|
-
* },
|
|
168
|
-
* success: async (ctx, value) => {
|
|
169
|
-
* if (value.provider === "code") {
|
|
170
|
-
* const email = value.claims.email
|
|
171
|
-
* const phone = value.claims.phone
|
|
172
|
-
*
|
|
173
|
-
* // Look up or create user based on verified claims
|
|
174
|
-
* const userId = await findOrCreateUser({ email, phone })
|
|
175
|
-
*
|
|
176
|
-
* return ctx.subject("user", { userId, email, phone })
|
|
177
|
-
* }
|
|
178
|
-
* }
|
|
179
|
-
* })
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
const CodeProvider = (config) => {
|
|
183
|
-
const codeLength = config.length || 6;
|
|
184
|
-
/**
|
|
185
|
-
* Generates a cryptographically secure PIN code.
|
|
186
|
-
*/
|
|
187
|
-
const generateCode = () => {
|
|
188
|
-
return generateUnbiasedDigits(codeLength);
|
|
189
|
-
};
|
|
190
|
-
return {
|
|
191
|
-
type: "code",
|
|
192
|
-
init(routes, ctx) {
|
|
193
|
-
/**
|
|
194
|
-
* Transitions between authentication states and renders the appropriate UI.
|
|
195
|
-
*/
|
|
196
|
-
const transition = async (c, nextState, formData, error) => {
|
|
197
|
-
await ctx.set(c, "provider", 3600 * 24, nextState);
|
|
198
|
-
const response = await config.request(c.request, nextState, formData, error);
|
|
199
|
-
return ctx.forward(c, response);
|
|
200
|
-
};
|
|
201
|
-
/**
|
|
202
|
-
* GET /authorize - Display initial claim collection form
|
|
203
|
-
*/
|
|
204
|
-
routes.get("/authorize", (c) => {
|
|
205
|
-
return transition(c, { type: "start" });
|
|
206
|
-
});
|
|
207
|
-
/**
|
|
208
|
-
* POST /authorize - Handle form submissions and state transitions
|
|
209
|
-
*/
|
|
210
|
-
routes.post("/authorize", async (c) => {
|
|
211
|
-
const formData = await c.formData();
|
|
212
|
-
const currentState = await ctx.get(c, "provider");
|
|
213
|
-
const action = formData.get("action")?.toString();
|
|
214
|
-
if (action === "request" || action === "resend") {
|
|
215
|
-
const code = generateCode();
|
|
216
|
-
const { action: _, ...claims } = Object.fromEntries(formData);
|
|
217
|
-
const sendError = await config.sendCode(claims, code);
|
|
218
|
-
if (sendError) return transition(c, { type: "start" }, formData, sendError);
|
|
219
|
-
return transition(c, {
|
|
220
|
-
type: "code",
|
|
221
|
-
resend: action === "resend",
|
|
222
|
-
claims,
|
|
223
|
-
code
|
|
224
|
-
}, formData);
|
|
225
|
-
} else if (action === "verify" && currentState?.type === "code") {
|
|
226
|
-
const enteredCode = formData.get("code")?.toString();
|
|
227
|
-
if (!(currentState.code && enteredCode && timingSafeCompare(currentState.code, enteredCode))) return transition(c, {
|
|
228
|
-
...currentState,
|
|
229
|
-
resend: false
|
|
230
|
-
}, formData, { type: "invalid_code" });
|
|
231
|
-
if (!await ctx.get(c, "authorization")) return transition(c, { type: "start" }, formData, {
|
|
232
|
-
type: "invalid_claim",
|
|
233
|
-
key: "session",
|
|
234
|
-
value: "Authentication session expired"
|
|
235
|
-
});
|
|
236
|
-
await ctx.unset(c, "provider");
|
|
237
|
-
return await ctx.success(c, { claims: currentState.claims });
|
|
238
|
-
}
|
|
239
|
-
return transition(c, { type: "start" });
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
//#endregion
|
|
246
|
-
export { CodeProvider };
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { Provider } from "./provider.mjs";
|
|
2
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/provider/discord.d.ts
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Configuration options for Discord OAuth 2.0 provider.
|
|
8
|
-
* Extends the base OAuth 2.0 configuration with Discord-specific documentation.
|
|
9
|
-
*/
|
|
10
|
-
interface DiscordConfig extends Oauth2WrappedConfig {
|
|
11
|
-
/**
|
|
12
|
-
* Discord OAuth 2.0 client ID from Discord Developer Portal.
|
|
13
|
-
* Found in your Discord application settings.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* {
|
|
18
|
-
* clientID: "1234567890123456789"
|
|
19
|
-
* }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
readonly clientID: string;
|
|
23
|
-
/**
|
|
24
|
-
* Discord OAuth 2.0 client secret from Discord Developer Portal.
|
|
25
|
-
* Keep this secure and never expose it to client-side code.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* {
|
|
30
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
31
|
-
* }
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
readonly clientSecret: string;
|
|
35
|
-
/**
|
|
36
|
-
* Discord OAuth scopes to request access for.
|
|
37
|
-
* Determines what data and actions your app can access.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```ts
|
|
41
|
-
* {
|
|
42
|
-
* scopes: [
|
|
43
|
-
* "identify", // Basic user information
|
|
44
|
-
* "email", // Email address
|
|
45
|
-
* "guilds", // User's Discord servers
|
|
46
|
-
* "connections" // Connected accounts (Steam, etc.)
|
|
47
|
-
* ]
|
|
48
|
-
* }
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
readonly scopes: string[];
|
|
52
|
-
/**
|
|
53
|
-
* Additional query parameters for Discord OAuth authorization.
|
|
54
|
-
* Useful for Discord-specific options like permissions.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* {
|
|
59
|
-
* query: {
|
|
60
|
-
* permissions: "8", // Administrator permission
|
|
61
|
-
* guild_id: "123456789", // Pre-select specific guild
|
|
62
|
-
* disable_guild_select: "true" // Disable guild selection
|
|
63
|
-
* }
|
|
64
|
-
* }
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
readonly query?: Record<string, string>;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Creates a Discord OAuth 2.0 authentication provider.
|
|
71
|
-
* Use this when you need access tokens to call Discord APIs on behalf of the user.
|
|
72
|
-
*
|
|
73
|
-
* @param config - Discord OAuth 2.0 configuration
|
|
74
|
-
* @returns OAuth 2.0 provider configured for Discord
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```ts
|
|
78
|
-
* // Basic Discord authentication
|
|
79
|
-
* const basicDiscord = DiscordProvider({
|
|
80
|
-
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
81
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
82
|
-
* })
|
|
83
|
-
*
|
|
84
|
-
* // Discord with specific scopes
|
|
85
|
-
* const discordWithScopes = DiscordProvider({
|
|
86
|
-
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
87
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
88
|
-
* scopes: [
|
|
89
|
-
* "identify",
|
|
90
|
-
* "email",
|
|
91
|
-
* "guilds",
|
|
92
|
-
* "connections"
|
|
93
|
-
* ]
|
|
94
|
-
* })
|
|
95
|
-
*
|
|
96
|
-
* // Discord bot integration
|
|
97
|
-
* const discordBot = DiscordProvider({
|
|
98
|
-
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
99
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
100
|
-
* scopes: ["bot", "guilds"],
|
|
101
|
-
* query: {
|
|
102
|
-
* permissions: "2048" // Send Messages permission
|
|
103
|
-
* }
|
|
104
|
-
* })
|
|
105
|
-
*
|
|
106
|
-
* // Using the access token to fetch data
|
|
107
|
-
* export default issuer({
|
|
108
|
-
* providers: { discord: discordWithScopes },
|
|
109
|
-
* success: async (ctx, value) => {
|
|
110
|
-
* if (value.provider === "discord") {
|
|
111
|
-
* const token = value.tokenset.access
|
|
112
|
-
*
|
|
113
|
-
* // Get user profile
|
|
114
|
-
* const userRes = await fetch('https://discord.com/api/users/@me', {
|
|
115
|
-
* headers: { Authorization: `Bearer ${token}` }
|
|
116
|
-
* })
|
|
117
|
-
* const user = await userRes.json()
|
|
118
|
-
*
|
|
119
|
-
* // Get user guilds (if guilds scope granted)
|
|
120
|
-
* const guildsRes = await fetch('https://discord.com/api/users/@me/guilds', {
|
|
121
|
-
* headers: { Authorization: `Bearer ${token}` }
|
|
122
|
-
* })
|
|
123
|
-
* const guilds = await guildsRes.json()
|
|
124
|
-
*
|
|
125
|
-
* return ctx.subject("user", {
|
|
126
|
-
* discordId: user.id,
|
|
127
|
-
* username: user.username,
|
|
128
|
-
* discriminator: user.discriminator,
|
|
129
|
-
* email: user.email,
|
|
130
|
-
* avatar: user.avatar ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png` : null,
|
|
131
|
-
* guildCount: guilds.length
|
|
132
|
-
* })
|
|
133
|
-
* }
|
|
134
|
-
* }
|
|
135
|
-
* })
|
|
136
|
-
* ```
|
|
137
|
-
*
|
|
138
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
139
|
-
* - Development: `http://localhost:3000/auth/discord/callback`
|
|
140
|
-
* - Production: `https://yourapp.com/auth/discord/callback`
|
|
141
|
-
*
|
|
142
|
-
* Register this URL in your Discord Developer Portal.
|
|
143
|
-
*/
|
|
144
|
-
declare const DiscordProvider: (config: DiscordConfig) => Provider<Oauth2UserData>;
|
|
145
|
-
//#endregion
|
|
146
|
-
export { DiscordConfig, DiscordProvider };
|
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
import { Oauth2Provider } from "./oauth2.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/provider/discord.ts
|
|
4
|
-
/**
|
|
5
|
-
* Discord OAuth 2.0 authentication provider for Draft Auth.
|
|
6
|
-
* Provides access tokens for calling Discord APIs on behalf of users.
|
|
7
|
-
*
|
|
8
|
-
* ## Quick Setup
|
|
9
|
-
*
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { DiscordProvider } from "@draftlab/auth/provider/discord"
|
|
12
|
-
*
|
|
13
|
-
* export default issuer({
|
|
14
|
-
* basePath: "/auth", // Important for callback URL
|
|
15
|
-
* providers: {
|
|
16
|
-
* discord: DiscordProvider({
|
|
17
|
-
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
18
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
19
|
-
* scopes: ["identify", "email", "guilds"]
|
|
20
|
-
* })
|
|
21
|
-
* }
|
|
22
|
-
* })
|
|
23
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
26
|
-
* - Development: `http://localhost:3000/auth/discord/callback`
|
|
27
|
-
* - Production: `https://yourapp.com/auth/discord/callback`
|
|
28
|
-
*
|
|
29
|
-
* Register this URL in your Discord Developer Portal.
|
|
30
|
-
*
|
|
31
|
-
* ## Common Scopes
|
|
32
|
-
*
|
|
33
|
-
* - `identify` - Access to user's basic account information
|
|
34
|
-
* - `email` - Access to user's email address
|
|
35
|
-
* - `guilds` - Access to user's guilds (servers)
|
|
36
|
-
* - `guilds.join` - Ability to join user to guilds
|
|
37
|
-
* - `gdm.join` - Ability to join user to group DMs
|
|
38
|
-
* - `connections` - Access to user's connections (Steam, YouTube, etc.)
|
|
39
|
-
* - `guilds.members.read` - Read guild member information
|
|
40
|
-
* - `bot` - For bot applications (requires additional setup)
|
|
41
|
-
*
|
|
42
|
-
* ## User Data Access
|
|
43
|
-
*
|
|
44
|
-
* ```ts
|
|
45
|
-
* success: async (ctx, value) => {
|
|
46
|
-
* if (value.provider === "discord") {
|
|
47
|
-
* const accessToken = value.tokenset.access
|
|
48
|
-
*
|
|
49
|
-
* // Fetch user information
|
|
50
|
-
* const userResponse = await fetch('https://discord.com/api/users/@me', {
|
|
51
|
-
* headers: { Authorization: `Bearer ${accessToken}` }
|
|
52
|
-
* })
|
|
53
|
-
* const user = await userResponse.json()
|
|
54
|
-
*
|
|
55
|
-
* // Fetch user guilds (requires guilds scope)
|
|
56
|
-
* const guildsResponse = await fetch('https://discord.com/api/users/@me/guilds', {
|
|
57
|
-
* headers: { Authorization: `Bearer ${accessToken}` }
|
|
58
|
-
* })
|
|
59
|
-
* const guilds = await guildsResponse.json()
|
|
60
|
-
*
|
|
61
|
-
* // User info: user.username + user.discriminator
|
|
62
|
-
* // Avatar: `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png`
|
|
63
|
-
* }
|
|
64
|
-
* }
|
|
65
|
-
* ```
|
|
66
|
-
*
|
|
67
|
-
* @packageDocumentation
|
|
68
|
-
*/
|
|
69
|
-
/**
|
|
70
|
-
* Creates a Discord OAuth 2.0 authentication provider.
|
|
71
|
-
* Use this when you need access tokens to call Discord APIs on behalf of the user.
|
|
72
|
-
*
|
|
73
|
-
* @param config - Discord OAuth 2.0 configuration
|
|
74
|
-
* @returns OAuth 2.0 provider configured for Discord
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```ts
|
|
78
|
-
* // Basic Discord authentication
|
|
79
|
-
* const basicDiscord = DiscordProvider({
|
|
80
|
-
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
81
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
82
|
-
* })
|
|
83
|
-
*
|
|
84
|
-
* // Discord with specific scopes
|
|
85
|
-
* const discordWithScopes = DiscordProvider({
|
|
86
|
-
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
87
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
88
|
-
* scopes: [
|
|
89
|
-
* "identify",
|
|
90
|
-
* "email",
|
|
91
|
-
* "guilds",
|
|
92
|
-
* "connections"
|
|
93
|
-
* ]
|
|
94
|
-
* })
|
|
95
|
-
*
|
|
96
|
-
* // Discord bot integration
|
|
97
|
-
* const discordBot = DiscordProvider({
|
|
98
|
-
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
99
|
-
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
100
|
-
* scopes: ["bot", "guilds"],
|
|
101
|
-
* query: {
|
|
102
|
-
* permissions: "2048" // Send Messages permission
|
|
103
|
-
* }
|
|
104
|
-
* })
|
|
105
|
-
*
|
|
106
|
-
* // Using the access token to fetch data
|
|
107
|
-
* export default issuer({
|
|
108
|
-
* providers: { discord: discordWithScopes },
|
|
109
|
-
* success: async (ctx, value) => {
|
|
110
|
-
* if (value.provider === "discord") {
|
|
111
|
-
* const token = value.tokenset.access
|
|
112
|
-
*
|
|
113
|
-
* // Get user profile
|
|
114
|
-
* const userRes = await fetch('https://discord.com/api/users/@me', {
|
|
115
|
-
* headers: { Authorization: `Bearer ${token}` }
|
|
116
|
-
* })
|
|
117
|
-
* const user = await userRes.json()
|
|
118
|
-
*
|
|
119
|
-
* // Get user guilds (if guilds scope granted)
|
|
120
|
-
* const guildsRes = await fetch('https://discord.com/api/users/@me/guilds', {
|
|
121
|
-
* headers: { Authorization: `Bearer ${token}` }
|
|
122
|
-
* })
|
|
123
|
-
* const guilds = await guildsRes.json()
|
|
124
|
-
*
|
|
125
|
-
* return ctx.subject("user", {
|
|
126
|
-
* discordId: user.id,
|
|
127
|
-
* username: user.username,
|
|
128
|
-
* discriminator: user.discriminator,
|
|
129
|
-
* email: user.email,
|
|
130
|
-
* avatar: user.avatar ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png` : null,
|
|
131
|
-
* guildCount: guilds.length
|
|
132
|
-
* })
|
|
133
|
-
* }
|
|
134
|
-
* }
|
|
135
|
-
* })
|
|
136
|
-
* ```
|
|
137
|
-
*
|
|
138
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
139
|
-
* - Development: `http://localhost:3000/auth/discord/callback`
|
|
140
|
-
* - Production: `https://yourapp.com/auth/discord/callback`
|
|
141
|
-
*
|
|
142
|
-
* Register this URL in your Discord Developer Portal.
|
|
143
|
-
*/
|
|
144
|
-
const DiscordProvider = (config) => {
|
|
145
|
-
return Oauth2Provider({
|
|
146
|
-
...config,
|
|
147
|
-
type: "discord",
|
|
148
|
-
endpoint: {
|
|
149
|
-
authorization: "https://discord.com/oauth2/authorize",
|
|
150
|
-
token: "https://discord.com/api/oauth2/token"
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
//#endregion
|
|
156
|
-
export { DiscordProvider };
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
import { Provider } from "./provider.mjs";
|
|
2
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/provider/facebook.d.ts
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Configuration options for Facebook OAuth 2.0 provider.
|
|
8
|
-
* Extends the base OAuth 2.0 configuration with Facebook-specific documentation.
|
|
9
|
-
*/
|
|
10
|
-
interface FacebookConfig extends Oauth2WrappedConfig {
|
|
11
|
-
/**
|
|
12
|
-
* Facebook App ID from your Facebook App Dashboard.
|
|
13
|
-
* This is the public identifier for your Facebook application.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* {
|
|
18
|
-
* clientID: "1234567890123456"
|
|
19
|
-
* }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
readonly clientID: string;
|
|
23
|
-
/**
|
|
24
|
-
* Facebook App Secret from your Facebook App Dashboard.
|
|
25
|
-
* Keep this secure and never expose it to client-side code.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* {
|
|
30
|
-
* clientSecret: process.env.FACEBOOK_APP_SECRET
|
|
31
|
-
* }
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
readonly clientSecret: string;
|
|
35
|
-
/**
|
|
36
|
-
* Facebook permissions to request during login.
|
|
37
|
-
* Determines what data your app can access from the user's Facebook account.
|
|
38
|
-
*
|
|
39
|
-
* @example
|
|
40
|
-
* ```ts
|
|
41
|
-
* {
|
|
42
|
-
* scopes: [
|
|
43
|
-
* "email", // User's email address
|
|
44
|
-
* "public_profile", // Basic profile info
|
|
45
|
-
* "user_friends", // User's friends list
|
|
46
|
-
* "user_posts" // User's timeline posts
|
|
47
|
-
* ]
|
|
48
|
-
* }
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
readonly scopes: string[];
|
|
52
|
-
/**
|
|
53
|
-
* Additional query parameters for Facebook OAuth authorization.
|
|
54
|
-
* Useful for Facebook-specific options like response type or display mode.
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```ts
|
|
58
|
-
* {
|
|
59
|
-
* query: {
|
|
60
|
-
* display: "popup", // Show login in popup
|
|
61
|
-
* auth_type: "rerequest", // Force permission re-request
|
|
62
|
-
* state: "custom-state" // Custom state parameter
|
|
63
|
-
* }
|
|
64
|
-
* }
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
readonly query?: Record<string, string>;
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* Creates a Facebook OAuth 2.0 authentication provider.
|
|
71
|
-
* Use this when you need access tokens to call Facebook Graph API on behalf of the user.
|
|
72
|
-
*
|
|
73
|
-
* @param config - Facebook OAuth 2.0 configuration
|
|
74
|
-
* @returns OAuth 2.0 provider configured for Facebook
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* ```ts
|
|
78
|
-
* // Basic Facebook authentication
|
|
79
|
-
* const basicFacebook = FacebookProvider({
|
|
80
|
-
* clientID: process.env.FACEBOOK_APP_ID,
|
|
81
|
-
* clientSecret: process.env.FACEBOOK_APP_SECRET,
|
|
82
|
-
* scopes: ["email", "public_profile"]
|
|
83
|
-
* })
|
|
84
|
-
*
|
|
85
|
-
* // Facebook with extended permissions
|
|
86
|
-
* const extendedFacebook = FacebookProvider({
|
|
87
|
-
* clientID: process.env.FACEBOOK_APP_ID,
|
|
88
|
-
* clientSecret: process.env.FACEBOOK_APP_SECRET,
|
|
89
|
-
* scopes: [
|
|
90
|
-
* "email",
|
|
91
|
-
* "public_profile",
|
|
92
|
-
* "user_friends",
|
|
93
|
-
* "user_posts",
|
|
94
|
-
* "user_photos"
|
|
95
|
-
* ],
|
|
96
|
-
* query: {
|
|
97
|
-
* display: "popup",
|
|
98
|
-
* auth_type: "rerequest" // Force permission approval
|
|
99
|
-
* }
|
|
100
|
-
* })
|
|
101
|
-
*
|
|
102
|
-
* // Using the access token for Graph API calls
|
|
103
|
-
* export default issuer({
|
|
104
|
-
* providers: { facebook: extendedFacebook },
|
|
105
|
-
* success: async (ctx, value) => {
|
|
106
|
-
* if (value.provider === "facebook") {
|
|
107
|
-
* const token = value.tokenset.access
|
|
108
|
-
*
|
|
109
|
-
* // Get user profile with custom fields
|
|
110
|
-
* const profileRes = await fetch(
|
|
111
|
-
* `https://graph.facebook.com/me?fields=id,name,email,picture.width(200),friends&access_token=${token}`
|
|
112
|
-
* )
|
|
113
|
-
* const profile = await profileRes.json()
|
|
114
|
-
*
|
|
115
|
-
* // Get user's posts (if permission granted)
|
|
116
|
-
* const postsRes = await fetch(
|
|
117
|
-
* `https://graph.facebook.com/me/posts?access_token=${token}`
|
|
118
|
-
* )
|
|
119
|
-
* const posts = await postsRes.json()
|
|
120
|
-
*
|
|
121
|
-
* return ctx.subject("user", {
|
|
122
|
-
* facebookId: profile.id,
|
|
123
|
-
* name: profile.name,
|
|
124
|
-
* email: profile.email,
|
|
125
|
-
* picture: profile.picture?.data?.url,
|
|
126
|
-
* friendsCount: profile.friends?.summary?.total_count || 0,
|
|
127
|
-
* postsCount: posts.data?.length || 0
|
|
128
|
-
* })
|
|
129
|
-
* }
|
|
130
|
-
* }
|
|
131
|
-
* })
|
|
132
|
-
* ```
|
|
133
|
-
*
|
|
134
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
135
|
-
* - Development: `http://localhost:3000/auth/facebook/callback`
|
|
136
|
-
* - Production: `https://yourapp.com/auth/facebook/callback`
|
|
137
|
-
*
|
|
138
|
-
* Register this URL in your Facebook App Dashboard.
|
|
139
|
-
*/
|
|
140
|
-
declare const FacebookProvider: (config: FacebookConfig) => Provider<Oauth2UserData>;
|
|
141
|
-
//#endregion
|
|
142
|
-
export { FacebookConfig, FacebookProvider };
|