@draftlab/auth 0.0.3 → 0.1.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/allow.d.ts +58 -1
- package/dist/allow.js +61 -2
- package/dist/client.d.ts +2 -3
- package/dist/client.js +2 -2
- package/dist/core.d.ts +128 -8
- package/dist/core.js +496 -12
- package/dist/error.d.ts +242 -1
- package/dist/error.js +235 -1
- package/dist/index.d.ts +1 -8
- package/dist/index.js +1 -12
- package/dist/keys.d.ts +1 -1
- package/dist/keys.js +138 -3
- package/dist/pkce.js +160 -1
- package/dist/provider/code.d.ts +227 -3
- package/dist/provider/code.js +27 -14
- package/dist/provider/facebook.d.ts +2 -3
- package/dist/provider/facebook.js +1 -5
- package/dist/provider/github.d.ts +2 -3
- package/dist/provider/github.js +1 -5
- package/dist/provider/google.d.ts +2 -3
- package/dist/provider/google.js +1 -5
- package/dist/provider/oauth2.d.ts +175 -3
- package/dist/provider/oauth2.js +153 -5
- package/dist/provider/password.d.ts +384 -3
- package/dist/provider/password.js +4 -4
- package/dist/provider/provider.d.ts +226 -2
- package/dist/random.js +85 -1
- package/dist/storage/memory.d.ts +2 -2
- package/dist/storage/memory.js +1 -1
- package/dist/storage/storage.d.ts +161 -1
- package/dist/storage/storage.js +60 -1
- package/dist/storage/turso.d.ts +1 -1
- package/dist/storage/turso.js +1 -1
- package/dist/storage/unstorage.d.ts +2 -2
- package/dist/storage/unstorage.js +2 -2
- package/dist/subject.d.ts +61 -2
- package/dist/themes/theme.d.ts +208 -1
- package/dist/themes/theme.js +118 -1
- package/dist/ui/base.d.ts +22 -35
- package/dist/ui/base.js +388 -3
- package/dist/ui/code.d.ts +22 -137
- package/dist/ui/code.js +199 -161
- package/dist/ui/form.d.ts +8 -6
- package/dist/ui/form.js +57 -1
- package/dist/ui/icon.d.ts +7 -84
- package/dist/ui/icon.js +69 -2
- package/dist/ui/password.d.ts +30 -37
- package/dist/ui/password.js +340 -237
- package/dist/ui/select.d.ts +19 -218
- package/dist/ui/select.js +91 -4
- package/dist/util.d.ts +71 -1
- package/dist/util.js +106 -1
- package/package.json +5 -3
- package/dist/allow-CixonwTW.d.ts +0 -59
- package/dist/allow-DX5cehSc.js +0 -63
- package/dist/base-DRutbxgL.js +0 -422
- package/dist/code-DJxdFR7p.d.ts +0 -212
- package/dist/core-BZHEAefX.d.ts +0 -129
- package/dist/core-CDM5o4rs.js +0 -498
- package/dist/error-CWAdNAzm.d.ts +0 -243
- package/dist/error-DgAKK7b2.js +0 -237
- package/dist/form-6XKM_cOk.js +0 -61
- package/dist/icon-Ci5uqGB_.js +0 -192
- package/dist/keys-EEfxEGfO.js +0 -140
- package/dist/oauth2-B7-6Z7Lc.js +0 -155
- package/dist/oauth2-CXHukHf2.d.ts +0 -176
- package/dist/password-C4KLmO0O.d.ts +0 -385
- package/dist/pkce-276Za_rZ.js +0 -162
- package/dist/provider-tndlqCzp.d.ts +0 -227
- package/dist/random-SXMYlaVr.js +0 -87
- package/dist/select-BjySLL8I.js +0 -280
- package/dist/storage-BEaqEPNQ.js +0 -62
- package/dist/storage-CxKerLlc.d.ts +0 -162
- package/dist/subject-DMIMVtaT.d.ts +0 -62
- package/dist/theme-C9by7VXf.d.ts +0 -209
- package/dist/theme-CswaLtbW.js +0 -120
- package/dist/util-CSdHUFOo.js +0 -108
- package/dist/util-DbSKG1Xm.d.ts +0 -72
|
@@ -1,4 +1,176 @@
|
|
|
1
|
-
import "
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
|
|
3
|
+
//#region src/provider/oauth2.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Configuration options for the OAuth 2.0 provider.
|
|
7
|
+
*/
|
|
8
|
+
interface Oauth2Config {
|
|
9
|
+
/**
|
|
10
|
+
* Provider type identifier for internal use.
|
|
11
|
+
* @internal
|
|
12
|
+
* @default "oauth2"
|
|
13
|
+
*/
|
|
14
|
+
readonly type?: string;
|
|
15
|
+
/**
|
|
16
|
+
* The client ID registered with the OAuth 2.0 provider.
|
|
17
|
+
* This public identifier is used in authorization requests.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* {
|
|
22
|
+
* clientID: "github-app-12345"
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
readonly clientID: string;
|
|
27
|
+
/**
|
|
28
|
+
* The client secret for authenticating with the OAuth 2.0 provider.
|
|
29
|
+
* This private credential must be kept secure and not exposed to clients.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* {
|
|
34
|
+
* clientSecret: process.env.OAUTH_CLIENT_SECRET
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
readonly clientSecret: string;
|
|
39
|
+
/**
|
|
40
|
+
* OAuth 2.0 endpoint URLs for the authorization and token flows.
|
|
41
|
+
*/
|
|
42
|
+
readonly endpoint: {
|
|
43
|
+
/**
|
|
44
|
+
* The authorization endpoint where users are redirected for authentication.
|
|
45
|
+
*
|
|
46
|
+
* @example "https://github.com/login/oauth/authorize"
|
|
47
|
+
*/
|
|
48
|
+
readonly authorization: string;
|
|
49
|
+
/**
|
|
50
|
+
* The token endpoint for exchanging authorization codes for access tokens.
|
|
51
|
+
*
|
|
52
|
+
* @example "https://github.com/login/oauth/access_token"
|
|
53
|
+
*/
|
|
54
|
+
readonly token: string;
|
|
55
|
+
/**
|
|
56
|
+
* Optional JWKS endpoint for verifying ID tokens.
|
|
57
|
+
* Required only if the provider returns ID tokens that need verification.
|
|
58
|
+
*
|
|
59
|
+
* @example "https://provider.com/.well-known/jwks.json"
|
|
60
|
+
*/
|
|
61
|
+
readonly jwks?: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* OAuth 2.0 scopes to request during authorization.
|
|
65
|
+
* Scopes define the level of access being requested.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* {
|
|
70
|
+
* scopes: ["user:email", "read:user", "repo"]
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
readonly scopes: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Whether to use PKCE (Proof Key for Code Exchange) for enhanced security.
|
|
77
|
+
* Recommended for public clients and required by some providers.
|
|
78
|
+
*
|
|
79
|
+
* @default false
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* {
|
|
84
|
+
* pkce: true // Required for Twitter/X, recommended for mobile apps
|
|
85
|
+
* }
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
readonly pkce?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Additional query parameters to include in the authorization request.
|
|
91
|
+
* Useful for provider-specific parameters or customizing the auth flow.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* {
|
|
96
|
+
* query: {
|
|
97
|
+
* access_type: "offline", // Request refresh token
|
|
98
|
+
* prompt: "consent", // Force consent screen
|
|
99
|
+
* hd: "mycompany.com" // Google Workspace domain
|
|
100
|
+
* }
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
readonly query?: Record<string, string>;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* OAuth 2.0 configuration without endpoint-specific fields.
|
|
108
|
+
* Used internally for provider wrapping.
|
|
109
|
+
* @internal
|
|
110
|
+
*/
|
|
111
|
+
type Oauth2WrappedConfig = Omit<Oauth2Config, "endpoint" | "name">;
|
|
112
|
+
/**
|
|
113
|
+
* OAuth 2.0 token response containing access tokens and metadata.
|
|
114
|
+
* Provides a structured interface for token data with lazy property access.
|
|
115
|
+
* @internal
|
|
116
|
+
*/
|
|
117
|
+
interface Oauth2Token {
|
|
118
|
+
/** Access token for making authenticated API requests */
|
|
119
|
+
readonly access: string;
|
|
120
|
+
/** Refresh token for obtaining new access tokens (if provided) */
|
|
121
|
+
readonly refresh: string;
|
|
122
|
+
/** Token expiration time in seconds (if provided) */
|
|
123
|
+
readonly expiry: number;
|
|
124
|
+
/** Raw token response from the provider */
|
|
125
|
+
readonly raw: Record<string, unknown>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* User data returned by successful OAuth 2.0 authentication.
|
|
129
|
+
*/
|
|
130
|
+
interface Oauth2UserData {
|
|
131
|
+
/** Token set containing access token, refresh token, and metadata */
|
|
132
|
+
readonly tokenset: Oauth2Token;
|
|
133
|
+
/** Client ID used for this authentication */
|
|
134
|
+
readonly clientID: string;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Creates an OAuth 2.0 authentication provider.
|
|
138
|
+
* Implements the Authorization Code Grant flow with optional PKCE support.
|
|
139
|
+
*
|
|
140
|
+
* @param config - OAuth 2.0 provider configuration
|
|
141
|
+
* @returns Provider instance implementing OAuth 2.0 authentication
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```ts
|
|
145
|
+
* // GitHub provider with basic configuration
|
|
146
|
+
* const githubProvider = Oauth2Provider({
|
|
147
|
+
* clientID: process.env.GITHUB_CLIENT_ID,
|
|
148
|
+
* clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
149
|
+
* endpoint: {
|
|
150
|
+
* authorization: "https://github.com/login/oauth/authorize",
|
|
151
|
+
* token: "https://github.com/login/oauth/access_token"
|
|
152
|
+
* },
|
|
153
|
+
* scopes: ["user:email", "read:user"]
|
|
154
|
+
* })
|
|
155
|
+
*
|
|
156
|
+
* // Provider with PKCE and custom parameters
|
|
157
|
+
* const customProvider = Oauth2Provider({
|
|
158
|
+
* clientID: "my-client-id",
|
|
159
|
+
* clientSecret: "my-client-secret",
|
|
160
|
+
* endpoint: {
|
|
161
|
+
* authorization: "https://provider.com/oauth/authorize",
|
|
162
|
+
* token: "https://provider.com/oauth/token",
|
|
163
|
+
* jwks: "https://provider.com/.well-known/jwks.json"
|
|
164
|
+
* },
|
|
165
|
+
* scopes: ["read", "write"],
|
|
166
|
+
* pkce: true,
|
|
167
|
+
* query: {
|
|
168
|
+
* prompt: "consent",
|
|
169
|
+
* access_type: "offline"
|
|
170
|
+
* }
|
|
171
|
+
* })
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
declare const Oauth2Provider: (config: Oauth2Config) => Provider<Oauth2UserData>;
|
|
175
|
+
//#endregion
|
|
4
176
|
export { Oauth2Config, Oauth2Provider, Oauth2Token, Oauth2UserData, Oauth2WrappedConfig };
|
package/dist/provider/oauth2.js
CHANGED
|
@@ -1,7 +1,155 @@
|
|
|
1
|
-
import "../util
|
|
2
|
-
import "../error
|
|
3
|
-
import "../pkce
|
|
4
|
-
import "../random
|
|
5
|
-
import { Oauth2Provider } from "../oauth2-B7-6Z7Lc.js";
|
|
1
|
+
import { getRelativeUrl } from "../util.js";
|
|
2
|
+
import { OauthError } from "../error.js";
|
|
3
|
+
import { generatePKCE } from "../pkce.js";
|
|
4
|
+
import { generateSecureToken, timingSafeCompare } from "../random.js";
|
|
6
5
|
|
|
6
|
+
//#region src/provider/oauth2.ts
|
|
7
|
+
/**
|
|
8
|
+
* Creates an OAuth 2.0 authentication provider.
|
|
9
|
+
* Implements the Authorization Code Grant flow with optional PKCE support.
|
|
10
|
+
*
|
|
11
|
+
* @param config - OAuth 2.0 provider configuration
|
|
12
|
+
* @returns Provider instance implementing OAuth 2.0 authentication
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* // GitHub provider with basic configuration
|
|
17
|
+
* const githubProvider = Oauth2Provider({
|
|
18
|
+
* clientID: process.env.GITHUB_CLIENT_ID,
|
|
19
|
+
* clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
20
|
+
* endpoint: {
|
|
21
|
+
* authorization: "https://github.com/login/oauth/authorize",
|
|
22
|
+
* token: "https://github.com/login/oauth/access_token"
|
|
23
|
+
* },
|
|
24
|
+
* scopes: ["user:email", "read:user"]
|
|
25
|
+
* })
|
|
26
|
+
*
|
|
27
|
+
* // Provider with PKCE and custom parameters
|
|
28
|
+
* const customProvider = Oauth2Provider({
|
|
29
|
+
* clientID: "my-client-id",
|
|
30
|
+
* clientSecret: "my-client-secret",
|
|
31
|
+
* endpoint: {
|
|
32
|
+
* authorization: "https://provider.com/oauth/authorize",
|
|
33
|
+
* token: "https://provider.com/oauth/token",
|
|
34
|
+
* jwks: "https://provider.com/.well-known/jwks.json"
|
|
35
|
+
* },
|
|
36
|
+
* scopes: ["read", "write"],
|
|
37
|
+
* pkce: true,
|
|
38
|
+
* query: {
|
|
39
|
+
* prompt: "consent",
|
|
40
|
+
* access_type: "offline"
|
|
41
|
+
* }
|
|
42
|
+
* })
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
const Oauth2Provider = (config) => {
|
|
46
|
+
const authQuery = config.query || {};
|
|
47
|
+
/**
|
|
48
|
+
* Handles the OAuth 2.0 callback logic for both GET and POST requests.
|
|
49
|
+
* Exchanges the authorization code for tokens and processes the response.
|
|
50
|
+
*/
|
|
51
|
+
const handleCallbackLogic = async (c, ctx, provider, code) => {
|
|
52
|
+
if (!(provider && code)) return c.redirect(getRelativeUrl(c, "./authorize"));
|
|
53
|
+
const tokenRequestBody = new URLSearchParams({
|
|
54
|
+
client_id: config.clientID,
|
|
55
|
+
client_secret: config.clientSecret,
|
|
56
|
+
code,
|
|
57
|
+
grant_type: "authorization_code",
|
|
58
|
+
redirect_uri: provider.redirect,
|
|
59
|
+
...provider.codeVerifier ? { code_verifier: provider.codeVerifier } : {}
|
|
60
|
+
});
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetch(config.endpoint.token, {
|
|
63
|
+
method: "POST",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
66
|
+
Accept: "application/json"
|
|
67
|
+
},
|
|
68
|
+
body: tokenRequestBody.toString()
|
|
69
|
+
});
|
|
70
|
+
if (!response.ok) throw new Error(`Token request failed with status ${response.status}`);
|
|
71
|
+
const tokenData = await response.json();
|
|
72
|
+
if (tokenData.error) throw new OauthError(tokenData.error, tokenData.error_description || "");
|
|
73
|
+
return await ctx.success(c, {
|
|
74
|
+
clientID: config.clientID,
|
|
75
|
+
tokenset: {
|
|
76
|
+
get access() {
|
|
77
|
+
return tokenData.access_token;
|
|
78
|
+
},
|
|
79
|
+
get refresh() {
|
|
80
|
+
return tokenData.refresh_token || "";
|
|
81
|
+
},
|
|
82
|
+
get expiry() {
|
|
83
|
+
return tokenData.expires_in || 0;
|
|
84
|
+
},
|
|
85
|
+
get raw() {
|
|
86
|
+
return tokenData;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
} catch (error) {
|
|
91
|
+
if (error instanceof OauthError) throw error;
|
|
92
|
+
throw new OauthError("server_error", `Token exchange failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
return {
|
|
96
|
+
type: config.type || "oauth2",
|
|
97
|
+
init(routes, ctx) {
|
|
98
|
+
/**
|
|
99
|
+
* Initiates OAuth 2.0 authorization flow.
|
|
100
|
+
* Redirects user to the provider's authorization endpoint with proper parameters.
|
|
101
|
+
*/
|
|
102
|
+
routes.get("/authorize", async (c) => {
|
|
103
|
+
const state = generateSecureToken();
|
|
104
|
+
const pkce = config.pkce ? await generatePKCE() : void 0;
|
|
105
|
+
await ctx.set(c, "provider", 60 * 10, {
|
|
106
|
+
state,
|
|
107
|
+
redirect: getRelativeUrl(c, "./callback"),
|
|
108
|
+
codeVerifier: pkce?.verifier
|
|
109
|
+
});
|
|
110
|
+
const authorizationUrl = new URL(config.endpoint.authorization);
|
|
111
|
+
authorizationUrl.searchParams.set("client_id", config.clientID);
|
|
112
|
+
authorizationUrl.searchParams.set("redirect_uri", getRelativeUrl(c, "./callback"));
|
|
113
|
+
authorizationUrl.searchParams.set("response_type", "code");
|
|
114
|
+
authorizationUrl.searchParams.set("state", state);
|
|
115
|
+
authorizationUrl.searchParams.set("scope", config.scopes.join(" "));
|
|
116
|
+
if (pkce) {
|
|
117
|
+
authorizationUrl.searchParams.set("code_challenge", pkce.challenge);
|
|
118
|
+
authorizationUrl.searchParams.set("code_challenge_method", pkce.method);
|
|
119
|
+
}
|
|
120
|
+
for (const [key, value] of Object.entries(authQuery)) authorizationUrl.searchParams.set(key, value);
|
|
121
|
+
return c.redirect(authorizationUrl.toString());
|
|
122
|
+
});
|
|
123
|
+
/**
|
|
124
|
+
* Handles OAuth 2.0 callback via query parameters (GET request).
|
|
125
|
+
* Standard OAuth 2.0 callback method for most providers.
|
|
126
|
+
*/
|
|
127
|
+
routes.get("/callback", async (c) => {
|
|
128
|
+
const provider = await ctx.get(c, "provider");
|
|
129
|
+
const code = c.query("code");
|
|
130
|
+
const state = c.query("state");
|
|
131
|
+
const error = c.query("error");
|
|
132
|
+
if (error) throw new OauthError(error, c.query("error_description") || "");
|
|
133
|
+
if (!(provider && code) || provider.state && !timingSafeCompare(state || "", provider.state)) return c.redirect(getRelativeUrl(c, "./authorize"));
|
|
134
|
+
return await handleCallbackLogic(c, ctx, provider, code);
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* Handles OAuth 2.0 callback via form data (POST request).
|
|
138
|
+
* Alternative callback method supported by some providers.
|
|
139
|
+
*/
|
|
140
|
+
routes.post("/callback", async (c) => {
|
|
141
|
+
const provider = await ctx.get(c, "provider");
|
|
142
|
+
const formData = await c.formData();
|
|
143
|
+
const code = formData.get("code")?.toString();
|
|
144
|
+
const state = formData.get("state")?.toString();
|
|
145
|
+
const error = formData.get("error")?.toString();
|
|
146
|
+
if (error) throw new OauthError(error, formData.get("error_description")?.toString() || "");
|
|
147
|
+
if (!(provider && code) || provider.state && !timingSafeCompare(state || "", provider.state)) return c.redirect(getRelativeUrl(c, "./authorize"));
|
|
148
|
+
return await handleCallbackLogic(c, ctx, provider, code);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
//#endregion
|
|
7
155
|
export { Oauth2Provider };
|