@draftlab/auth 0.9.0 → 0.10.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.
@@ -99,6 +99,12 @@ interface AppleConfig extends Oauth2WrappedConfig {
99
99
  * }
100
100
  * })
101
101
  * ```
102
+ *
103
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
104
+ * - Development: `http://localhost:3000/auth/apple/callback`
105
+ * - Production: `https://yourapp.com/auth/apple/callback`
106
+ *
107
+ * Register this URL in your Apple Developer Portal.
102
108
  */
103
109
  declare const AppleProvider: (config: AppleConfig) => Provider<Oauth2UserData>;
104
110
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * apple: AppleProvider({
16
17
  * clientID: process.env.APPLE_CLIENT_ID,
17
18
  * clientSecret: process.env.APPLE_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/apple/callback`
27
+ * - Production: `https://yourapp.com/auth/apple/callback`
28
+ *
29
+ * Register this URL in your Apple Developer Portal.
30
+ *
24
31
  * ## Setup Instructions
25
32
  *
26
33
  * ### 1. Create App ID
@@ -135,6 +142,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
135
142
  * }
136
143
  * })
137
144
  * ```
145
+ *
146
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
147
+ * - Development: `http://localhost:3000/auth/apple/callback`
148
+ * - Production: `https://yourapp.com/auth/apple/callback`
149
+ *
150
+ * Register this URL in your Apple Developer Portal.
138
151
  */
139
152
  const AppleProvider = (config) => {
140
153
  return Oauth2Provider({
@@ -134,6 +134,12 @@ interface DiscordConfig extends Oauth2WrappedConfig {
134
134
  * }
135
135
  * })
136
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.
137
143
  */
138
144
  declare const DiscordProvider: (config: DiscordConfig) => Provider<Oauth2UserData>;
139
145
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * discord: DiscordProvider({
16
17
  * clientID: process.env.DISCORD_CLIENT_ID,
17
18
  * clientSecret: process.env.DISCORD_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
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
+ *
24
31
  * ## Common Scopes
25
32
  *
26
33
  * - `identify` - Access to user's basic account information
@@ -127,6 +134,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
127
134
  * }
128
135
  * })
129
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.
130
143
  */
131
144
  const DiscordProvider = (config) => {
132
145
  return Oauth2Provider({
@@ -130,6 +130,12 @@ interface FacebookConfig extends Oauth2WrappedConfig {
130
130
  * }
131
131
  * })
132
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.
133
139
  */
134
140
  declare const FacebookProvider: (config: FacebookConfig) => Provider<Oauth2UserData>;
135
141
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * facebook: FacebookProvider({
16
17
  * clientID: process.env.FACEBOOK_APP_ID,
17
18
  * clientSecret: process.env.FACEBOOK_APP_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/facebook/callback`
27
+ * - Production: `https://yourapp.com/auth/facebook/callback`
28
+ *
29
+ * Register this URL in your Facebook App Dashboard.
30
+ *
24
31
  * ## Configuration Options
25
32
  *
26
33
  * - Access tokens for Facebook Graph API calls
@@ -121,6 +128,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
121
128
  * }
122
129
  * })
123
130
  * ```
131
+ *
132
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
133
+ * - Development: `http://localhost:3000/auth/facebook/callback`
134
+ * - Production: `https://yourapp.com/auth/facebook/callback`
135
+ *
136
+ * Register this URL in your Facebook App Dashboard.
124
137
  */
