@draftlab/auth 0.5.0 → 0.6.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.
@@ -0,0 +1,102 @@
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
+ declare const TwitchProvider: (config: TwitchConfig) => Provider<Oauth2UserData>;
101
+ //#endregion
102
+ export { TwitchConfig, TwitchProvider };
@@ -0,0 +1,118 @@
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
+ * providers: {
15
+ * twitch: TwitchProvider({
16
+ * clientID: process.env.TWITCH_CLIENT_ID,
17
+ * clientSecret: process.env.TWITCH_CLIENT_SECRET,
18
+ * scopes: ["user:read:email"]
19
+ * })
20
+ * }
21
+ * })
22
+ * ```
23
+ *
24
+ * ## Common Scopes
25
+ *
26
+ * - `user:read:email` - Access user's email address
27
+ * - `user:read:subscriptions` - View user subscriptions
28
+ * - `user:read:follows` - View user's follows
29
+ * - `channel:read:subscriptions` - View channel subscribers
30
+ * - `analytics:read:games` - View game analytics
31
+ * - `bits:read` - View bits information
32
+ *
33
+ * ## User Data Access
34
+ *
35
+ * ```ts
36
+ * success: async (ctx, value) => {
37
+ * if (value.provider === "twitch") {
38
+ * const accessToken = value.tokenset.access
39
+ *
40
+ * // Fetch user information
41
+ * const userResponse = await fetch('https://api.twitch.tv/helix/users', {
42
+ * headers: {
43
+ * 'Authorization': `Bearer ${accessToken}`,
44
+ * 'Client-ID': process.env.TWITCH_CLIENT_ID
45
+ * }
46
+ * })
47
+ * const { data } = await userResponse.json()
48
+ * const user = data[0]
49
+ *
50
+ * // User info available: id, login, display_name, email, profile_image_url
51
+ * }
52
+ * }
53
+ * ```
54
+ *
55
+ * @packageDocumentation
56
+ */
57
+ /**
58
+ * Creates a Twitch OAuth 2.0 authentication provider.
59
+ * Allows users to authenticate using their Twitch accounts.
60
+ *
61
+ * @param config - Twitch OAuth 2.0 configuration
62
+ * @returns OAuth 2.0 provider configured for Twitch
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * // Basic Twitch authentication
67
+ * const basicTwitch = TwitchProvider({
68
+ * clientID: process.env.TWITCH_CLIENT_ID,
69
+ * clientSecret: process.env.TWITCH_CLIENT_SECRET
70
+ * })
71
+ *
72
+ * // Twitch with email scope
73
+ * const twitchWithEmail = TwitchProvider({
74
+ * clientID: process.env.TWITCH_CLIENT_ID,
75
+ * clientSecret: process.env.TWITCH_CLIENT_SECRET,
76
+ * scopes: ["user:read:email"]
77
+ * })
78
+ *
79
+ * // Using the access token to fetch user data
80
+ * export default issuer({
81
+ * providers: { twitch: twitchWithEmail },
82
+ * success: async (ctx, value) => {
83
+ * if (value.provider === "twitch") {
84
+ * const token = value.tokenset.access
85
+ *
86
+ * const userRes = await fetch('https://api.twitch.tv/helix/users', {
87
+ * headers: {
88
+ * 'Authorization': `Bearer ${token}`,
89
+ * 'Client-ID': process.env.TWITCH_CLIENT_ID
90
+ * }
91
+ * })
92
+ * const { data } = await userRes.json()
93
+ * const user = data[0]
94
+ *
95
+ * return ctx.subject("user", {
96
+ * twitchId: user.id,
97
+ * login: user.login,
98
+ * email: user.email,
99
+ * displayName: user.display_name
100
+ * })
101
+ * }
102
+ * }
103
+ * })
104
+ * ```
105
+ */
106
+ const TwitchProvider = (config) => {
107
+ return Oauth2Provider({
108
+ ...config,
109
+ type: "twitch",
110
+ endpoint: {
111
+ authorization: "https://id.twitch.tv/oauth2/authorize",
112
+ token: "https://id.twitch.tv/oauth2/token"
113
+ }
114
+ });
115
+ };
116
+
117
+ //#endregion
118
+ export { TwitchProvider };
@@ -0,0 +1,55 @@
1
+ import { StorageAdapter } from "./storage/storage.mjs";
2
+
3
+ //#region src/revocation.d.ts
4
+
5
+ /**
6
+ * Data stored for a revoked token.
7
+ * Tracks when the token was revoked and when it naturally expires.
8
+ */
9
+ interface RevocationRecord {
10
+ /** Timestamp when the token was revoked (milliseconds) */
11
+ revokedAt: number;
12
+ /** Timestamp when the token naturally expires (milliseconds) */
13
+ expiresAt: number;
14
+ }
15
+ /**
16
+ * Token revocation manager.
17
+ * Provides methods to revoke tokens and check if a token has been revoked.
18
+ */
19
+ declare const Revocation: {
20
+ /**
21
+ * Revokes a token, preventing it from being used even if not yet expired.
22
+ *
23
+ * @param storage - Storage adapter to use
24
+ * @param token - The token to revoke (access or refresh token)
25
+ * @param expiresAt - When the token naturally expires (milliseconds since epoch)
26
+ * @returns Promise that resolves when revocation is stored
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * // Revoke a refresh token on logout
31
+ * await Revocation.revoke(storage, refreshToken, expiresAt)
32
+ * ```
33
+ */
34
+ readonly revoke: (storage: StorageAdapter, token: string, expiresAt: number) => Promise<void>;
35
+ /**
36
+ * Checks if a token has been revoked.
37
+ * Returns false if token is not in revocation list (never revoked or already expired).
38
+ *
39
+ * @param storage - Storage adapter to use
40
+ * @param token - The token to check
41
+ * @returns Promise resolving to true if token is revoked, false otherwise
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * // Check if token was revoked before using it
46
+ * const isRevoked = await Revocation.isRevoked(storage, accessToken)
47
+ * if (isRevoked) {
48
+ * throw new InvalidAccessTokenError()
49
+ * }
50
+ * ```
51
+ */
52
+ readonly isRevoked: (storage: StorageAdapter, token: string) => Promise<boolean>;
53
+ };
54
+ //#endregion
55
+ export { Revocation, RevocationRecord };
@@ -0,0 +1,63 @@
1
+ import { Storage } from "./storage/storage.mjs";
2
+ import { createHash } from "node:crypto";
3
+
4
+ //#region src/revocation.ts
5
+ /**
6
+ * Token revocation management for Draft Auth.
7
+ * Handles blacklisting of revoked tokens to prevent their use.
8
+ *
9
+ * ## Overview
10
+ *
11
+ * Revocation allows users to invalidate specific tokens before their natural expiration.
12
+ * This is essential for logout functionality and security in case of token compromise.
13
+ *
14
+ * ## Storage Structure
15
+ *
16
+ * Revoked tokens are stored with their expiration time to allow automatic cleanup:
17
+ * ```
18
+ * revocation:token:{tokenHash} → { revokedAt: timestamp, expiresAt: timestamp }
19
+ * ```
20
+ *
21
+ * ## Security Considerations
22
+ *
23
+ * - Revoked tokens are checked on every use
24
+ * - Storage automatically cleans up expired revocations
25
+ * - Hash tokens for storage to reduce memory usage
26
+ * - Use constant-time comparison for hash verification
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+ /**
31
+ * Hashes a token for storage.
32
+ * Uses SHA-256 to reduce storage size and prevent exposure of full token value.
33
+ *
34
+ * @param token - The token to hash
35
+ * @returns SHA-256 hash of the token
36
+ *
37
+ * @internal
38
+ */
39
+ const hashToken = (token) => {
40
+ return createHash("sha256").update(token).digest("hex");
41
+ };
42
+ /**
43
+ * Token revocation manager.
44
+ * Provides methods to revoke tokens and check if a token has been revoked.
45
+ */
46
+ const Revocation = {
47
+ revoke: async (storage, token, expiresAt) => {
48
+ const key = ["revocation:token", hashToken(token)];
49
+ const record = {
50
+ revokedAt: Date.now(),
51
+ expiresAt
52
+ };
53
+ const ttlSeconds = Math.ceil((expiresAt - Date.now()) / 1e3);
54
+ await Storage.set(storage, key, record, Math.max(1, ttlSeconds));
55
+ },
56
+ isRevoked: async (storage, token) => {
57
+ const key = ["revocation:token", hashToken(token)];
58
+ return !!await Storage.get(storage, key);
59
+ }
60
+ };
61
+
62
+ //#endregion
63
+ export { Revocation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftlab/auth",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "description": "Core implementation for @draftlab/auth",
6
6
  "author": "Matheus Pergoli",