@zitadel/astro-auth 1.2.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/LICENSE +201 -0
- package/README.md +254 -0
- package/dist/api/[...auth].js +41 -0
- package/dist/api/[...auth].js.map +1 -0
- package/dist/client.d.ts +91 -0
- package/dist/client.js +157 -0
- package/dist/client.js.map +1 -0
- package/dist/components/Auth.astro +38 -0
- package/dist/components/SignIn.astro +133 -0
- package/dist/components/SignOut.astro +99 -0
- package/dist/components/index.d.ts +9 -0
- package/dist/components/index.js +10 -0
- package/dist/components/index.js.map +1 -0
- package/dist/config-lAdfi49m.d.ts +104 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +50 -0
- package/dist/server.js +84 -0
- package/dist/server.js.map +1 -0
- package/dist/types-Bvas30QH.d.ts +66 -0
- package/package.json +107 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// src/client.ts
|
|
2
|
+
async function __getCsrfToken(prefix) {
|
|
3
|
+
const res = await fetch(`${prefix}/csrf`, {
|
|
4
|
+
method: "GET",
|
|
5
|
+
credentials: "same-origin",
|
|
6
|
+
headers: {
|
|
7
|
+
Accept: "application/json",
|
|
8
|
+
"X-Requested-With": "XMLHttpRequest"
|
|
9
|
+
},
|
|
10
|
+
cache: "no-store"
|
|
11
|
+
});
|
|
12
|
+
if (!res.ok) {
|
|
13
|
+
throw new Error(`Failed to fetch CSRF token (${res.status})`);
|
|
14
|
+
}
|
|
15
|
+
const json = await res.json().catch(() => {
|
|
16
|
+
throw new Error("CSRF endpoint returned non-JSON response");
|
|
17
|
+
});
|
|
18
|
+
const token = json?.csrfToken;
|
|
19
|
+
if (typeof token !== "string" || token.length === 0) {
|
|
20
|
+
throw new Error("Missing or invalid CSRF token");
|
|
21
|
+
}
|
|
22
|
+
return token;
|
|
23
|
+
}
|
|
24
|
+
function __normalizePathOnly(href) {
|
|
25
|
+
try {
|
|
26
|
+
const u = new URL(href, window.location.origin);
|
|
27
|
+
if (u.origin !== window.location.origin) {
|
|
28
|
+
return window.location.pathname + window.location.search + window.location.hash;
|
|
29
|
+
}
|
|
30
|
+
return u.pathname + u.search + u.hash;
|
|
31
|
+
} catch {
|
|
32
|
+
return window.location.pathname + window.location.search + window.location.hash;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function __safeRedirect(target, fallbackPathOnly) {
|
|
36
|
+
if (!target) return fallbackPathOnly;
|
|
37
|
+
try {
|
|
38
|
+
const u = new URL(target, window.location.origin);
|
|
39
|
+
if (u.origin !== window.location.origin) return fallbackPathOnly;
|
|
40
|
+
return u.href;
|
|
41
|
+
} catch {
|
|
42
|
+
return fallbackPathOnly;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
async function signIn(providerId, options, authorizationParams) {
|
|
46
|
+
const initialCallbackUrl = options?.callbackUrl ?? window.location.href;
|
|
47
|
+
const redirect = options?.redirect ?? true;
|
|
48
|
+
const callbackUrl = __normalizePathOnly(initialCallbackUrl);
|
|
49
|
+
const prefix = options?.prefix ?? "/api/auth";
|
|
50
|
+
const { prefix: _p, callbackUrl: _c, redirect: _r, ...opts } = options ?? {};
|
|
51
|
+
const isCredentials = providerId === "credentials";
|
|
52
|
+
const isEmail = providerId === "email";
|
|
53
|
+
const isSupportingReturn = isCredentials || isEmail;
|
|
54
|
+
const signInUrl = isCredentials ? `${prefix}/callback/${providerId}` : `${prefix}/signin/${providerId}`;
|
|
55
|
+
if (!isSupportingReturn) {
|
|
56
|
+
const csrfToken2 = await __getCsrfToken(prefix);
|
|
57
|
+
const action = new URL(signInUrl, window.location.origin);
|
|
58
|
+
if (authorizationParams) {
|
|
59
|
+
for (const [k, v] of Object.entries(authorizationParams)) {
|
|
60
|
+
action.searchParams.set(k, v);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
const form = document.createElement("form");
|
|
64
|
+
form.method = "POST";
|
|
65
|
+
form.action = action.pathname + action.search;
|
|
66
|
+
form.style.display = "none";
|
|
67
|
+
const fields = {
|
|
68
|
+
csrfToken: csrfToken2,
|
|
69
|
+
callbackUrl,
|
|
70
|
+
...Object.fromEntries(
|
|
71
|
+
Object.entries(opts).map(([k, v]) => [k, String(v)])
|
|
72
|
+
)
|
|
73
|
+
};
|
|
74
|
+
for (const [name, value] of Object.entries(fields)) {
|
|
75
|
+
const input = document.createElement("input");
|
|
76
|
+
input.type = "hidden";
|
|
77
|
+
input.name = name;
|
|
78
|
+
input.value = value;
|
|
79
|
+
form.appendChild(input);
|
|
80
|
+
}
|
|
81
|
+
document.body.appendChild(form);
|
|
82
|
+
form.submit();
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const signInUrlWithParams = (() => {
|
|
86
|
+
const url = new URL(signInUrl, window.location.origin);
|
|
87
|
+
if (authorizationParams) {
|
|
88
|
+
for (const [k, v] of Object.entries(authorizationParams)) {
|
|
89
|
+
url.searchParams.set(k, v);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return url.pathname + url.search;
|
|
93
|
+
})();
|
|
94
|
+
const csrfToken = await __getCsrfToken(prefix);
|
|
95
|
+
const res = await fetch(signInUrlWithParams, {
|
|
96
|
+
method: "post",
|
|
97
|
+
credentials: "same-origin",
|
|
98
|
+
cache: "no-store",
|
|
99
|
+
headers: {
|
|
100
|
+
Accept: "application/json",
|
|
101
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
102
|
+
"X-Auth-Return-Redirect": "1",
|
|
103
|
+
"X-Requested-With": "XMLHttpRequest"
|
|
104
|
+
},
|
|
105
|
+
body: new URLSearchParams({
|
|
106
|
+
...opts,
|
|
107
|
+
csrfToken,
|
|
108
|
+
callbackUrl
|
|
109
|
+
})
|
|
110
|
+
});
|
|
111
|
+
const data = await res.clone().json().catch(() => ({}));
|
|
112
|
+
const error = data.url ? new URL(data.url).searchParams.get("error") : null;
|
|
113
|
+
if (redirect !== false || !isSupportingReturn || !error) {
|
|
114
|
+
const candidate = data.url ?? (res.redirected ? res.url : void 0);
|
|
115
|
+
const target = __safeRedirect(candidate, callbackUrl);
|
|
116
|
+
window.location.assign(target);
|
|
117
|
+
if (target && target.includes("#")) {
|
|
118
|
+
window.location.reload();
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
} else {
|
|
122
|
+
return res;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
async function signOut(options) {
|
|
126
|
+
const initialCallbackUrl = options?.callbackUrl ?? window.location.href;
|
|
127
|
+
const prefix = options?.prefix ?? "/api/auth";
|
|
128
|
+
const callbackUrl = __normalizePathOnly(initialCallbackUrl);
|
|
129
|
+
const csrfToken = await __getCsrfToken(prefix);
|
|
130
|
+
const res = await fetch(`${prefix}/signout`, {
|
|
131
|
+
method: "post",
|
|
132
|
+
credentials: "same-origin",
|
|
133
|
+
cache: "no-store",
|
|
134
|
+
headers: {
|
|
135
|
+
Accept: "application/json",
|
|
136
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
137
|
+
"X-Auth-Return-Redirect": "1",
|
|
138
|
+
"X-Requested-With": "XMLHttpRequest"
|
|
139
|
+
},
|
|
140
|
+
body: new URLSearchParams({
|
|
141
|
+
csrfToken,
|
|
142
|
+
callbackUrl
|
|
143
|
+
})
|
|
144
|
+
});
|
|
145
|
+
const data = await res.json().catch(() => ({}));
|
|
146
|
+
const candidate = data.url ?? (res.redirected ? res.url : void 0);
|
|
147
|
+
const url = __safeRedirect(candidate, callbackUrl);
|
|
148
|
+
window.location.assign(url);
|
|
149
|
+
if (url.includes("#")) {
|
|
150
|
+
window.location.reload();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export {
|
|
154
|
+
signIn,
|
|
155
|
+
signOut
|
|
156
|
+
};
|
|
157
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import type {\n AstroSignInOptions,\n AstroSignOutParams,\n LiteralUnion,\n SignInAuthorizationParams,\n} from './types.js';\n\nasync function __getCsrfToken(prefix: string): Promise<string> {\n const res = await fetch(`${prefix}/csrf`, {\n method: 'GET',\n credentials: 'same-origin',\n headers: {\n Accept: 'application/json',\n 'X-Requested-With': 'XMLHttpRequest',\n },\n cache: 'no-store',\n });\n if (!res.ok) {\n throw new Error(`Failed to fetch CSRF token (${res.status})`);\n }\n const json: unknown = await res.json().catch(() => {\n throw new Error('CSRF endpoint returned non-JSON response');\n });\n const token = (json as { csrfToken?: string })?.csrfToken;\n if (typeof token !== 'string' || token.length === 0) {\n throw new Error('Missing or invalid CSRF token');\n }\n return token;\n}\n\nfunction __normalizePathOnly(href: string): string {\n try {\n const u = new URL(href, window.location.origin);\n if (u.origin !== window.location.origin) {\n return (\n window.location.pathname + window.location.search + window.location.hash\n );\n }\n return u.pathname + u.search + u.hash;\n } catch {\n return (\n window.location.pathname + window.location.search + window.location.hash\n );\n }\n}\n\nfunction __safeRedirect(\n target: string | null | undefined,\n fallbackPathOnly: string,\n): string {\n if (!target) return fallbackPathOnly;\n try {\n const u = new URL(target, window.location.origin);\n if (u.origin !== window.location.origin) return fallbackPathOnly;\n return u.href;\n } catch {\n return fallbackPathOnly;\n }\n}\n\n/**\n * Initiates a sign-in flow with the specified authentication provider.\n *\n * This function handles authentication for different provider types using\n * the appropriate mechanism for each. OAuth providers require browser form\n * submission to properly handle redirect chains, while credential-based\n * providers can use fetch for JSON responses.\n *\n * @typeParam P - The provider identifier type for type-safe provider names\n * @param providerId - The authentication provider identifier (e.g., 'github',\n * 'google', 'credentials')\n * @param options - Configuration options for the sign-in flow\n * @param authorizationParams - Additional OAuth authorization parameters to\n * pass to the provider\n * @returns Promise that resolves to Response for credential providers when\n * redirect is false and an error occurs, otherwise void\n *\n * @remarks\n * **Why Different Mechanisms for Different Providers:**\n *\n * OAuth Providers (github, google, etc.):\n * - Auth.js responds with a 302 redirect to the OAuth provider's login page\n * - Using `fetch()` causes the browser to follow redirects in JS context\n * - The OAuth provider expects a real browser navigation, not XHR\n * - Result: fetch completes but no visible navigation occurs\n * - Solution: Use real form submission to let browser handle redirects\n *\n * Credential/Email Providers:\n * - Auth.js responds with JSON containing success/error information\n * - These providers need to return Response objects for error handling\n * - Using `fetch()` allows access to response data and conditional redirects\n * - Result: Can detect errors and optionally suppress redirect\n * - Solution: Use fetch with JSON response handling\n *\n * **Security Considerations:**\n * - All requests include CSRF token protection\n * - Callback URLs are normalized to prevent open redirect attacks\n * - Only same-origin URLs are allowed for redirects\n *\n * **Browser Compatibility:**\n * - Form submission approach works in all browsers including those that\n * restrict fetch() behavior for cross-origin redirects\n * - Hash-based navigation triggers explicit reload for proper routing\n *\n * @example OAuth Provider Sign-In\n * ```ts\n * // Triggers form submission and browser navigation to GitHub\n * await signIn('github')\n * ```\n *\n * @example OAuth with Custom Callback\n * ```ts\n * await signIn('google', {\n * callbackUrl: '/dashboard'\n * })\n * ```\n *\n * @example OAuth with Authorization Parameters\n * ```ts\n * await signIn('google', undefined, {\n * prompt: 'consent',\n * login_hint: 'user@example.com'\n * })\n * ```\n *\n * @example Credential Provider with Error Handling\n * ```ts\n * const response = await signIn('credentials', {\n * redirect: false\n * }, {\n * username: 'user',\n * password: 'pass'\n * })\n *\n * if (response) {\n * const data = await response.json()\n * if (data.error) {\n * console.error('Sign in failed:', data.error)\n * }\n * }\n * ```\n *\n * @public\n */\nexport async function signIn<P extends string | undefined = undefined>(\n providerId?: LiteralUnion<P extends string ? P | string : string>,\n options?: AstroSignInOptions,\n authorizationParams?: SignInAuthorizationParams,\n): Promise<Response | void> {\n const initialCallbackUrl = options?.callbackUrl ?? window.location.href;\n const redirect = options?.redirect ?? true;\n\n const callbackUrl = __normalizePathOnly(initialCallbackUrl);\n\n const prefix = options?.prefix ?? '/api/auth';\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { prefix: _p, callbackUrl: _c, redirect: _r, ...opts } = options ?? {};\n\n const isCredentials = providerId === 'credentials';\n const isEmail = providerId === 'email';\n const isSupportingReturn = isCredentials || isEmail;\n\n const signInUrl = isCredentials\n ? `${prefix}/callback/${providerId}`\n : `${prefix}/signin/${providerId}`;\n\n // ========================================================================\n // OAuth Provider Flow: Use Form Submission\n // ========================================================================\n // OAuth providers (GitHub, Google, etc.) require browser navigation to\n // follow the 302 redirect chain to the provider's authorization page.\n // Using fetch() keeps the redirect in JavaScript context, preventing the\n // actual navigation from occurring. Form submission allows the browser to\n // handle the redirect naturally.\n if (!isSupportingReturn) {\n const csrfToken: string = await __getCsrfToken(prefix);\n\n // Build the form action URL with authorization parameters\n const action = new URL(signInUrl, window.location.origin);\n if (authorizationParams) {\n for (const [k, v] of Object.entries(authorizationParams)) {\n action.searchParams.set(k, v);\n }\n }\n\n // Create a hidden form to submit the authentication request\n const form = document.createElement('form');\n form.method = 'POST';\n form.action = action.pathname + action.search;\n form.style.display = 'none';\n\n // Add required fields for Auth.js\n const fields: Record<string, string> = {\n csrfToken,\n callbackUrl,\n ...Object.fromEntries(\n Object.entries(opts).map(([k, v]) => [k, String(v)]),\n ),\n };\n\n for (const [name, value] of Object.entries(fields)) {\n const input = document.createElement('input');\n input.type = 'hidden';\n input.name = name;\n input.value = value;\n form.appendChild(input);\n }\n\n document.body.appendChild(form);\n form.submit();\n return;\n }\n\n // ========================================================================\n // Credential/Email Provider Flow: Use Fetch\n // ========================================================================\n // Credential and email providers return JSON responses that may contain\n // error information. Using fetch() allows us to inspect the response and\n // conditionally handle redirects based on the redirect option and error\n // state. This enables the redirect: false pattern for error handling.\n\n const signInUrlWithParams = (() => {\n const url = new URL(signInUrl, window.location.origin);\n if (authorizationParams) {\n for (const [k, v] of Object.entries(authorizationParams)) {\n url.searchParams.set(k, v);\n }\n }\n return url.pathname + url.search;\n })();\n\n const csrfToken: string = await __getCsrfToken(prefix);\n\n const res = await fetch(signInUrlWithParams, {\n method: 'post',\n credentials: 'same-origin',\n cache: 'no-store',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'X-Auth-Return-Redirect': '1',\n 'X-Requested-With': 'XMLHttpRequest',\n },\n body: new URLSearchParams({\n ...opts,\n csrfToken,\n callbackUrl,\n }),\n });\n\n const data: { url?: string } = await res\n .clone()\n .json()\n .catch(() => ({}));\n const error = data.url ? new URL(data.url).searchParams.get('error') : null;\n\n // Redirect unless explicitly disabled and an error occurred\n if (redirect !== false || !isSupportingReturn || !error) {\n const candidate = data.url ?? (res.redirected ? res.url : undefined);\n const target = __safeRedirect(candidate, callbackUrl);\n window.location.assign(target);\n\n // Force reload for hash-based navigation\n if (target && target.includes('#')) {\n window.location.reload();\n }\n return;\n } else {\n // Return response for error handling when redirect is false\n return res;\n }\n}\n\nexport async function signOut(options?: AstroSignOutParams): Promise<void> {\n const initialCallbackUrl = options?.callbackUrl ?? window.location.href;\n const prefix = options?.prefix ?? '/api/auth';\n\n const callbackUrl = __normalizePathOnly(initialCallbackUrl);\n\n const csrfToken: string = await __getCsrfToken(prefix);\n\n const res = await fetch(`${prefix}/signout`, {\n method: 'post',\n credentials: 'same-origin',\n cache: 'no-store',\n headers: {\n Accept: 'application/json',\n 'Content-Type': 'application/x-www-form-urlencoded',\n 'X-Auth-Return-Redirect': '1',\n 'X-Requested-With': 'XMLHttpRequest',\n },\n body: new URLSearchParams({\n csrfToken,\n callbackUrl,\n }),\n });\n\n const data: { url?: string } = await res.json().catch(() => ({}));\n const candidate = data.url ?? (res.redirected ? res.url : undefined);\n const url = __safeRedirect(candidate, callbackUrl);\n\n window.location.assign(url);\n\n if (url.includes('#')) {\n window.location.reload();\n }\n}\n"],"mappings":"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","names":["csrfToken"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { getSession } from '../server.js';
|
|
3
|
+
import { type FullAuthConfig } from '../config.js';
|
|
4
|
+
import authDefaultConfig from 'auth:config';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Props for the Session component.
|
|
8
|
+
*
|
|
9
|
+
* Allows overriding the authentication configuration for retrieving
|
|
10
|
+
* session data.
|
|
11
|
+
*/
|
|
12
|
+
interface Props {
|
|
13
|
+
/**
|
|
14
|
+
* Optional authentication configuration override.
|
|
15
|
+
*
|
|
16
|
+
* If not provided, uses the default configuration from the
|
|
17
|
+
* auth:config virtual module.
|
|
18
|
+
*/
|
|
19
|
+
authConfig?: FullAuthConfig;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const { authConfig = authDefaultConfig } = Astro.props as Props;
|
|
23
|
+
|
|
24
|
+
// Retrieve the current session using the provided or default auth config
|
|
25
|
+
let session = await getSession(Astro.request, authConfig);
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
<!--
|
|
29
|
+
Render the default slot with session data passed as an argument.
|
|
30
|
+
This allows child components to access session information.
|
|
31
|
+
|
|
32
|
+
Note: Slots are rendered by Astro's template engine. User-provided
|
|
33
|
+
content is automatically escaped. Do not use set:html with user data
|
|
34
|
+
to prevent XSS vulnerabilities.
|
|
35
|
+
-->
|
|
36
|
+
<div>
|
|
37
|
+
<Fragment set:html={Astro.slots.render('default', [session])} />
|
|
38
|
+
</div>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import type {
|
|
4
|
+
BuiltInProviders,
|
|
5
|
+
SignInOptions,
|
|
6
|
+
SignInAuthorizationParams,
|
|
7
|
+
} from '../types.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Props for the SignIn component.
|
|
11
|
+
*
|
|
12
|
+
* Extends standard HTML button attributes with authentication-specific
|
|
13
|
+
* options for configuring the sign-in behavior.
|
|
14
|
+
*/
|
|
15
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
16
|
+
/**
|
|
17
|
+
* The authentication provider to sign in with.
|
|
18
|
+
*
|
|
19
|
+
* Can be a built-in provider (e.g., 'google', 'github') or a custom
|
|
20
|
+
* provider identifier.
|
|
21
|
+
*/
|
|
22
|
+
provider?: BuiltInProviders | string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Additional options to pass to the signIn function.
|
|
26
|
+
*
|
|
27
|
+
* These options control the sign-in behavior, such as redirect URLs
|
|
28
|
+
* and callback handling.
|
|
29
|
+
*/
|
|
30
|
+
options?: SignInOptions;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Authorization parameters to pass to the authentication provider.
|
|
34
|
+
*
|
|
35
|
+
* These are provider-specific parameters that customize the
|
|
36
|
+
* authentication flow.
|
|
37
|
+
*/
|
|
38
|
+
authParams?: SignInAuthorizationParams;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Generate a unique identifier for this component instance to avoid
|
|
42
|
+
// conflicts when multiple SignIn buttons are present on the same page
|
|
43
|
+
const key = crypto.randomUUID();
|
|
44
|
+
|
|
45
|
+
// noinspection JSUnusedGlobalSymbols
|
|
46
|
+
const { provider, options, authParams, ...attrs } = Astro.props;
|
|
47
|
+
attrs.class = `signin-button ${attrs.class ?? ''}`;
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<button
|
|
51
|
+
{...attrs}
|
|
52
|
+
data-signin-key={key}
|
|
53
|
+
data-signin-provider={provider}
|
|
54
|
+
data-signin-options={options ? JSON.stringify(options) : undefined}
|
|
55
|
+
data-signin-auth-params={authParams ? JSON.stringify(authParams) : undefined}
|
|
56
|
+
>
|
|
57
|
+
<slot />
|
|
58
|
+
</button>
|
|
59
|
+
|
|
60
|
+
<script>
|
|
61
|
+
import { signIn } from '../client.js';
|
|
62
|
+
|
|
63
|
+
function initializeSignInButton(button: Element) {
|
|
64
|
+
if (button instanceof HTMLElement && !button.dataset.signinInitialized) {
|
|
65
|
+
button.dataset.signinInitialized = 'true';
|
|
66
|
+
|
|
67
|
+
button.addEventListener(
|
|
68
|
+
'click',
|
|
69
|
+
() => {
|
|
70
|
+
const provider = button.dataset.signinProvider;
|
|
71
|
+
|
|
72
|
+
// Guard JSON parsing to prevent errors from malformed data
|
|
73
|
+
let options;
|
|
74
|
+
let authParams;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
options = button.dataset.signinOptions
|
|
78
|
+
? JSON.parse(button.dataset.signinOptions)
|
|
79
|
+
: undefined;
|
|
80
|
+
} catch (e) {
|
|
81
|
+
console.warn('[astro-auth] Invalid signin options JSON:', e);
|
|
82
|
+
options = undefined;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
authParams = button.dataset.signinAuthParams
|
|
87
|
+
? JSON.parse(button.dataset.signinAuthParams)
|
|
88
|
+
: undefined;
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.warn('[astro-auth] Invalid signin authParams JSON:', e);
|
|
91
|
+
authParams = undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
signIn(provider, options, authParams);
|
|
95
|
+
},
|
|
96
|
+
{ passive: true, capture: true },
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
document
|
|
102
|
+
.querySelectorAll('[data-signin-key]')
|
|
103
|
+
.forEach(initializeSignInButton);
|
|
104
|
+
|
|
105
|
+
const observer = new MutationObserver((mutations) => {
|
|
106
|
+
mutations.forEach((mutation) => {
|
|
107
|
+
mutation.addedNodes.forEach((node) => {
|
|
108
|
+
if (node instanceof HTMLElement) {
|
|
109
|
+
if (node.hasAttribute('data-signin-key')) {
|
|
110
|
+
initializeSignInButton(node);
|
|
111
|
+
}
|
|
112
|
+
node
|
|
113
|
+
.querySelectorAll('[data-signin-key]')
|
|
114
|
+
.forEach(initializeSignInButton);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
121
|
+
|
|
122
|
+
// Clean up observer when page becomes hidden or before unload
|
|
123
|
+
const cleanup = () => {
|
|
124
|
+
observer.disconnect();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
window.addEventListener('beforeunload', cleanup);
|
|
128
|
+
document.addEventListener('visibilitychange', () => {
|
|
129
|
+
if (document.hidden) {
|
|
130
|
+
cleanup();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
</script>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
3
|
+
import type { SignOutParams } from '../types.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Props for the SignOut component.
|
|
7
|
+
*
|
|
8
|
+
* Extends standard HTML button attributes with authentication-specific
|
|
9
|
+
* options for configuring the sign-out behavior.
|
|
10
|
+
*/
|
|
11
|
+
interface Props extends HTMLAttributes<'button'> {
|
|
12
|
+
/**
|
|
13
|
+
* Optional parameters to pass to the signOut function.
|
|
14
|
+
*
|
|
15
|
+
* These parameters control the sign-out behavior, such as redirect
|
|
16
|
+
* URLs and callback handling.
|
|
17
|
+
*/
|
|
18
|
+
params?: SignOutParams;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Generate a unique identifier for this component instance to avoid
|
|
22
|
+
// conflicts when multiple SignOut buttons are present on the same page
|
|
23
|
+
const key = crypto.randomUUID();
|
|
24
|
+
|
|
25
|
+
// noinspection JSUnusedGlobalSymbols
|
|
26
|
+
const { params, ...attrs } = Astro.props;
|
|
27
|
+
attrs.class = `signout-button ${attrs.class ?? ''}`;
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<button
|
|
31
|
+
{...attrs}
|
|
32
|
+
data-signout-key={key}
|
|
33
|
+
data-signout-params={params ? JSON.stringify(params) : undefined}
|
|
34
|
+
>
|
|
35
|
+
<slot />
|
|
36
|
+
</button>
|
|
37
|
+
|
|
38
|
+
<script>
|
|
39
|
+
import { signOut } from '../client.js';
|
|
40
|
+
|
|
41
|
+
function initializeSignOutButton(button: Element) {
|
|
42
|
+
if (button instanceof HTMLElement && !button.dataset.signoutInitialized) {
|
|
43
|
+
button.dataset.signoutInitialized = 'true';
|
|
44
|
+
|
|
45
|
+
button.addEventListener(
|
|
46
|
+
'click',
|
|
47
|
+
() => {
|
|
48
|
+
// Guard JSON parsing to prevent errors from malformed data
|
|
49
|
+
let params;
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
params = button.dataset.signoutParams
|
|
53
|
+
? JSON.parse(button.dataset.signoutParams)
|
|
54
|
+
: undefined;
|
|
55
|
+
} catch (e) {
|
|
56
|
+
console.warn('[astro-auth] Invalid signout params JSON:', e);
|
|
57
|
+
params = undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
signOut(params);
|
|
61
|
+
},
|
|
62
|
+
{ passive: true, capture: true },
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
document
|
|
68
|
+
.querySelectorAll('[data-signout-key]')
|
|
69
|
+
.forEach(initializeSignOutButton);
|
|
70
|
+
|
|
71
|
+
const observer = new MutationObserver((mutations) => {
|
|
72
|
+
mutations.forEach((mutation) => {
|
|
73
|
+
mutation.addedNodes.forEach((node) => {
|
|
74
|
+
if (node instanceof HTMLElement) {
|
|
75
|
+
if (node.hasAttribute('data-signout-key')) {
|
|
76
|
+
initializeSignOutButton(node);
|
|
77
|
+
}
|
|
78
|
+
node
|
|
79
|
+
.querySelectorAll('[data-signout-key]')
|
|
80
|
+
.forEach(initializeSignOutButton);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
87
|
+
|
|
88
|
+
// Clean up observer when page becomes hidden or before unload
|
|
89
|
+
const cleanup = () => {
|
|
90
|
+
observer.disconnect();
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
window.addEventListener('beforeunload', cleanup);
|
|
94
|
+
document.addEventListener('visibilitychange', () => {
|
|
95
|
+
if (document.hidden) {
|
|
96
|
+
cleanup();
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
</script>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { AstroComponentFactory } from 'astro/runtime/server/index.js';
|
|
2
|
+
|
|
3
|
+
declare const Auth: AstroComponentFactory;
|
|
4
|
+
// noinspection JSUnusedGlobalSymbols
|
|
5
|
+
declare const SignIn: AstroComponentFactory;
|
|
6
|
+
// noinspection JSUnusedGlobalSymbols
|
|
7
|
+
declare const SignOut: AstroComponentFactory;
|
|
8
|
+
|
|
9
|
+
export { Auth, SignIn, SignOut };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// src/components/index.ts
|
|
2
|
+
import { default as default2 } from "./Auth.astro";
|
|
3
|
+
import { default as default3 } from "./SignIn.astro";
|
|
4
|
+
import { default as default4 } from "./SignOut.astro";
|
|
5
|
+
export {
|
|
6
|
+
default2 as Auth,
|
|
7
|
+
default3 as SignIn,
|
|
8
|
+
default4 as SignOut
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/index.ts"],"sourcesContent":["/**\n * Re-exports Astro components for authentication UI.\n *\n * These components provide pre-built UI elements for sign-in, sign-out,\n * and session management in Astro applications.\n *\n * @public\n */\n\n// Component exports are consumed by end users importing from the package.\n// IDEs cannot detect usage across package boundaries, resulting in false\n// positive unused export warnings.\n// noinspection JSUnusedGlobalSymbols\nexport { default as Auth } from './Auth.astro';\n// noinspection JSUnusedGlobalSymbols\nexport { default as SignIn } from './SignIn.astro';\n// noinspection JSUnusedGlobalSymbols\nexport { default as SignOut } from './SignOut.astro';\n"],"mappings":";AAaA,SAAoB,WAAXA,gBAAuB;AAEhC,SAAoB,WAAXA,gBAAyB;AAElC,SAAoB,WAAXA,gBAA0B;","names":["default"]}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { AuthConfig } from '@auth/core/types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options specific to the Astro integration.
|
|
5
|
+
*
|
|
6
|
+
* @public
|
|
7
|
+
*/
|
|
8
|
+
interface AstroIntegrationOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Base path for authentication routes.
|
|
11
|
+
*
|
|
12
|
+
* @defaultValue '/api/auth'
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```js
|
|
16
|
+
* authAstro({ prefix: '/auth' })
|
|
17
|
+
* // Routes will be available at /auth/signin, /auth/signout, etc.
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
prefix?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Whether the integration should automatically inject authentication
|
|
23
|
+
* endpoints.
|
|
24
|
+
*
|
|
25
|
+
* Set to `false` if you want to manually define authentication routes.
|
|
26
|
+
*
|
|
27
|
+
* @defaultValue true
|
|
28
|
+
*/
|
|
29
|
+
injectEndpoints?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Path to the authentication configuration file.
|
|
32
|
+
*
|
|
33
|
+
* @defaultValue './auth.config'
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```js
|
|
37
|
+
* authAstro({ configFile: './config/authentication.ts' })
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
configFile?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Complete authentication configuration merging Astro-specific options
|
|
44
|
+
* with Auth.js core configuration.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
48
|
+
interface FullAuthConfig extends AstroIntegrationOptions, Omit<AuthConfig, 'raw'> {
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Environment variables structure used by Auth.js configuration.
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
*/
|
|
55
|
+
interface AuthEnvVars {
|
|
56
|
+
AUTH_SECRET?: string;
|
|
57
|
+
AUTH_TRUST_HOST?: string;
|
|
58
|
+
VERCEL?: string;
|
|
59
|
+
CF_PAGES?: string;
|
|
60
|
+
NODE_ENV?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Helper function to define authentication configuration with type safety.
|
|
64
|
+
*
|
|
65
|
+
* This function applies default values and reads environment variables to
|
|
66
|
+
* provide a complete configuration object. Environment variables are only
|
|
67
|
+
* used as fallbacks when values are not explicitly provided in the config.
|
|
68
|
+
*
|
|
69
|
+
* Environment variables read (in order of precedence for `trustHost`):
|
|
70
|
+
* - `config.trustHost`: Explicit config value (highest priority)
|
|
71
|
+
* - `AUTH_TRUST_HOST`: Whether to trust the X-Forwarded-Host header ("1"/"true"/"yes"/"on")
|
|
72
|
+
* - `VERCEL`: Automatically set by Vercel (any truthy value enables trustHost)
|
|
73
|
+
* - `CF_PAGES`: Automatically set by Cloudflare Pages (any truthy value enables trustHost)
|
|
74
|
+
* - `NODE_ENV`: Node environment (trustHost enabled if not 'production')
|
|
75
|
+
*
|
|
76
|
+
* For `secret`:
|
|
77
|
+
* - `config.secret`: Explicit config value (highest priority)
|
|
78
|
+
* - `AUTH_SECRET`: Environment variable fallback
|
|
79
|
+
*
|
|
80
|
+
* @param config - Authentication configuration object
|
|
81
|
+
* @param envOverride - Optional environment variables for testing
|
|
82
|
+
* @returns Configuration with defaults applied
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { defineConfig } from 'astro-auth'
|
|
87
|
+
* import GitHub from '@auth/core/providers/github'
|
|
88
|
+
*
|
|
89
|
+
* export default defineConfig({
|
|
90
|
+
* providers: [
|
|
91
|
+
* GitHub({
|
|
92
|
+
* clientId: process.env.GITHUB_ID,
|
|
93
|
+
* clientSecret: process.env.GITHUB_SECRET
|
|
94
|
+
* })
|
|
95
|
+
* ],
|
|
96
|
+
* secret: process.env.AUTH_SECRET
|
|
97
|
+
* })
|
|
98
|
+
* ```
|
|
99
|
+
*
|
|
100
|
+
* @public
|
|
101
|
+
*/
|
|
102
|
+
declare const defineConfig: (config: Readonly<FullAuthConfig>, envOverride?: AuthEnvVars) => FullAuthConfig;
|
|
103
|
+
|
|
104
|
+
export { type AstroIntegrationOptions as A, type FullAuthConfig as F, type AuthEnvVars as a, defineConfig as d };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { AstroIntegration } from 'astro';
|
|
2
|
+
import { A as AstroIntegrationOptions } from './config-lAdfi49m.js';
|
|
3
|
+
export { a as AuthEnvVars, F as FullAuthConfig, d as defineConfig } from './config-lAdfi49m.js';
|
|
4
|
+
import '@auth/core/types';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates an Astro integration for authentication using Auth.js.
|
|
8
|
+
*
|
|
9
|
+
* This integration handles:
|
|
10
|
+
* - Virtual module configuration for auth settings
|
|
11
|
+
* - Automatic route injection for authentication endpoints
|
|
12
|
+
* - Vite configuration for proper module resolution
|
|
13
|
+
*
|
|
14
|
+
* @param config - Configuration options for the integration
|
|
15
|
+
* @returns Configured Astro integration
|
|
16
|
+
* @throws {Error} When no adapter is configured (server-side rendering is required).
|
|
17
|
+
*
|
|
18
|
+
* @remarks
|
|
19
|
+
* This integration requires server-side rendering. Ensure you have
|
|
20
|
+
* configured an Astro adapter (e.g., `@astrojs/node`, `@astrojs/vercel`)
|
|
21
|
+
* in your Astro config.
|
|
22
|
+
*
|
|
23
|
+
* @public
|
|
24
|
+
*/
|
|
25
|
+
declare const _default: (config?: AstroIntegrationOptions) => AstroIntegration;
|
|
26
|
+
|
|
27
|
+
export { AstroIntegrationOptions, _default as default };
|