125
138
  const FacebookProvider = (config) => {
126
139
  return Oauth2Provider({
@@ -11,6 +11,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
11
11
  * import { GithubProvider } from "@draftlab/auth/provider/github"
12
12
  *
13
13
  * export default issuer({
14
+ * basePath: "/auth", // Important for callback URL
14
15
  * providers: {
15
16
  * github: GithubProvider({
16
17
  * clientID: process.env.GITHUB_CLIENT_ID,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/github/callback`
27
+ * - Production: `https://yourapp.com/auth/github/callback`
28
+ *
29
+ * Register this URL in your GitHub App/OAuth App settings.
30
+ *
24
31
  * ## GitHub App vs OAuth App
25
32
  *
26
33
  * This provider works with both GitHub OAuth Apps and GitHub Apps:
@@ -94,6 +94,12 @@ interface GitlabConfig extends Oauth2WrappedConfig {
94
94
  * }
95
95
  * })
96
96
  * ```
97
+ *
98
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
99
+ * - Development: `http://localhost:3000/auth/gitlab/callback`
100
+ * - Production: `https://yourapp.com/auth/gitlab/callback`
101
+ *
102
+ * Register this URL in your GitLab Application settings.
97
103
  */
98
104
  declare const GitlabProvider: (config: GitlabConfig) => Provider<Oauth2UserData>;
99
105
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * gitlab: GitlabProvider({
16
17
  * clientID: process.env.GITLAB_CLIENT_ID,
17
18
  * clientSecret: process.env.GITLAB_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/gitlab/callback`
27
+ * - Production: `https://yourapp.com/auth/gitlab/callback`
28
+ *
29
+ * Register this URL in your GitLab Application settings.
30
+ *
24
31
  * ## Common Scopes
25
32
  *
26
33
  * - `read_user` - Access user profile
@@ -47,6 +54,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
47
54
  * })
48
55
  * ```
49
56
  *
57
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
58
+ * - Development: `http://localhost:3000/auth/gitlab/callback`
59
+ * - Production: `https://yourapp.com/auth/gitlab/callback`
60
+ *
61
+ * Register this URL in your GitLab Application settings.
62
+ *
50
63
  * ## User Data Access
51
64
  *
52
65
  * ```ts
@@ -112,6 +125,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
112
125
  * }
113
126
  * })
114
127
  * ```
128
+ *
129
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
130
+ * - Development: `http://localhost:3000/auth/gitlab/callback`
131
+ * - Production: `https://yourapp.com/auth/gitlab/callback`
132
+ *
133
+ * Register this URL in your GitLab Application settings.
115
134
  */
116
135
  const GitlabProvider = (config) => {
117
136
  return Oauth2Provider({
@@ -11,6 +11,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
11
11
  * import { GoogleProvider } from "@draftlab/auth/provider/google"
12
12
  *
13
13
  * export default issuer({
14
+ * basePath: "/auth", // Important for callback URL
14
15
  * providers: {
15
16
  * google: GoogleProvider({
16
17
  * clientID: process.env.GOOGLE_CLIENT_ID,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/google/callback`
27
+ * - Production: `https://yourapp.com/auth/google/callback`
28
+ *
29
+ * Register this URL in your Google Cloud Console OAuth 2.0 credentials.
30
+ *
24
31
  * ## Configuration Options
25
32
  *
26
33
  * - Access tokens for Google API calls
@@ -120,6 +120,12 @@ interface LinkedInConfig extends Oauth2WrappedConfig {
120
120
  * }
121
121
  * })
122
122
  * ```
123
+ *
124
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
125
+ * - Development: `http://localhost:3000/auth/linkedin/callback`
126
+ * - Production: `https://yourapp.com/auth/linkedin/callback`
127
+ *
128
+ * Register this URL in your LinkedIn Developer Portal.
123
129
  */
124
130
  declare const LinkedInProvider: (config: LinkedInConfig) => Provider<Oauth2UserData>;
125
131
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * linkedin: LinkedInProvider({
16
17
  * clientID: process.env.LINKEDIN_CLIENT_ID,
17
18
  * clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/linkedin/callback`
27
+ * - Production: `https://yourapp.com/auth/linkedin/callback`
28
+ *
29
+ * Register this URL in your LinkedIn Developer Portal.
30
+ *
24
31
  * ## Common Scopes
25
32
  *
26
33
  * - `r_liteprofile` - Access to basic profile information
@@ -113,6 +120,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
113
120
  * }
114
121
  * })
115
122
  * ```
123
+ *
124
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
125
+ * - Development: `http://localhost:3000/auth/linkedin/callback`
126
+ * - Production: `https://yourapp.com/auth/linkedin/callback`
127
+ *
128
+ * Register this URL in your LinkedIn Developer Portal.
116
129
  */
117
130
  const LinkedInProvider = (config) => {
118
131
  return Oauth2Provider({
@@ -166,6 +166,12 @@ interface MicrosoftConfig extends Oauth2WrappedConfig {
166
166
  * }
167
167
  * })
168
168
  * ```
169
+ *
170
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
171
+ * - Development: `http://localhost:3000/auth/microsoft/callback`
172
+ * - Production: `https://yourapp.com/auth/microsoft/callback`
173
+ *
174
+ * Register this URL in your Azure Portal App Registration.
169
175
  */
170
176
  declare const MicrosoftProvider: (config: MicrosoftConfig) => Provider<Oauth2UserData>;
171
177
  //#endregion
@@ -13,6 +13,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
13
13
  *
14
14
  * export default issuer({
15
15
  * providers: {
16
+ * basePath: "/auth", // Important for callback URL
16
17
  * microsoft: MicrosoftProvider({
17
18
  * tenant: "common", // or specific tenant ID
18
19
  * clientID: process.env.MICROSOFT_CLIENT_ID,
@@ -23,6 +24,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
23
24
  * })
24
25
  * ```
25
26
  *
27
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
28
+ * - Development: `http://localhost:3000/auth/microsoft/callback`
29
+ * - Production: `https://yourapp.com/auth/microsoft/callback`
30
+ *
31
+ * Register this URL in your Azure Portal App Registration.
32
+ *
26
33
  * ## Tenant Configuration
27
34
  *
28
35
  * - `common` - Both personal and work/school accounts
@@ -148,6 +155,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
148
155
  * }
149
156
  * })
150
157
  * ```
158
+ *
159
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
160
+ * - Development: `http://localhost:3000/auth/microsoft/callback`
161
+ * - Production: `https://yourapp.com/auth/microsoft/callback`
162
+ *
163
+ * Register this URL in your Azure Portal App Registration.
151
164
  */
152
165
  const MicrosoftProvider = (config) => {
153
166
  return Oauth2Provider({
@@ -73,7 +73,10 @@ const Oauth2Provider = (config) => {
73
73
  if (tokenData.error) throw new OauthError(tokenData.error, tokenData.error_description || "");
74
74
  if (tokenData.id_token && config.endpoint.jwks) try {
75
75
  const jwks = createRemoteJWKSet(new URL(config.endpoint.jwks));
76
- await jwtVerify(tokenData.id_token, jwks, { issuer: config.endpoint.authorization.split("/").slice(0, 3).join("/") });
76
+ await jwtVerify(tokenData.id_token, jwks, {
77
+ issuer: config.endpoint.authorization.split("/").slice(0, 3).join("/"),
78
+ clockTolerance: 60
79
+ });
77
80
  } catch (error) {
78
81
  throw new OauthError("invalid_request", `ID token validation failed: ${error instanceof Error ? error.message : "Unknown error"}`);
79
82
  }
@@ -95,6 +95,12 @@ interface RedditConfig extends Oauth2WrappedConfig {
95
95
  * }
96
96
  * })
97
97
  * ```
98
+ *
99
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
100
+ * - Development: `http://localhost:3000/auth/reddit/callback`
101
+ * - Production: `https://yourapp.com/auth/reddit/callback`
102
+ *
103
+ * Register this URL in your Reddit App Preferences.
98
104
  */
99
105
  declare const RedditProvider: (config: RedditConfig) => Provider<Oauth2UserData>;
100
106
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * reddit: RedditProvider({
16
17
  * clientID: process.env.REDDIT_CLIENT_ID,
17
18
  * clientSecret: process.env.REDDIT_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/reddit/callback`
27
+ * - Production: `https://yourapp.com/auth/reddit/callback`
28
+ *
29
+ * Register this URL in your Reddit App Preferences.
30
+ *
24
31
  * ## Common Scopes
25
32
  *
26
33
  * - `identity` - Access user's identity information
@@ -98,6 +105,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
98
105
  * }
99
106
  * })
100
107
  * ```
108
+ *
109
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
110
+ * - Development: `http://localhost:3000/auth/reddit/callback`
111
+ * - Production: `https://yourapp.com/auth/reddit/callback`
112
+ *
113
+ * Register this URL in your Reddit App Preferences.
101
114
  */
102
115
  const RedditProvider = (config) => {
103
116
  return Oauth2Provider({
@@ -102,6 +102,12 @@ interface SlackConfig extends Oauth2WrappedConfig {
102
102
  * }
103
103
  * })
104
104
  * ```
105
+ *
106
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
107
+ * - Development: `http://localhost:3000/auth/slack/callback`
108
+ * - Production: `https://yourapp.com/auth/slack/callback`
109
+ *
110
+ * Register this URL in your Slack App settings.
105
111
  */
106
112
  declare const SlackProvider: (config: SlackConfig) => Provider<Oauth2UserData>;
107
113
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * slack: SlackProvider({
16
17
  * clientID: process.env.SLACK_CLIENT_ID,
17
18
  * clientSecret: process.env.SLACK_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
23
24
  *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Development: `http://localhost:3000/auth/slack/callback`
27
+ * - Production: `https://yourapp.com/auth/slack/callback`
28
+ *
29
+ * Register this URL in your Slack App settings.
30
+ *
24
31
  * ## Common Scopes
25
32
  *
26
33
  * - `users:read` - Access to user profiles
@@ -109,6 +116,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
109
116
  * }
110
117
  * })
111
118
  * ```
119
+ *
120
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
121
+ * - Development: `http://localhost:3000/auth/slack/callback`
122
+ * - Production: `https://yourapp.com/auth/slack/callback`
123
+ *
124
+ * Register this URL in your Slack App settings.
112
125
  */
113
126
  const SlackProvider = (config) => {
114
127
  return Oauth2Provider({
@@ -101,6 +101,12 @@ interface SpotifyConfig extends Oauth2WrappedConfig {
101
101
  * }
102
102
  * })
103
103
  * ```
104
+ *
105
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
106
+ * - Development: `http://localhost:3000/auth/spotify/callback`
107
+ * - Production: `https://yourapp.com/auth/spotify/callback`
108
+ *
109
+ * Register this URL in your Spotify Developer Dashboard.
104
110
  */
105
111
  declare const SpotifyProvider: (config: SpotifyConfig) => Provider<Oauth2UserData>;
106
112
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * spotify: SpotifyProvider({
16
17
  * clientID: process.env.SPOTIFY_CLIENT_ID,
17
18
  * clientSecret: process.env.SPOTIFY_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
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
+ *
24
31
  * ## Common Scopes
25
32
  *
26
33
  * - `user-read-private` - Access user's private data
@@ -106,6 +113,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
106
113
  * }
107
114
  * })
108
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.
109
122
  */
110
123
  const SpotifyProvider = (config) => {
111
124
  return Oauth2Provider({
@@ -96,6 +96,12 @@ interface TwitchConfig extends Oauth2WrappedConfig {
96
96
  * }
97
97
  * })
98
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.
99
105
  */
100
106
  declare const TwitchProvider: (config: TwitchConfig) => Provider<Oauth2UserData>;
101
107
  //#endregion
@@ -12,6 +12,7 @@ import { Oauth2Provider } from "./oauth2.mjs";
12
12
  *
13
13
  * export default issuer({
14
14
  * providers: {
15
+ * basePath: "/auth", // Important for callback URL
15
16
  * twitch: TwitchProvider({
16
17
  * clientID: process.env.TWITCH_CLIENT_ID,
17
18
  * clientSecret: process.env.TWITCH_CLIENT_SECRET,
@@ -21,6 +22,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
21
22
  * })
22
23
  * ```
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
+ *
24
31
  * ## Common Scopes
25
32
  *
26
33
  * - `user:read:email` - Access user's email address
@@ -102,6 +109,12 @@ import { Oauth2Provider } from "./oauth2.mjs";
102
109
  * }
103
110
  * })
104
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.
105
118
  */
106
119
  const TwitchProvider = (config) => {
107
120
  return Oauth2Provider({
@@ -0,0 +1,177 @@
1
+ import { Provider } from "./provider.mjs";
2
+ import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
3
+
4
+ //#region src/provider/vercel.d.ts
5
+
6
+ /**
7
+ * Configuration options for Vercel OAuth 2.0 + OpenID Connect provider.
8
+ * Extends the base OAuth 2.0 configuration with Vercel-specific documentation.
9
+ */
10
+ interface VercelConfig extends Oauth2WrappedConfig {
11
+ /**
12
+ * Vercel OAuth App client ID.
13
+ * Found in your Vercel App settings under the Authentication tab.
14
+ *
15
+ * To create an app:
16
+ * 1. Go to Team Settings → Apps → Create
17
+ * 2. Configure app details and callback URLs
18
+ * 3. Copy the Client ID from the Authentication tab
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * {
23
+ * clientID: "oac_abc123xyz789" // Vercel OAuth App Client ID
24
+ * }
25
+ * ```
26
+ */
27
+ readonly clientID: string;
28
+ /**
29
+ * Vercel OAuth App client secret.
30
+ * Generated in your Vercel App settings under the Authentication tab.
31
+ * Keep this secure and never expose it to client-side code.
32
+ *
33
+ * To generate:
34
+ * 1. Go to your app's Authentication tab
35
+ * 2. Click "Generate Client Secret"
36
+ * 3. Copy and store securely (shown only once)
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * {
41
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET
42
+ * }
43
+ * ```
44
+ */
45
+ readonly clientSecret: string;
46
+ /**
47
+ * OpenID Connect scopes to request.
48
+ * Controls what user information is included in the ID Token.
49
+ *
50
+ * Available scopes (must be enabled in Vercel App dashboard first):
51
+ * - `openid`: Required for ID Token issuance
52
+ * - `email`: User's email address
53
+ * - `profile`: Name, username, and avatar
54
+ * - `offline_access`: Refresh token for long-lived access (optional)
55
+ *
56
+ * **Important**: Enable scopes in: Vercel App → Permissions page
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * {
61
+ * // Basic scopes (usually sufficient)
62
+ * scopes: ["openid", "email", "profile"]
63
+ *
64
+ * // With refresh token support (enable offline_access in dashboard first)
65
+ * scopes: ["openid", "email", "profile", "offline_access"]
66
+ * }
67
+ * ```
68
+ */
69
+ readonly scopes: string[];
70
+ /**
71
+ * Additional query parameters for Vercel OAuth authorization.
72
+ * Useful for customizing the authorization flow.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * {
77
+ * query: {
78
+ * prompt: "consent" // Force consent screen every time
79
+ * }
80
+ * }
81
+ * ```
82
+ */
83
+ readonly query?: Record<string, string>;
84
+ }
85
+ /**
86
+ * Creates a Vercel OAuth 2.0 + OpenID Connect authentication provider.
87
+ * Implements "Sign in with Vercel" for user authentication.
88
+ *
89
+ * This provider uses the standard OAuth 2.0 Authorization Code Grant flow
90
+ * with PKCE (Proof Key for Code Exchange) for enhanced security.
91
+ *
92
+ * @param config - Vercel OAuth 2.0 configuration
93
+ * @returns OAuth 2.0 provider configured for Vercel
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * // Basic Vercel authentication (email + profile)
98
+ * const basicVercel = VercelProvider({
99
+ * clientID: process.env.VERCEL_CLIENT_ID,
100
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET,
101
+ * scopes: ["openid", "email", "profile"]
102
+ * })
103
+ *
104
+ * // Vercel with refresh token support
105
+ * const vercelWithRefresh = VercelProvider({
106
+ * clientID: process.env.VERCEL_CLIENT_ID,
107
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET,
108
+ * scopes: ["openid", "email", "profile", "offline_access"]
109
+ * })
110
+ *
111
+ * // Minimal setup (only user ID in ID Token)
112
+ * const minimalVercel = VercelProvider({
113
+ * clientID: process.env.VERCEL_CLIENT_ID,
114
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET,
115
+ * scopes: ["openid"] // Only sub claim in ID Token
116
+ * })
117
+ *
118
+ * // Using the tokens in your app
119
+ * export default issuer({
120
+ * providers: { vercel: vercelWithRefresh },
121
+ * success: async (ctx, value) => {
122
+ * if (value.provider === "vercel") {
123
+ * const idToken = value.tokenset.raw.id_token as string | undefined
124
+ * const accessToken = value.tokenset.access
125
+ * const refreshToken = value.tokenset.refresh
126
+ *
127
+ * if (idToken) {
128
+ * // Decode ID Token to access user claims
129
+ * // (Already validated by oauth2.ts - signature, issuer, audience, exp)
130
+ * const claims = JSON.parse(
131
+ * Buffer.from(idToken.split('.')[1], 'base64').toString()
132
+ * )
133
+ *
134
+ * // Claims available (depending on scopes):
135
+ * // - sub: Vercel user ID (always present)
136
+ * // - email: user@example.com (if email scope)
137
+ * // - name: "John Doe" (if profile scope)
138
+ * // - picture: "https://..." (if profile scope)
139
+ * // - preferred_username: "johndoe" (if profile scope)
140
+ *
141
+ * // Optionally call Vercel API for more data
142
+ * const userRes = await fetch('https://api.vercel.com/v2/user', {
143
+ * headers: { Authorization: `Bearer ${accessToken}` }
144
+ * })
145
+ * const user = await userRes.json()
146
+ *
147
+ * // Get user's teams
148
+ * const teamsRes = await fetch('https://api.vercel.com/v2/teams', {
149
+ * headers: { Authorization: `Bearer ${accessToken}` }
150
+ * })
151
+ * const teams = await teamsRes.json()
152
+ *
153
+ * return ctx.subject("user", {
154
+ * vercelId: claims.sub,
155
+ * email: claims.email,
156
+ * name: claims.name,
157
+ * username: claims.preferred_username,
158
+ * avatar: claims.picture,
159
+ * teamCount: teams.teams?.length || 0
160
+ * })
161
+ * }
162
+ * }
163
+ * }
164
+ * })
165
+ * ```
166
+ *
167
+ * @remarks
168
+ * - Requires creating a Vercel App in Team Settings → Apps
169
+ * - PKCE is enabled by default for enhanced security
170
+ * - ID Token is automatically validated (signature, issuer, audience, expiration)
171
+ * - Access tokens expire after 1 hour
172
+ * - Refresh tokens rotate on each use and last 30 days
173
+ * - The `openid` scope is required for ID Token issuance
174
+ */
175
+ declare const VercelProvider: (config: VercelConfig) => Provider<Oauth2UserData>;
176
+ //#endregion
177
+ export { VercelConfig, VercelProvider };
@@ -0,0 +1,230 @@
1
+ import { Oauth2Provider } from "./oauth2.mjs";
2
+
3
+ //#region src/provider/vercel.ts
4
+ /**
5
+ * Vercel OAuth 2.0 + OpenID Connect authentication provider for Draft Auth.
6
+ * Implements "Sign in with Vercel" for user authentication.
7
+ *
8
+ * ## Quick Setup
9
+ *
10
+ * ```ts
11
+ * import { VercelProvider } from "@draftlab/auth/provider/vercel"
12
+ *
13
+ * export default issuer({
14
+ * basePath: "/auth", // Important for callback URL
15
+ * providers: {
16
+ * vercel: VercelProvider({
17
+ * clientID: process.env.VERCEL_CLIENT_ID,
18
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET,
19
+ * scopes: ["openid", "email", "profile"]
20
+ * })
21
+ * }
22
+ * })
23
+ * ```
24
+ *
25
+ * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
+ * - Example: `http://localhost:3000/auth/vercel/callback`
27
+ * - Production: `https://yourapp.com/auth/vercel/callback`
28
+ *
29
+ * ## Creating a Vercel App
30
+ *
31
+ * Before using this provider, create a Vercel App in your dashboard:
32
+ *
33
+ * 1. Go to **Team Settings** → **Apps** → **Create**
34
+ * 2. Fill in app name, description, and logo
35
+ * 3. Add **Authorization Callback URLs**:
36
+ * - Development: `http://localhost:3000/auth/vercel/callback`
37
+ * - Production: `https://yourapp.com/auth/vercel/callback`
38
+ * - Pattern: `{baseURL}{basePath}/{provider}/callback`
39
+ * 4. Configure **Scopes** in the app's Permissions page:
40
+ * - ✅ openid (Required)
41
+ * - ✅ email
42
+ * - ✅ profile
43
+ * - ✅ offline_access (optional, for refresh tokens)
44
+ * 5. Generate a **Client Secret** in the Authentication tab
45
+ * 6. Copy the **Client ID** and **Client Secret**
46
+ *
47
+ * **Important**: You must enable the scopes in the Vercel App dashboard before requesting them!
48
+ *
49
+ * ## Available Scopes
50
+ *
51
+ * - `openid` - **Required**. Enables ID Token issuance for user identification
52
+ * - `email` - Access user's email address in ID Token
53
+ * - `profile` - Access user's name, username, and avatar in ID Token
54
+ * - `offline_access` - Issue a Refresh Token for long-lived access (30 days)
55
+ *
56
+ * ## Tokens Returned
57
+ *
58
+ * - **ID Token**: Signed JWT with user identity claims (verified automatically)
59
+ * - **Access Token**: Bearer token for Vercel API calls (1 hour duration)
60
+ * - **Refresh Token**: Rotates on each use (30 days, requires offline_access scope)
61
+ *
62
+ * ## User Data Access
63
+ *
64
+ * ```ts
65
+ * success: async (ctx, value) => {
66
+ * if (value.provider === "vercel") {
67
+ * // ID Token is automatically validated (signature, issuer, audience, expiration)
68
+ * const idToken = value.tokenset.raw.id_token as string | undefined
69
+ * const accessToken = value.tokenset.access
70
+ * const refreshToken = value.tokenset.refresh
71
+ *
72
+ * // Decode ID Token to access user claims
73
+ * if (idToken) {
74
+ * const claims = JSON.parse(
75
+ * Buffer.from(idToken.split('.')[1], 'base64').toString()
76
+ * )
77
+ *
78
+ * // Claims available (depending on scopes):
79
+ * // - sub: Unique Vercel user ID (always present)
80
+ * // - email: User's email (if email scope granted)
81
+ * // - name: User's full name (if profile scope granted)
82
+ * // - picture: Avatar URL (if profile scope granted)
83
+ * // - preferred_username: Vercel username (if profile scope granted)
84
+ *
85
+ * return ctx.subject("user", {
86
+ * email: claims.email || claims.sub
87
+ * })
88
+ * }
89
+ * }
90
+ * }
91
+ * ```
92
+ *
93
+ * ## Calling Vercel API
94
+ *
95
+ * Use the access token to call Vercel's REST API:
96
+ *
97
+ * ```ts
98
+ * // Get user information
99
+ * const userRes = await fetch('https://api.vercel.com/v2/user', {
100
+ * headers: { Authorization: `Bearer ${accessToken}` }
101
+ * })
102
+ *
103
+ * // Get user's teams
104
+ * const teamsRes = await fetch('https://api.vercel.com/v2/teams', {
105
+ * headers: { Authorization: `Bearer ${accessToken}` }
106
+ * })
107
+ *
108
+ * // Get projects (requires appropriate permissions)
109
+ * const projectsRes = await fetch('https://api.vercel.com/v9/projects', {
110
+ * headers: { Authorization: `Bearer ${accessToken}` }
111
+ * })
112
+ * ```
113
+ *
114
+ * ## Consent Page
115
+ *
116
+ * The first time a user signs in, Vercel shows a consent page with:
117
+ * - Your app's name and logo
118
+ * - Requested scopes and permissions
119
+ * - Allow/Cancel buttons
120
+ *
121
+ * If the user grants access, they're redirected back with an authorization code.
122
+ * If they cancel, they're redirected with an error parameter.
123
+ *
124
+ * @packageDocumentation
125
+ */
126
+ /**
127
+ * Creates a Vercel OAuth 2.0 + OpenID Connect authentication provider.
128
+ * Implements "Sign in with Vercel" for user authentication.
129
+ *
130
+ * This provider uses the standard OAuth 2.0 Authorization Code Grant flow
131
+ * with PKCE (Proof Key for Code Exchange) for enhanced security.
132
+ *
133
+ * @param config - Vercel OAuth 2.0 configuration
134
+ * @returns OAuth 2.0 provider configured for Vercel
135
+ *
136
+ * @example
137
+ * ```ts
138
+ * // Basic Vercel authentication (email + profile)
139
+ * const basicVercel = VercelProvider({
140
+ * clientID: process.env.VERCEL_CLIENT_ID,
141
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET,
142
+ * scopes: ["openid", "email", "profile"]
143
+ * })
144
+ *
145
+ * // Vercel with refresh token support
146
+ * const vercelWithRefresh = VercelProvider({
147
+ * clientID: process.env.VERCEL_CLIENT_ID,
148
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET,
149
+ * scopes: ["openid", "email", "profile", "offline_access"]
150
+ * })
151
+ *
152
+ * // Minimal setup (only user ID in ID Token)
153
+ * const minimalVercel = VercelProvider({
154
+ * clientID: process.env.VERCEL_CLIENT_ID,
155
+ * clientSecret: process.env.VERCEL_CLIENT_SECRET,
156
+ * scopes: ["openid"] // Only sub claim in ID Token
157
+ * })
158
+ *
159
+ * // Using the tokens in your app
160
+ * export default issuer({
161
+ * providers: { vercel: vercelWithRefresh },
162
+ * success: async (ctx, value) => {
163
+ * if (value.provider === "vercel") {
164
+ * const idToken = value.tokenset.raw.id_token as string | undefined
165
+ * const accessToken = value.tokenset.access
166
+ * const refreshToken = value.tokenset.refresh
167
+ *
168
+ * if (idToken) {
169
+ * // Decode ID Token to access user claims
170
+ * // (Already validated by oauth2.ts - signature, issuer, audience, exp)
171
+ * const claims = JSON.parse(
172
+ * Buffer.from(idToken.split('.')[1], 'base64').toString()
173
+ * )
174
+ *
175
+ * // Claims available (depending on scopes):
176
+ * // - sub: Vercel user ID (always present)
177
+ * // - email: user@example.com (if email scope)
178
+ * // - name: "John Doe" (if profile scope)
179
+ * // - picture: "https://..." (if profile scope)
180
+ * // - preferred_username: "johndoe" (if profile scope)
181
+ *
182
+ * // Optionally call Vercel API for more data
183
+ * const userRes = await fetch('https://api.vercel.com/v2/user', {
184
+ * headers: { Authorization: `Bearer ${accessToken}` }
185
+ * })
186
+ * const user = await userRes.json()
187
+ *
188
+ * // Get user's teams
189
+ * const teamsRes = await fetch('https://api.vercel.com/v2/teams', {
190
+ * headers: { Authorization: `Bearer ${accessToken}` }
191
+ * })
192
+ * const teams = await teamsRes.json()
193
+ *
194
+ * return ctx.subject("user", {
195
+ * vercelId: claims.sub,
196
+ * email: claims.email,
197
+ * name: claims.name,
198
+ * username: claims.preferred_username,
199
+ * avatar: claims.picture,
200
+ * teamCount: teams.teams?.length || 0
201
+ * })
202
+ * }
203
+ * }
204
+ * }
205
+ * })
206
+ * ```
207
+ *
208
+ * @remarks
209
+ * - Requires creating a Vercel App in Team Settings → Apps
210
+ * - PKCE is enabled by default for enhanced security
211
+ * - ID Token is automatically validated (signature, issuer, audience, expiration)
212
+ * - Access tokens expire after 1 hour
213
+ * - Refresh tokens rotate on each use and last 30 days
214
+ * - The `openid` scope is required for ID Token issuance
215
+ */
216
+ const VercelProvider = (config) => {
217
+ return Oauth2Provider({
218
+ ...config,
219
+ type: "vercel",
220
+ endpoint: {
221
+ authorization: "https://vercel.com/oauth/authorize",
222
+ token: "https://api.vercel.com/login/oauth/token",
223
+ jwks: "https://vercel.com/.well-known/jwks.json"
224
+ },
225
+ pkce: true
226
+ });
227
+ };
228
+
229
+ //#endregion
230
+ export { VercelProvider };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftlab/auth",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "Core implementation for @draftlab/auth",
6
6
  "author": "Matheus Pergoli",
@@ -39,7 +39,7 @@
39
39
  "devDependencies": {
40
40
  "@types/node": "^24.10.1",
41
41
  "@types/qrcode": "^1.5.6",
42
- "tsdown": "^0.16.7",
42
+ "tsdown": "^0.16.8",
43
43
  "typescript": "^5.9.3",
44
44
  "@draftlab/tsconfig": "0.1.0"
45
45
  },
@@ -63,7 +63,7 @@
63
63
  "preact": "^10.27.2",
64
64
  "preact-render-to-string": "^6.6.3",
65
65
  "qrcode": "^1.5.4",
66
- "@draftlab/auth-router": "0.2.0"
66
+ "@draftlab/auth-router": "0.3.0"
67
67
  },
68
68
  "engines": {
69
69
  "node": ">=18"