@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
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { Oauth2Provider } from "./oauth2.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/provider/spotify.ts
|
|
4
|
-
/**
|
|
5
|
-
* Spotify authentication provider for Draft Auth.
|
|
6
|
-
* Implements OAuth 2.0 flow for authenticating users with their Spotify accounts.
|
|
7
|
-
*
|
|
8
|
-
* ## Quick Setup
|
|
9
|
-
*
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { SpotifyProvider } from "@draftlab/auth/provider/spotify"
|
|
12
|
-
*
|
|
13
|
-
* export default issuer({
|
|
14
|
-
* basePath: "/auth", // Important for callback URL
|
|
15
|
-
* providers: {
|
|
16
|
-
* spotify: SpotifyProvider({
|
|
17
|
-
* clientID: process.env.SPOTIFY_CLIENT_ID,
|
|
18
|
-
* clientSecret: process.env.SPOTIFY_CLIENT_SECRET,
|
|
19
|
-
* scopes: ["user-read-private", "user-read-email"]
|
|
20
|
-
* })
|
|
21
|
-
* }
|
|
22
|
-
* })
|
|
23
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
26
|
-
* - Development: `http://localhost:3000/auth/spotify/callback`
|
|
27
|
-
* - Production: `https://yourapp.com/auth/spotify/callback`
|
|
28
|
-
*
|
|
29
|
-
* Register this URL in your Spotify Developer Dashboard.
|
|
30
|
-
*
|
|
31
|
-
* ## Common Scopes
|
|
32
|
-
*
|
|
33
|
-
* - `user-read-private` - Access user's private data
|
|
34
|
-
* - `user-read-email` - Access user's email address
|
|
35
|
-
* - `user-top-read` - Read user's top artists and tracks
|
|
36
|
-
* - `user-read-playback-state` - Read current playback state
|
|
37
|
-
* - `user-modify-playback-state` - Modify playback state
|
|
38
|
-
* - `user-read-currently-playing` - Read currently playing track
|
|
39
|
-
* - `playlist-read-private` - Access private playlists
|
|
40
|
-
* - `playlist-read-public` - Access public playlists
|
|
41
|
-
* - `user-library-read` - Read user's library
|
|
42
|
-
* - `user-follow-read` - Read followed artists and users
|
|
43
|
-
*
|
|
44
|
-
* ## User Data Access
|
|
45
|
-
*
|
|
46
|
-
* ```ts
|
|
47
|
-
* success: async (ctx, value) => {
|
|
48
|
-
* if (value.provider === "spotify") {
|
|
49
|
-
* const accessToken = value.tokenset.access
|
|
50
|
-
*
|
|
51
|
-
* // Fetch user profile
|
|
52
|
-
* const userResponse = await fetch('https://api.spotify.com/v1/me', {
|
|
53
|
-
* headers: { Authorization: `Bearer ${accessToken}` }
|
|
54
|
-
* })
|
|
55
|
-
* const user = await userResponse.json()
|
|
56
|
-
*
|
|
57
|
-
* // User info: id, email, display_name, external_urls, images, followers
|
|
58
|
-
* }
|
|
59
|
-
* }
|
|
60
|
-
* ```
|
|
61
|
-
*
|
|
62
|
-
* @packageDocumentation
|
|
63
|
-
*/
|
|
64
|
-
/**
|
|
65
|
-
* Creates a Spotify OAuth 2.0 authentication provider.
|
|
66
|
-
* Allows users to authenticate using their Spotify accounts.
|
|
67
|
-
*
|
|
68
|
-
* @param config - Spotify OAuth 2.0 configuration
|
|
69
|
-
* @returns OAuth 2.0 provider configured for Spotify
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```ts
|
|
73
|
-
* // Basic Spotify authentication
|
|
74
|
-
* const basicSpotify = SpotifyProvider({
|
|
75
|
-
* clientID: process.env.SPOTIFY_CLIENT_ID,
|
|
76
|
-
* clientSecret: process.env.SPOTIFY_CLIENT_SECRET
|
|
77
|
-
* })
|
|
78
|
-
*
|
|
79
|
-
* // Spotify with user data access
|
|
80
|
-
* const spotifyWithScopes = SpotifyProvider({
|
|
81
|
-
* clientID: process.env.SPOTIFY_CLIENT_ID,
|
|
82
|
-
* clientSecret: process.env.SPOTIFY_CLIENT_SECRET,
|
|
83
|
-
* scopes: ["user-read-private", "user-read-email", "user-top-read"]
|
|
84
|
-
* })
|
|
85
|
-
*
|
|
86
|
-
* // Using the access token to fetch user data
|
|
87
|
-
* export default issuer({
|
|
88
|
-
* providers: { spotify: spotifyWithScopes },
|
|
89
|
-
* success: async (ctx, value) => {
|
|
90
|
-
* if (value.provider === "spotify") {
|
|
91
|
-
* const token = value.tokenset.access
|
|
92
|
-
*
|
|
93
|
-
* const userRes = await fetch('https://api.spotify.com/v1/me', {
|
|
94
|
-
* headers: { Authorization: `Bearer ${token}` }
|
|
95
|
-
* })
|
|
96
|
-
* const user = await userRes.json()
|
|
97
|
-
*
|
|
98
|
-
* // Optionally fetch top tracks
|
|
99
|
-
* const topRes = await fetch('https://api.spotify.com/v1/me/top/tracks?limit=5', {
|
|
100
|
-
* headers: { Authorization: `Bearer ${token}` }
|
|
101
|
-
* })
|
|
102
|
-
* const { items: topTracks } = await topRes.json()
|
|
103
|
-
*
|
|
104
|
-
* return ctx.subject("user", {
|
|
105
|
-
* spotifyId: user.id,
|
|
106
|
-
* email: user.email,
|
|
107
|
-
* displayName: user.display_name,
|
|
108
|
-
* profileUrl: user.external_urls?.spotify,
|
|
109
|
-
* followers: user.followers?.total,
|
|
110
|
-
* topTracks: topTracks.map(t => t.name)
|
|
111
|
-
* })
|
|
112
|
-
* }
|
|
113
|
-
* }
|
|
114
|
-
* })
|
|
115
|
-
* ```
|
|
116
|
-
*
|
|
117
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
118
|
-
* - Development: `http://localhost:3000/auth/spotify/callback`
|
|
119
|
-
* - Production: `https://yourapp.com/auth/spotify/callback`
|
|
120
|
-
*
|
|
121
|
-
* Register this URL in your Spotify Developer Dashboard.
|
|
122
|
-
*/
|
|
123
|
-
const SpotifyProvider = (config) => {
|
|
124
|
-
return Oauth2Provider({
|
|
125
|
-
...config,
|
|
126
|
-
type: "spotify",
|
|
127
|
-
endpoint: {
|
|
128
|
-
authorization: "https://accounts.spotify.com/authorize",
|
|
129
|
-
token: "https://accounts.spotify.com/api/token"
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
//#endregion
|
|
135
|
-
export { SpotifyProvider };
|
package/dist/provider/totp.d.mts
DELETED
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import { Provider } from "./provider.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/provider/totp.d.ts
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* TOTP data model stored in the database.
|
|
7
|
-
* Contains the user's TOTP configuration and backup codes.
|
|
8
|
-
*/
|
|
9
|
-
interface TOTPModel {
|
|
10
|
-
/** Base32-encoded secret key */
|
|
11
|
-
secret: string;
|
|
12
|
-
/** Whether TOTP is enabled for this user */
|
|
13
|
-
enabled: boolean;
|
|
14
|
-
/** Array of one-time backup/recovery codes */
|
|
15
|
-
backupCodes: string[];
|
|
16
|
-
/** Timestamp when TOTP was first set up */
|
|
17
|
-
createdAt: string;
|
|
18
|
-
/** Optional user label for the TOTP */
|
|
19
|
-
label?: string;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Configuration for the TOTPProvider.
|
|
23
|
-
* Defines how the TOTP authentication flow should behave.
|
|
24
|
-
*/
|
|
25
|
-
interface TOTPProviderConfig {
|
|
26
|
-
/**
|
|
27
|
-
* The human-readable name of the issuer (your application).
|
|
28
|
-
* This appears in authenticator apps next to the TOTP entry.
|
|
29
|
-
*/
|
|
30
|
-
issuer: string;
|
|
31
|
-
/**
|
|
32
|
-
* Custom authorize handler that generates the UI for TOTP login.
|
|
33
|
-
* Called when user wants to login with TOTP (main page).
|
|
34
|
-
*
|
|
35
|
-
* @param req - The HTTP request object
|
|
36
|
-
* @param error - Optional error message to display
|
|
37
|
-
*/
|
|
38
|
-
authorize: (req: Request, error?: string) => Promise<Response>;
|
|
39
|
-
/**
|
|
40
|
-
* Custom register handler that generates the UI for TOTP setup.
|
|
41
|
-
* Called when user is setting up TOTP for the first time.
|
|
42
|
-
*
|
|
43
|
-
* @param req - The HTTP request object
|
|
44
|
-
* @param qrCodeUrl - The otpauth:// URL for QR code generation
|
|
45
|
-
* @param secret - The raw secret (for manual entry)
|
|
46
|
-
* @param backupCodes - Array of backup/recovery codes
|
|
47
|
-
* @param error - Optional error message to display
|
|
48
|
-
*/
|
|
49
|
-
register: (req: Request, qrCodeUrl: string, secret: string, backupCodes: string[], error?: string, email?: string) => Promise<Response>;
|
|
50
|
-
/**
|
|
51
|
-
* Custom recovery handler that generates the UI for backup code entry.
|
|
52
|
-
* Called when user wants to use a recovery code instead of TOTP.
|
|
53
|
-
*
|
|
54
|
-
* @param req - The HTTP request object
|
|
55
|
-
* @param error - Optional error message to display
|
|
56
|
-
*/
|
|
57
|
-
recovery: (req: Request, error?: string) => Promise<Response>;
|
|
58
|
-
/**
|
|
59
|
-
* Optional TOTP algorithm. Defaults to SHA1 for maximum compatibility.
|
|
60
|
-
* Most authenticator apps support SHA1, fewer support SHA256/SHA512.
|
|
61
|
-
*/
|
|
62
|
-
algorithm?: "SHA1" | "SHA256" | "SHA512";
|
|
63
|
-
/**
|
|
64
|
-
* Optional number of digits in TOTP codes. Defaults to 6.
|
|
65
|
-
* Some apps support 8 digits for increased security.
|
|
66
|
-
*/
|
|
67
|
-
digits?: 6 | 8;
|
|
68
|
-
/**
|
|
69
|
-
* Optional validity period for TOTP codes in seconds. Defaults to 30.
|
|
70
|
-
* Standard is 30 seconds, some high-security apps use 60.
|
|
71
|
-
*/
|
|
72
|
-
period?: number;
|
|
73
|
-
/**
|
|
74
|
-
* Optional time window tolerance for clock drift. Defaults to 1.
|
|
75
|
-
* Allows tokens from previous/next time window to be valid.
|
|
76
|
-
*/
|
|
77
|
-
window?: number;
|
|
78
|
-
/**
|
|
79
|
-
* Optional number of backup codes to generate. Defaults to 10.
|
|
80
|
-
*/
|
|
81
|
-
backupCodesCount?: number;
|
|
82
|
-
/**
|
|
83
|
-
* Optional function to check if a user is allowed to set up TOTP.
|
|
84
|
-
*/
|
|
85
|
-
userCanSetupTOTP?: (userId: string, req: Request) => Promise<boolean>;
|
|
86
|
-
/**
|
|
87
|
-
* Optional custom label generator for TOTP entries.
|
|
88
|
-
* Defaults to using the userId as the label.
|
|
89
|
-
*/
|
|
90
|
-
generateLabel?: (userId: string) => Promise<string>;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Creates a TOTP (Time-based One-Time Password) authentication provider.
|
|
94
|
-
*
|
|
95
|
-
* TOTP tokens. Users can set up TOTP using any compatible authenticator app
|
|
96
|
-
* and use backup codes when their primary device is unavailable.
|
|
97
|
-
*
|
|
98
|
-
* It handles:
|
|
99
|
-
* - TOTP secret generation and QR code creation
|
|
100
|
-
* - Token verification with timing attack protection
|
|
101
|
-
* - Backup code generation and one-time usage validation
|
|
102
|
-
* - Complete setup, verification, and recovery flows
|
|
103
|
-
*
|
|
104
|
-
* @param config Configuration options for the TOTP provider
|
|
105
|
-
* @returns A Provider instance configured for TOTP authentication
|
|
106
|
-
*/
|
|
107
|
-
declare const TOTPProvider: (config: TOTPProviderConfig) => Provider<{
|
|
108
|
-
email: string;
|
|
109
|
-
method: "totp" | "recovery";
|
|
110
|
-
}>;
|
|
111
|
-
//#endregion
|
|
112
|
-
export { TOTPModel, TOTPProvider, TOTPProviderConfig };
|
package/dist/provider/totp.mjs
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
import { generateSecureToken } from "../random.mjs";
|
|
2
|
-
import { Storage } from "../storage/storage.mjs";
|
|
3
|
-
import { Secret, TOTP } from "otpauth";
|
|
4
|
-
|
|
5
|
-
//#region src/provider/totp.ts
|
|
6
|
-
/**
|
|
7
|
-
* Configures a provider that supports TOTP (Time-based One-Time Password) authentication.
|
|
8
|
-
*
|
|
9
|
-
* ```ts
|
|
10
|
-
* import { TOTPProvider } from "@draftlab/auth/provider/totp"
|
|
11
|
-
*
|
|
12
|
-
* export default issuer({
|
|
13
|
-
* providers: {
|
|
14
|
-
* totp: TOTPProvider({
|
|
15
|
-
* issuer: "My Application",
|
|
16
|
-
* setup: async (req, qrCode, secret, backupCodes) => {
|
|
17
|
-
* return new Response(renderSetupPage(qrCode, secret, backupCodes))
|
|
18
|
-
* },
|
|
19
|
-
* verify: async (req, error) => {
|
|
20
|
-
* return new Response(renderVerifyPage(error))
|
|
21
|
-
* },
|
|
22
|
-
* recovery: async (req, error) => {
|
|
23
|
-
* return new Response(renderRecoveryPage(error))
|
|
24
|
-
* }
|
|
25
|
-
* })
|
|
26
|
-
* },
|
|
27
|
-
* // ...
|
|
28
|
-
* })
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* TOTPProvider implements Time-based One-Time Password authentication.
|
|
32
|
-
* It provides secure TOTP token generation and verification with backup recovery codes.
|
|
33
|
-
*
|
|
34
|
-
* The provider requires configuration of:
|
|
35
|
-
* - Issuer name for authenticator apps
|
|
36
|
-
* - UI handlers for setup, verification, and recovery flows
|
|
37
|
-
* - Optional TOTP parameters (algorithm, digits, period)
|
|
38
|
-
*
|
|
39
|
-
* It automatically manages:
|
|
40
|
-
* - Secure secret generation
|
|
41
|
-
* - QR code URL generation for authenticator apps
|
|
42
|
-
* - Token validation with timing attack protection
|
|
43
|
-
* - Recovery codes generation and one-time usage
|
|
44
|
-
* - Storage of TOTP configuration and backup codes
|
|
45
|
-
*
|
|
46
|
-
* @packageDocumentation
|
|
47
|
-
*/
|
|
48
|
-
const totpKey = (userId) => [
|
|
49
|
-
"totp",
|
|
50
|
-
"user",
|
|
51
|
-
userId
|
|
52
|
-
];
|
|
53
|
-
const DEFAULT_CONFIG = {
|
|
54
|
-
algorithm: "SHA1",
|
|
55
|
-
digits: 6,
|
|
56
|
-
period: 30,
|
|
57
|
-
window: 1,
|
|
58
|
-
backupCodesCount: 4,
|
|
59
|
-
qrSize: 200
|
|
60
|
-
};
|
|
61
|
-
/**
|
|
62
|
-
* Creates a TOTP (Time-based One-Time Password) authentication provider.
|
|
63
|
-
*
|
|
64
|
-
* TOTP tokens. Users can set up TOTP using any compatible authenticator app
|
|
65
|
-
* and use backup codes when their primary device is unavailable.
|
|
66
|
-
*
|
|
67
|
-
* It handles:
|
|
68
|
-
* - TOTP secret generation and QR code creation
|
|
69
|
-
* - Token verification with timing attack protection
|
|
70
|
-
* - Backup code generation and one-time usage validation
|
|
71
|
-
* - Complete setup, verification, and recovery flows
|
|
72
|
-
*
|
|
73
|
-
* @param config Configuration options for the TOTP provider
|
|
74
|
-
* @returns A Provider instance configured for TOTP authentication
|
|
75
|
-
*/
|
|
76
|
-
const TOTPProvider = (config) => {
|
|
77
|
-
const { issuer, algorithm = DEFAULT_CONFIG.algorithm, digits = DEFAULT_CONFIG.digits, period = DEFAULT_CONFIG.period, window = DEFAULT_CONFIG.window, backupCodesCount = DEFAULT_CONFIG.backupCodesCount } = config;
|
|
78
|
-
return {
|
|
79
|
-
type: "totp",
|
|
80
|
-
init(routes, ctx) {
|
|
81
|
-
const getTOTPData = async (userId) => {
|
|
82
|
-
return await Storage.get(ctx.storage, totpKey(userId));
|
|
83
|
-
};
|
|
84
|
-
const saveTOTPData = async (userId, data) => {
|
|
85
|
-
await Storage.set(ctx.storage, totpKey(userId), data);
|
|
86
|
-
};
|
|
87
|
-
const generateBackupCodes = (count) => {
|
|
88
|
-
const codes = [];
|
|
89
|
-
for (let i = 0; i < count; i++) {
|
|
90
|
-
const code = generateSecureToken().slice(0, 8).toUpperCase();
|
|
91
|
-
codes.push(`${code.slice(0, 4)}-${code.slice(4)}`);
|
|
92
|
-
}
|
|
93
|
-
return codes;
|
|
94
|
-
};
|
|
95
|
-
const createTOTPInstance = (secret, label) => {
|
|
96
|
-
return new TOTP({
|
|
97
|
-
issuer,
|
|
98
|
-
label,
|
|
99
|
-
algorithm,
|
|
100
|
-
digits,
|
|
101
|
-
period,
|
|
102
|
-
secret
|
|
103
|
-
});
|
|
104
|
-
};
|
|
105
|
-
routes.get("/register", async (c) => {
|
|
106
|
-
return ctx.forward(c, await config.register(c.request, "", "", []));
|
|
107
|
-
});
|
|
108
|
-
routes.post("/register", async (c) => {
|
|
109
|
-
const formData = await c.formData();
|
|
110
|
-
const email = formData.get("email")?.toString();
|
|
111
|
-
const action = formData.get("action")?.toString();
|
|
112
|
-
if (!email) return ctx.forward(c, await config.register(c.request, "", "", [], "Email is required"));
|
|
113
|
-
if (action === "generate") {
|
|
114
|
-
const secret = new Secret({ size: 20 });
|
|
115
|
-
const label = config.generateLabel ? await config.generateLabel(email) : email;
|
|
116
|
-
const backupCodes = generateBackupCodes(backupCodesCount);
|
|
117
|
-
const qrCodeUrl$1 = createTOTPInstance(secret.base32, label).toString();
|
|
118
|
-
await saveTOTPData(email, {
|
|
119
|
-
secret: secret.base32,
|
|
120
|
-
enabled: false,
|
|
121
|
-
backupCodes,
|
|
122
|
-
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
123
|
-
label
|
|
124
|
-
});
|
|
125
|
-
return ctx.forward(c, await config.register(c.request, qrCodeUrl$1, secret.base32, backupCodes, void 0, email));
|
|
126
|
-
}
|
|
127
|
-
const token = formData.get("token")?.toString();
|
|
128
|
-
if (!token) return ctx.forward(c, await config.register(c.request, "", "", [], "Verification code is required"));
|
|
129
|
-
const totpData = await getTOTPData(email);
|
|
130
|
-
if (!totpData) return ctx.forward(c, await config.register(c.request, "", "", [], "TOTP setup session not found"));
|
|
131
|
-
const totp = createTOTPInstance(totpData.secret, totpData.label || email);
|
|
132
|
-
if (totp.validate({
|
|
133
|
-
token,
|
|
134
|
-
window
|
|
135
|
-
}) !== null) {
|
|
136
|
-
totpData.enabled = true;
|
|
137
|
-
await saveTOTPData(email, totpData);
|
|
138
|
-
return ctx.success(c, {
|
|
139
|
-
email,
|
|
140
|
-
method: "totp"
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
const qrCodeUrl = totp.toString();
|
|
144
|
-
return ctx.forward(c, await config.register(c.request, qrCodeUrl, totpData.secret, totpData.backupCodes, "Invalid verification code. Please try again."));
|
|
145
|
-
});
|
|
146
|
-
routes.get("/authorize", async (c) => {
|
|
147
|
-
return ctx.forward(c, await config.authorize(c.request));
|
|
148
|
-
});
|
|
149
|
-
routes.post("/authorize", async (c) => {
|
|
150
|
-
const formData = await c.formData();
|
|
151
|
-
const email = formData.get("email")?.toString();
|
|
152
|
-
const token = formData.get("token")?.toString();
|
|
153
|
-
if (!email || !token) return ctx.forward(c, await config.authorize(c.request, "Email and verification code are required"));
|
|
154
|
-
const totpData = await getTOTPData(email);
|
|
155
|
-
if (!totpData || !totpData.enabled) return ctx.forward(c, await config.authorize(c.request, "TOTP is not set up for this email"));
|
|
156
|
-
if (createTOTPInstance(totpData.secret, totpData.label || email).validate({
|
|
157
|
-
token,
|
|
158
|
-
window
|
|
159
|
-
}) !== null) return ctx.success(c, {
|
|
160
|
-
email,
|
|
161
|
-
method: "totp"
|
|
162
|
-
});
|
|
163
|
-
return ctx.forward(c, await config.authorize(c.request, "Invalid verification code"));
|
|
164
|
-
});
|
|
165
|
-
routes.get("/recovery", async (c) => {
|
|
166
|
-
return ctx.forward(c, await config.recovery(c.request));
|
|
167
|
-
});
|
|
168
|
-
routes.post("/recovery", async (c) => {
|
|
169
|
-
const formData = await c.formData();
|
|
170
|
-
const email = formData.get("email")?.toString();
|
|
171
|
-
const code = formData.get("code")?.toString()?.toUpperCase();
|
|
172
|
-
if (!email || !code) return ctx.forward(c, await config.recovery(c.request, "Email and recovery code are required"));
|
|
173
|
-
const totpData = await getTOTPData(email);
|
|
174
|
-
if (!totpData || !totpData.enabled) return ctx.forward(c, await config.recovery(c.request, "TOTP is not set up for this email"));
|
|
175
|
-
const codeIndex = totpData.backupCodes.indexOf(code);
|
|
176
|
-
if (codeIndex !== -1) {
|
|
177
|
-
totpData.backupCodes.splice(codeIndex, 1);
|
|
178
|
-
await saveTOTPData(email, totpData);
|
|
179
|
-
return ctx.success(c, {
|
|
180
|
-
email,
|
|
181
|
-
method: "recovery"
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
return ctx.forward(c, await config.recovery(c.request, "Invalid or already used recovery code"));
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
//#endregion
|
|
191
|
-
export { TOTPProvider };
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { Provider } from "./provider.mjs";
|
|
2
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
|
|
3
|
-
|
|
4
|
-
//#region src/provider/twitch.d.ts
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Configuration options for Twitch OAuth 2.0 provider.
|
|
8
|
-
* Extends the base OAuth 2.0 configuration with Twitch-specific documentation.
|
|
9
|
-
*/
|
|
10
|
-
interface TwitchConfig extends Oauth2WrappedConfig {
|
|
11
|
-
/**
|
|
12
|
-
* Twitch application client ID.
|
|
13
|
-
* Get this from your Twitch Console at https://dev.twitch.tv/console
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* {
|
|
18
|
-
* clientID: "abcdef123456"
|
|
19
|
-
* }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
readonly clientID: string;
|
|
23
|
-
/**
|
|
24
|
-
* Twitch application client secret.
|
|
25
|
-
* Keep this secure and never expose it to client-side code.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* {
|
|
30
|
-
* clientSecret: process.env.TWITCH_CLIENT_SECRET
|
|
31
|
-
* }
|
|
32
|
-
* ```
|
|
33
|
-
*/
|
|
34
|
-
readonly clientSecret: string;
|
|
35
|
-
/**
|
|
36
|
-
* Twitch 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
|
-
* "user:read:email", // Access user email
|
|
44
|
-
* "user:read:subscriptions" // View subscriptions
|
|
45
|
-
* ]
|
|
46
|
-
* }
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
readonly scopes: string[];
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Creates a Twitch OAuth 2.0 authentication provider.
|
|
53
|
-
* Allows users to authenticate using their Twitch accounts.
|
|
54
|
-
*
|
|
55
|
-
* @param config - Twitch OAuth 2.0 configuration
|
|
56
|
-
* @returns OAuth 2.0 provider configured for Twitch
|
|
57
|
-
*
|
|
58
|
-
* @example
|
|
59
|
-
* ```ts
|
|
60
|
-
* // Basic Twitch authentication
|
|
61
|
-
* const basicTwitch = TwitchProvider({
|
|
62
|
-
* clientID: process.env.TWITCH_CLIENT_ID,
|
|
63
|
-
* clientSecret: process.env.TWITCH_CLIENT_SECRET
|
|
64
|
-
* })
|
|
65
|
-
*
|
|
66
|
-
* // Twitch with email scope
|
|
67
|
-
* const twitchWithEmail = TwitchProvider({
|
|
68
|
-
* clientID: process.env.TWITCH_CLIENT_ID,
|
|
69
|
-
* clientSecret: process.env.TWITCH_CLIENT_SECRET,
|
|
70
|
-
* scopes: ["user:read:email"]
|
|
71
|
-
* })
|
|
72
|
-
*
|
|
73
|
-
* // Using the access token to fetch user data
|
|
74
|
-
* export default issuer({
|
|
75
|
-
* providers: { twitch: twitchWithEmail },
|
|
76
|
-
* success: async (ctx, value) => {
|
|
77
|
-
* if (value.provider === "twitch") {
|
|
78
|
-
* const token = value.tokenset.access
|
|
79
|
-
*
|
|
80
|
-
* const userRes = await fetch('https://api.twitch.tv/helix/users', {
|
|
81
|
-
* headers: {
|
|
82
|
-
* 'Authorization': `Bearer ${token}`,
|
|
83
|
-
* 'Client-ID': process.env.TWITCH_CLIENT_ID
|
|
84
|
-
* }
|
|
85
|
-
* })
|
|
86
|
-
* const { data } = await userRes.json()
|
|
87
|
-
* const user = data[0]
|
|
88
|
-
*
|
|
89
|
-
* return ctx.subject("user", {
|
|
90
|
-
* twitchId: user.id,
|
|
91
|
-
* login: user.login,
|
|
92
|
-
* email: user.email,
|
|
93
|
-
* displayName: user.display_name
|
|
94
|
-
* })
|
|
95
|
-
* }
|
|
96
|
-
* }
|
|
97
|
-
* })
|
|
98
|
-
* ```
|
|
99
|
-
*
|
|
100
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
101
|
-
* - Development: `http://localhost:3000/auth/twitch/callback`
|
|
102
|
-
* - Production: `https://yourapp.com/auth/twitch/callback`
|
|
103
|
-
*
|
|
104
|
-
* Register this URL in your Twitch Developer Console.
|
|
105
|
-
*/
|
|
106
|
-
declare const TwitchProvider: (config: TwitchConfig) => Provider<Oauth2UserData>;
|
|
107
|
-
//#endregion
|
|
108
|
-
export { TwitchConfig, TwitchProvider };
|
package/dist/provider/twitch.mjs
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { Oauth2Provider } from "./oauth2.mjs";
|
|
2
|
-
|
|
3
|
-
//#region src/provider/twitch.ts
|
|
4
|
-
/**
|
|
5
|
-
* Twitch authentication provider for Draft Auth.
|
|
6
|
-
* Implements OAuth 2.0 flow for authenticating users with their Twitch accounts.
|
|
7
|
-
*
|
|
8
|
-
* ## Quick Setup
|
|
9
|
-
*
|
|
10
|
-
* ```ts
|
|
11
|
-
* import { TwitchProvider } from "@draftlab/auth/provider/twitch"
|
|
12
|
-
*
|
|
13
|
-
* export default issuer({
|
|
14
|
-
* basePath: "/auth", // Important for callback URL
|
|
15
|
-
* providers: {
|
|
16
|
-
* twitch: TwitchProvider({
|
|
17
|
-
* clientID: process.env.TWITCH_CLIENT_ID,
|
|
18
|
-
* clientSecret: process.env.TWITCH_CLIENT_SECRET,
|
|
19
|
-
* scopes: ["user:read:email"]
|
|
20
|
-
* })
|
|
21
|
-
* }
|
|
22
|
-
* })
|
|
23
|
-
* ```
|
|
24
|
-
*
|
|
25
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
26
|
-
* - Development: `http://localhost:3000/auth/twitch/callback`
|
|
27
|
-
* - Production: `https://yourapp.com/auth/twitch/callback`
|
|
28
|
-
*
|
|
29
|
-
* Register this URL in your Twitch Developer Console.
|
|
30
|
-
*
|
|
31
|
-
* ## Common Scopes
|
|
32
|
-
*
|
|
33
|
-
* - `user:read:email` - Access user's email address
|
|
34
|
-
* - `user:read:subscriptions` - View user subscriptions
|
|
35
|
-
* - `user:read:follows` - View user's follows
|
|
36
|
-
* - `channel:read:subscriptions` - View channel subscribers
|
|
37
|
-
* - `analytics:read:games` - View game analytics
|
|
38
|
-
* - `bits:read` - View bits information
|
|
39
|
-
*
|
|
40
|
-
* ## User Data Access
|
|
41
|
-
*
|
|
42
|
-
* ```ts
|
|
43
|
-
* success: async (ctx, value) => {
|
|
44
|
-
* if (value.provider === "twitch") {
|
|
45
|
-
* const accessToken = value.tokenset.access
|
|
46
|
-
*
|
|
47
|
-
* // Fetch user information
|
|
48
|
-
* const userResponse = await fetch('https://api.twitch.tv/helix/users', {
|
|
49
|
-
* headers: {
|
|
50
|
-
* 'Authorization': `Bearer ${accessToken}`,
|
|
51
|
-
* 'Client-ID': process.env.TWITCH_CLIENT_ID
|
|
52
|
-
* }
|
|
53
|
-
* })
|
|
54
|
-
* const { data } = await userResponse.json()
|
|
55
|
-
* const user = data[0]
|
|
56
|
-
*
|
|
57
|
-
* // User info available: id, login, display_name, email, profile_image_url
|
|
58
|
-
* }
|
|
59
|
-
* }
|
|
60
|
-
* ```
|
|
61
|
-
*
|
|
62
|
-
* @packageDocumentation
|
|
63
|
-
*/
|
|
64
|
-
/**
|
|
65
|
-
* Creates a Twitch OAuth 2.0 authentication provider.
|
|
66
|
-
* Allows users to authenticate using their Twitch accounts.
|
|
67
|
-
*
|
|
68
|
-
* @param config - Twitch OAuth 2.0 configuration
|
|
69
|
-
* @returns OAuth 2.0 provider configured for Twitch
|
|
70
|
-
*
|
|
71
|
-
* @example
|
|
72
|
-
* ```ts
|
|
73
|
-
* // Basic Twitch authentication
|
|
74
|
-
* const basicTwitch = TwitchProvider({
|
|
75
|
-
* clientID: process.env.TWITCH_CLIENT_ID,
|
|
76
|
-
* clientSecret: process.env.TWITCH_CLIENT_SECRET
|
|
77
|
-
* })
|
|
78
|
-
*
|
|
79
|
-
* // Twitch with email scope
|
|
80
|
-
* const twitchWithEmail = TwitchProvider({
|
|
81
|
-
* clientID: process.env.TWITCH_CLIENT_ID,
|
|
82
|
-
* clientSecret: process.env.TWITCH_CLIENT_SECRET,
|
|
83
|
-
* scopes: ["user:read:email"]
|
|
84
|
-
* })
|
|
85
|
-
*
|
|
86
|
-
* // Using the access token to fetch user data
|
|
87
|
-
* export default issuer({
|
|
88
|
-
* providers: { twitch: twitchWithEmail },
|
|
89
|
-
* success: async (ctx, value) => {
|
|
90
|
-
* if (value.provider === "twitch") {
|
|
91
|
-
* const token = value.tokenset.access
|
|
92
|
-
*
|
|
93
|
-
* const userRes = await fetch('https://api.twitch.tv/helix/users', {
|
|
94
|
-
* headers: {
|
|
95
|
-
* 'Authorization': `Bearer ${token}`,
|
|
96
|
-
* 'Client-ID': process.env.TWITCH_CLIENT_ID
|
|
97
|
-
* }
|
|
98
|
-
* })
|
|
99
|
-
* const { data } = await userRes.json()
|
|
100
|
-
* const user = data[0]
|
|
101
|
-
*
|
|
102
|
-
* return ctx.subject("user", {
|
|
103
|
-
* twitchId: user.id,
|
|
104
|
-
* login: user.login,
|
|
105
|
-
* email: user.email,
|
|
106
|
-
* displayName: user.display_name
|
|
107
|
-
* })
|
|
108
|
-
* }
|
|
109
|
-
* }
|
|
110
|
-
* })
|
|
111
|
-
* ```
|
|
112
|
-
*
|
|
113
|
-
* **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
|
|
114
|
-
* - Development: `http://localhost:3000/auth/twitch/callback`
|
|
115
|
-
* - Production: `https://yourapp.com/auth/twitch/callback`
|
|
116
|
-
*
|
|
117
|
-
* Register this URL in your Twitch Developer Console.
|
|
118
|
-
*/
|
|
119
|
-
const TwitchProvider = (config) => {
|
|
120
|
-
return Oauth2Provider({
|
|
121
|
-
...config,
|
|
122
|
-
type: "twitch",
|
|
123
|
-
endpoint: {
|
|
124
|
-
authorization: "https://id.twitch.tv/oauth2/authorize",
|
|
125
|
-
token: "https://id.twitch.tv/oauth2/token"
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
//#endregion
|
|
131
|
-
export { TwitchProvider };
|