@auth/sveltekit
Advanced tools
Comparing version 1.7.4 to 1.8.0
@@ -1,18 +0,37 @@ | ||
import type { BuiltInProviderType, RedirectableProviderType } from "@auth/core/providers"; | ||
import type { LiteralUnion } from "./types.js"; | ||
export interface SignInOptions extends Record<string, unknown> { | ||
import type { ProviderId } from "@auth/core/providers"; | ||
export interface SignInOptions<Redirect extends boolean = true> extends Record<string, unknown> { | ||
/** @deprecated Use `redirectTo` instead. */ | ||
callbackUrl?: string; | ||
/** | ||
* Specify to which URL the user will be redirected after signing in. Defaults to the page URL the sign-in is initiated from. | ||
* Specify where the user should be redirected to after a successful signin. | ||
* | ||
* [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl) | ||
* By default, it is the page the sign-in was initiated from. | ||
*/ | ||
callbackUrl?: string; | ||
/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option) */ | ||
redirect?: boolean; | ||
redirectTo?: string; | ||
/** | ||
* You might want to deal with the signin response on the same page, instead of redirecting to another page. | ||
* For example, if an error occurs (like wrong credentials given by the user), you might want to show an inline error message on the input field. | ||
* | ||
* For this purpose, you can set this to option `redirect: false`. | ||
*/ | ||
redirect?: Redirect; | ||
} | ||
interface SignOutParams<R extends boolean = true> { | ||
/** [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl-1) */ | ||
export interface SignInResponse { | ||
error: string | undefined; | ||
code: string | undefined; | ||
status: number; | ||
ok: boolean; | ||
url: string | null; | ||
} | ||
export interface SignOutParams<Redirect extends boolean = true> { | ||
/** @deprecated Use `redirectTo` instead. */ | ||
callbackUrl?: string; | ||
/** | ||
* If you pass `redirect: false`, the page will not reload. | ||
* The session will be deleted, and `useSession` is notified, so any indication about the user will be shown as logged out automatically. | ||
* It can give a very nice experience for the user. | ||
*/ | ||
redirectTo?: string; | ||
/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */ | ||
redirect?: R; | ||
redirect?: Redirect; | ||
} | ||
@@ -27,10 +46,23 @@ /** Match `inputType` of `new URLSearchParams(inputType)` */ | ||
*/ | ||
export declare function signIn<P extends RedirectableProviderType | undefined = undefined>(providerId?: LiteralUnion<P extends RedirectableProviderType ? P | BuiltInProviderType : BuiltInProviderType>, options?: SignInOptions, authorizationParams?: SignInAuthorizationParams): Promise<Response | undefined>; | ||
/** | ||
* Signs the user out, by removing the session cookie. | ||
* Initiates a signin flow or sends the user to the signin page listing all possible providers. | ||
* Handles CSRF protection. | ||
* | ||
* [Documentation](https://authjs.dev/reference/sveltekit/client#signout) | ||
* @note This method can only be used from Client Components ("use client" or Pages Router). | ||
* For Server Actions, use the `signIn` method imported from the `auth` config. | ||
*/ | ||
export declare function signOut(options?: SignOutParams): Promise<void>; | ||
export {}; | ||
export declare function signIn(provider?: ProviderId, options?: SignInOptions<true>, authorizationParams?: SignInAuthorizationParams): Promise<void>; | ||
export declare function signIn(provider?: ProviderId, options?: SignInOptions<false>, authorizationParams?: SignInAuthorizationParams): Promise<SignInResponse>; | ||
export interface SignOutResponse { | ||
url: string; | ||
} | ||
/** | ||
* Initiate a signout, by destroying the current session. | ||
* Handles CSRF protection. | ||
* | ||
* @note This method can only be used from Client Components ("use client" or Pages Router). | ||
* For Server Actions, use the `signOut` method imported from the `auth` config. | ||
*/ | ||
export declare function signOut(options?: SignOutParams<true>): Promise<void>; | ||
export declare function signOut(options?: SignOutParams<false>): Promise<SignOutResponse>; | ||
//# sourceMappingURL=client.d.ts.map |
import { base } from "$app/paths"; | ||
/** | ||
* Client-side method to initiate a signin flow | ||
* or send the user to the signin page listing all possible providers. | ||
* | ||
* [Documentation](https://authjs.dev/reference/sveltekit/client#signin) | ||
*/ | ||
export async function signIn(providerId, options, authorizationParams) { | ||
const { callbackUrl = window.location.href, redirect = true } = options ?? {}; | ||
// TODO: Support custom providers | ||
const isCredentials = providerId === "credentials"; | ||
const isEmail = providerId === "email"; | ||
const isSupportingReturn = isCredentials || isEmail; | ||
const basePath = base ?? ""; | ||
const signInUrl = `${basePath}/auth/${isCredentials ? "callback" : "signin"}/${providerId}`; | ||
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`; | ||
const res = await fetch(_signInUrl, { | ||
export async function signIn(provider, options, authorizationParams) { | ||
const { callbackUrl, ...rest } = options ?? {}; | ||
const { redirect = true, redirectTo = callbackUrl ?? window.location.href, ...signInParams } = rest; | ||
const baseUrl = base ?? ""; | ||
const signInUrl = `${baseUrl}/${provider === "credentials" ? "callback" : "signin"}/${provider}`; | ||
const res = await fetch(`${signInUrl}?${new URLSearchParams(authorizationParams)}`, { | ||
method: "post", | ||
@@ -23,28 +13,30 @@ headers: { | ||
}, | ||
// @ts-ignore | ||
body: new URLSearchParams({ | ||
...options, | ||
callbackUrl, | ||
...signInParams, | ||
callbackUrl: redirectTo, | ||
}), | ||
}); | ||
const data = await res.clone().json(); | ||
if (redirect || !isSupportingReturn) { | ||
// TODO: Do not redirect for Credentials and Email providers by default in next major | ||
window.location.href = data.url ?? callbackUrl; | ||
const data = await res.json(); | ||
if (redirect) { | ||
const url = data.url ?? redirectTo; | ||
window.location.href = url; | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (data.url.includes("#")) | ||
if (url.includes("#")) | ||
window.location.reload(); | ||
return; | ||
} | ||
return res; | ||
const error = new URL(data.url).searchParams.get("error") ?? undefined; | ||
const code = new URL(data.url).searchParams.get("code") ?? undefined; | ||
return { | ||
error, | ||
code, | ||
status: res.status, | ||
ok: res.ok, | ||
url: error ? null : data.url, | ||
}; | ||
} | ||
/** | ||
* Signs the user out, by removing the session cookie. | ||
* | ||
* [Documentation](https://authjs.dev/reference/sveltekit/client#signout) | ||
*/ | ||
export async function signOut(options) { | ||
const { callbackUrl = window.location.href } = options ?? {}; | ||
const basePath = base ?? ""; | ||
const res = await fetch(`${basePath}/auth/signout`, { | ||
const { redirect = true, redirectTo = options?.callbackUrl ?? window.location.href, } = options ?? {}; | ||
const baseUrl = base ?? ""; | ||
const res = await fetch(`${baseUrl}/signout`, { | ||
method: "post", | ||
@@ -56,11 +48,15 @@ headers: { | ||
body: new URLSearchParams({ | ||
callbackUrl, | ||
callbackUrl: redirectTo, | ||
}), | ||
}); | ||
const data = await res.json(); | ||
const url = data.url ?? callbackUrl; | ||
window.location.href = url; | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (url.includes("#")) | ||
window.location.reload(); | ||
if (redirect) { | ||
const url = data.url ?? redirectTo; | ||
window.location.href = url; | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (url.includes("#")) | ||
window.location.reload(); | ||
return; | ||
} | ||
return data; | ||
} |
@@ -68,8 +68,10 @@ /** | ||
* | ||
* `<SignIn />` and `<SignOut />` are components that `@auth/sveltekit` provides out of the box - they handle the sign-in/signout flow, and can be used as-is as a starting point or customized for your own components. This is an example of how to use the `SignIn` and `SignOut` components to login and logout using SvelteKit's server-side form actions. You will need two things to make this work: | ||
* `<SignIn />` and `<SignOut />` are components that `@auth/sveltekit` provides out of the box - they handle the sign-in/signout flow, and can be used as-is as a starting point or customized for your own components. This is an example of how to use the `SignIn` and `SignOut` components to login and logout using SvelteKit's server-side form actions. Another example is available on [our svelte-auth-example repo](https://github.com/nextauthjs/sveltekit-auth-example). | ||
* | ||
* 1. Using the components in your SvelteKit app's frontend | ||
* You will need two things to make this work: | ||
* | ||
* 1. Using the components in your SvelteKit app's frontend (for instance `src/routes/+page.svelte`) | ||
* 2. Add the required `page.server.ts` at `/signin` (for `SignIn`) and `/signout` (for `SignOut`) to handle the form actions | ||
* | ||
* ```ts | ||
* ```ts title="src/routes/+page.svelte" | ||
* <script> | ||
@@ -127,4 +129,7 @@ * import { SignIn, SignOut } from "@auth/sveltekit/components" | ||
* | ||
* ```ts title="src/routes/index.svelte" | ||
* import { signIn, signOut } from "@auth/sveltekit/client" | ||
* ```ts title="src/routes/+page.svelte" | ||
* <script> | ||
* import { signIn, signOut } from "@auth/sveltekit/client" | ||
* let password | ||
* </script> | ||
* | ||
@@ -157,3 +162,3 @@ * <nav> | ||
* </button> | ||
* <button on:click={() => signOut()})}> | ||
* <button on:click={() => signOut()}> | ||
* Sign Out | ||
@@ -183,3 +188,3 @@ * </button> | ||
* What you return in the function `LayoutServerLoad` will be available inside the `$page` store, in the `data` property: `$page.data`. | ||
* In this case we return an object with the 'session' property which is what we are accessing in the other code paths. | ||
* In this case we return an object with the `session` property which is what we are accessing in the other code paths. | ||
* | ||
@@ -193,3 +198,3 @@ * ## Handling authorization | ||
* The simplest case is protecting a single page, in which case you should put the logic in the `+page.server.ts` file. | ||
* Notice in this case that you could also await event.parent and grab the session from there, however this implementation works even if you haven't done the above in your root `+layout.server.ts` | ||
* Notice in this case that you could also `await event.parent` and grab the session from there, however this implementation works even if you haven't done the above in your root `+layout.server.ts` | ||
* | ||
@@ -210,3 +215,3 @@ * ```ts | ||
* Not doing so can lead to users being able to incorrectly access protected information in the case the `+layout.server.ts` does not run for that page load. | ||
* This code sample already implements the correct method by using `const { session } = await parent();`. For more information on SvelteKit's `load` functionality behaviour and its implications on authentication, see this [SvelteKit docs section](https://kit.svelte.dev/docs/load#implications-for-authentication). | ||
* For more information on SvelteKit's `load` functionality behaviour and its implications on authentication, see this [SvelteKit docs section](https://kit.svelte.dev/docs/load#implications-for-authentication). | ||
* ::: | ||
@@ -213,0 +218,0 @@ * |
@@ -68,8 +68,10 @@ /** | ||
* | ||
* `<SignIn />` and `<SignOut />` are components that `@auth/sveltekit` provides out of the box - they handle the sign-in/signout flow, and can be used as-is as a starting point or customized for your own components. This is an example of how to use the `SignIn` and `SignOut` components to login and logout using SvelteKit's server-side form actions. You will need two things to make this work: | ||
* `<SignIn />` and `<SignOut />` are components that `@auth/sveltekit` provides out of the box - they handle the sign-in/signout flow, and can be used as-is as a starting point or customized for your own components. This is an example of how to use the `SignIn` and `SignOut` components to login and logout using SvelteKit's server-side form actions. Another example is available on [our svelte-auth-example repo](https://github.com/nextauthjs/sveltekit-auth-example). | ||
* | ||
* 1. Using the components in your SvelteKit app's frontend | ||
* You will need two things to make this work: | ||
* | ||
* 1. Using the components in your SvelteKit app's frontend (for instance `src/routes/+page.svelte`) | ||
* 2. Add the required `page.server.ts` at `/signin` (for `SignIn`) and `/signout` (for `SignOut`) to handle the form actions | ||
* | ||
* ```ts | ||
* ```ts title="src/routes/+page.svelte" | ||
* <script> | ||
@@ -127,4 +129,7 @@ * import { SignIn, SignOut } from "@auth/sveltekit/components" | ||
* | ||
* ```ts title="src/routes/index.svelte" | ||
* import { signIn, signOut } from "@auth/sveltekit/client" | ||
* ```ts title="src/routes/+page.svelte" | ||
* <script> | ||
* import { signIn, signOut } from "@auth/sveltekit/client" | ||
* let password | ||
* </script> | ||
* | ||
@@ -157,3 +162,3 @@ * <nav> | ||
* </button> | ||
* <button on:click={() => signOut()})}> | ||
* <button on:click={() => signOut()}> | ||
* Sign Out | ||
@@ -183,3 +188,3 @@ * </button> | ||
* What you return in the function `LayoutServerLoad` will be available inside the `$page` store, in the `data` property: `$page.data`. | ||
* In this case we return an object with the 'session' property which is what we are accessing in the other code paths. | ||
* In this case we return an object with the `session` property which is what we are accessing in the other code paths. | ||
* | ||
@@ -193,3 +198,3 @@ * ## Handling authorization | ||
* The simplest case is protecting a single page, in which case you should put the logic in the `+page.server.ts` file. | ||
* Notice in this case that you could also await event.parent and grab the session from there, however this implementation works even if you haven't done the above in your root `+layout.server.ts` | ||
* Notice in this case that you could also `await event.parent` and grab the session from there, however this implementation works even if you haven't done the above in your root `+layout.server.ts` | ||
* | ||
@@ -210,3 +215,3 @@ * ```ts | ||
* Not doing so can lead to users being able to incorrectly access protected information in the case the `+layout.server.ts` does not run for that page load. | ||
* This code sample already implements the correct method by using `const { session } = await parent();`. For more information on SvelteKit's `load` functionality behaviour and its implications on authentication, see this [SvelteKit docs section](https://kit.svelte.dev/docs/load#implications-for-authentication). | ||
* For more information on SvelteKit's `load` functionality behaviour and its implications on authentication, see this [SvelteKit docs section](https://kit.svelte.dev/docs/load#implications-for-authentication). | ||
* ::: | ||
@@ -213,0 +218,0 @@ * |
import type { AuthConfig } from "@auth/core"; | ||
import type { BuiltInProviderType } from "@auth/core/providers"; | ||
import type { ProviderId } from "@auth/core/providers"; | ||
import type { Session } from "@auth/core/types"; | ||
export type LiteralUnion<T extends U, U = string> = T | (U & Record<never, never>); | ||
/** Configure the {@link SvelteKitAuth} method. */ | ||
@@ -14,5 +13,5 @@ export interface SvelteKitAuthConfig extends Omit<AuthConfig, "raw"> { | ||
getSession(): Promise<Session | null>; | ||
signIn: <P extends BuiltInProviderType | (string & NonNullable<unknown>), R extends boolean = true>( | ||
signIn: <Redirect extends boolean = true>( | ||
/** Provider to sign in to */ | ||
provider?: P, // See: https://github.com/microsoft/TypeScript/issues/29729 | ||
provider?: ProviderId, // See: https://github.com/microsoft/TypeScript/issues/29729 | ||
options?: FormData | ({ | ||
@@ -22,10 +21,10 @@ /** The URL to redirect to after signing in. By default, the user is redirected to the current page. */ | ||
/** If set to `false`, the `signIn` method will return the URL to redirect to instead of redirecting automatically. */ | ||
redirect?: R; | ||
} & Record<string, any>), authorizationParams?: string[][] | Record<string, string> | string | URLSearchParams) => Promise<R extends false ? any : never>; | ||
signOut: <R extends boolean = true>(options?: { | ||
redirect?: Redirect; | ||
} & Record<string, any>), authorizationParams?: string[][] | Record<string, string> | string | URLSearchParams) => Promise<Redirect extends false ? any : never>; | ||
signOut: <Redirect extends boolean = true>(options?: { | ||
/** The URL to redirect to after signing out. By default, the user is redirected to the current page. */ | ||
redirectTo?: string; | ||
/** If set to `false`, the `signOut` method will return the URL to redirect to instead of redirecting automatically. */ | ||
redirect?: R; | ||
}) => Promise<R extends false ? any : never>; | ||
redirect?: Redirect; | ||
}) => Promise<Redirect extends false ? any : never>; | ||
} | ||
@@ -32,0 +31,0 @@ interface PageData { |
@@ -1,11 +0,9 @@ | ||
import type { BuiltInProviderType, RedirectableProviderType } from "@auth/core/providers"; | ||
import type { SignInOptions, SignInAuthorizationParams } from "./client.js"; | ||
import type { LiteralUnion } from "./types.js"; | ||
import type { ProviderId } from "@auth/core/providers"; | ||
import type { SignInAuthorizationParams, SignInOptions, SignInResponse } from "./client.js"; | ||
/** | ||
* Client-side method to initiate a webauthn signin flow | ||
* or send the user to the signin page listing all possible providers. | ||
* | ||
* [Documentation](https://authjs.dev/reference/sveltekit/client#signin) | ||
* Initiate a WebAuthn signin flow. | ||
* @see https://authjs.dev/getting-started/authentication/webauthn | ||
*/ | ||
export declare function signIn<P extends RedirectableProviderType | undefined = undefined>(providerId?: LiteralUnion<P extends RedirectableProviderType ? P | BuiltInProviderType : BuiltInProviderType>, options?: SignInOptions, authorizationParams?: SignInAuthorizationParams): Promise<Response | undefined>; | ||
export declare function signIn(provider?: ProviderId, options?: SignInOptions<true>, authorizationParams?: SignInAuthorizationParams): Promise<void>; | ||
export declare function signIn(provider?: ProviderId, options?: SignInOptions<false>, authorizationParams?: SignInAuthorizationParams): Promise<SignInResponse>; | ||
//# sourceMappingURL=webauthn.d.ts.map |
import { base } from "$app/paths"; | ||
import { startAuthentication, startRegistration } from "@simplewebauthn/browser"; | ||
const logger = { | ||
debug: console.debug, | ||
error: console.error, | ||
warn: console.warn, | ||
}; | ||
/** | ||
* Fetch webauthn options from server and prompt user for authentication or registration. | ||
* Returns either the completed WebAuthn response or an error request. | ||
* | ||
* @param providerId provider ID | ||
* @param options SignInOptions | ||
* @returns WebAuthn response or error | ||
*/ | ||
async function webAuthnOptions(providerId, options) { | ||
const baseUrl = `${base}/auth`; | ||
async function webAuthnOptions(providerID, options) { | ||
const baseUrl = base ?? ""; | ||
// @ts-expect-error | ||
const params = new URLSearchParams(options); | ||
const optionsResp = await fetch(`${baseUrl}/webauthn-options/${providerId}?${params}`); | ||
const optionsResp = await fetch(`${baseUrl}/webauthn-options/${providerID}?${params}`); | ||
if (!optionsResp.ok) { | ||
@@ -29,30 +30,23 @@ return { error: optionsResp }; | ||
} | ||
/** | ||
* Client-side method to initiate a webauthn signin flow | ||
* or send the user to the signin page listing all possible providers. | ||
* | ||
* [Documentation](https://authjs.dev/reference/sveltekit/client#signin) | ||
*/ | ||
export async function signIn(providerId, options, authorizationParams) { | ||
const { callbackUrl = window.location.href, redirect = true } = options ?? {}; | ||
// TODO: Support custom providers | ||
const isCredentials = providerId === "credentials"; | ||
const isEmail = providerId === "email"; | ||
const isWebAuthn = providerId === "webauthn"; | ||
const isSupportingReturn = isCredentials || isEmail || isWebAuthn; | ||
const basePath = base ?? ""; | ||
const signInUrl = `${basePath}/auth/${isCredentials || isWebAuthn ? "callback" : "signin"}/${providerId}`; | ||
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`; | ||
// Execute WebAuthn client flow if needed | ||
export async function signIn(provider, options, authorizationParams) { | ||
const { callbackUrl, ...rest } = options ?? {}; | ||
const { redirectTo = callbackUrl ?? window.location.href, redirect = true, ...signInParams } = rest; | ||
const baseUrl = base ?? ""; | ||
if (!provider || provider !== "webauthn") { | ||
// TODO: Add docs link with explanation | ||
throw new TypeError([ | ||
`Provider id "${provider}" does not refer to a WebAuthn provider.`, | ||
'Please use `import { signIn } from "@auth/sveltekit/client"` instead.', | ||
].join("\n")); | ||
} | ||
const webAuthnBody = {}; | ||
if (isWebAuthn) { | ||
const { data, error, action } = await webAuthnOptions(providerId, options); | ||
if (error) { | ||
// logger.error(new Error(await error.text())) | ||
return; | ||
} | ||
webAuthnBody.data = JSON.stringify(data); | ||
webAuthnBody.action = action; | ||
const webAuthnResponse = await webAuthnOptions(provider, signInParams); | ||
if (webAuthnResponse.error) { | ||
logger.error(new Error(await webAuthnResponse.error.text())); | ||
return; | ||
} | ||
const res = await fetch(_signInUrl, { | ||
webAuthnBody.data = JSON.stringify(webAuthnResponse.data); | ||
webAuthnBody.action = webAuthnResponse.action; | ||
const signInUrl = `${baseUrl}/callback/${provider}?${new URLSearchParams(authorizationParams)}`; | ||
const res = await fetch(signInUrl, { | ||
method: "post", | ||
@@ -63,19 +57,26 @@ headers: { | ||
}, | ||
// @ts-ignore | ||
body: new URLSearchParams({ | ||
...options, | ||
callbackUrl, | ||
...signInParams, | ||
...webAuthnBody, | ||
callbackUrl: redirectTo, | ||
}), | ||
}); | ||
const data = await res.clone().json(); | ||
if (redirect || !isSupportingReturn) { | ||
// TODO: Do not redirect for Credentials and Email providers by default in next major | ||
window.location.href = data.url ?? callbackUrl; | ||
const data = await res.json(); | ||
if (redirect) { | ||
const url = data.url ?? callbackUrl; | ||
window.location.href = url; | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (data.url.includes("#")) | ||
if (url.includes("#")) | ||
window.location.reload(); | ||
return; | ||
} | ||
return res; | ||
const error = new URL(data.url).searchParams.get("error"); | ||
const code = new URL(data.url).searchParams.get("code"); | ||
return { | ||
error, | ||
code, | ||
status: res.status, | ||
ok: res.ok, | ||
url: error ? null : data.url, | ||
}; | ||
} |
{ | ||
"name": "@auth/sveltekit", | ||
"version": "1.7.4", | ||
"version": "1.8.0", | ||
"description": "Authentication for SvelteKit.", | ||
@@ -44,3 +44,3 @@ "keywords": [ | ||
"set-cookie-parser": "^2.7.0", | ||
"@auth/core": "0.37.4" | ||
"@auth/core": "0.38.0" | ||
}, | ||
@@ -47,0 +47,0 @@ "peerDependencies": { |
@@ -12,3 +12,3 @@ <p align="center"> | ||
<a href="https://www.npmtrends.com/@auth/sveltekit"><img src="https://img.shields.io/npm/dm/@auth/sveltekit?style=flat-square&color=cyan" alt="Downloads" /></a> | ||
<a href="https://github.com/nextauthjs/next-auth/stargazers"><img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange" alt="Github Stars" /></a> | ||
<a href="https://github.com/nextauthjs/next-auth/stargazers"><img src="https://img.shields.io/github/stars/nextauthjs/next-auth?style=flat-square&color=orange" alt="GitHub Stars" /></a> | ||
<img src="https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=fff&style=flat-square" alt="TypeScript" /> | ||
@@ -15,0 +15,0 @@ </p> |
import { base } from "$app/paths" | ||
import type { | ||
BuiltInProviderType, | ||
RedirectableProviderType, | ||
} from "@auth/core/providers" | ||
import type { LiteralUnion } from "./types.js" | ||
import type { ProviderId } from "@auth/core/providers" | ||
/* | ||
* @internal | ||
*/ | ||
export interface SignInOptions extends Record<string, unknown> { | ||
export interface SignInOptions<Redirect extends boolean = true> | ||
extends Record<string, unknown> { | ||
/** @deprecated Use `redirectTo` instead. */ | ||
callbackUrl?: string | ||
/** | ||
* Specify to which URL the user will be redirected after signing in. Defaults to the page URL the sign-in is initiated from. | ||
* Specify where the user should be redirected to after a successful signin. | ||
* | ||
* [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl) | ||
* By default, it is the page the sign-in was initiated from. | ||
*/ | ||
callbackUrl?: string | ||
/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option) */ | ||
redirect?: boolean | ||
redirectTo?: string | ||
/** | ||
* You might want to deal with the signin response on the same page, instead of redirecting to another page. | ||
* For example, if an error occurs (like wrong credentials given by the user), you might want to show an inline error message on the input field. | ||
* | ||
* For this purpose, you can set this to option `redirect: false`. | ||
*/ | ||
redirect?: Redirect | ||
} | ||
interface SignOutParams<R extends boolean = true> { | ||
/** [Documentation](https://next-auth.js.org/getting-started/client#specifying-a-callbackurl-1) */ | ||
export interface SignInResponse { | ||
error: string | undefined | ||
code: string | undefined | ||
status: number | ||
ok: boolean | ||
url: string | null | ||
} | ||
export interface SignOutParams<Redirect extends boolean = true> { | ||
/** @deprecated Use `redirectTo` instead. */ | ||
callbackUrl?: string | ||
/** | ||
* If you pass `redirect: false`, the page will not reload. | ||
* The session will be deleted, and `useSession` is notified, so any indication about the user will be shown as logged out automatically. | ||
* It can give a very nice experience for the user. | ||
*/ | ||
redirectTo?: string | ||
/** [Documentation](https://next-auth.js.org/getting-started/client#using-the-redirect-false-option-1 */ | ||
redirect?: R | ||
redirect?: Redirect | ||
} | ||
@@ -42,62 +57,100 @@ | ||
*/ | ||
export async function signIn< | ||
P extends RedirectableProviderType | undefined = undefined, | ||
>( | ||
providerId?: LiteralUnion< | ||
P extends RedirectableProviderType | ||
? P | BuiltInProviderType | ||
: BuiltInProviderType | ||
>, | ||
options?: SignInOptions, | ||
/** | ||
* Initiates a signin flow or sends the user to the signin page listing all possible providers. | ||
* Handles CSRF protection. | ||
* | ||
* @note This method can only be used from Client Components ("use client" or Pages Router). | ||
* For Server Actions, use the `signIn` method imported from the `auth` config. | ||
*/ | ||
export async function signIn( | ||
provider?: ProviderId, | ||
options?: SignInOptions<true>, | ||
authorizationParams?: SignInAuthorizationParams | ||
) { | ||
const { callbackUrl = window.location.href, redirect = true } = options ?? {} | ||
): Promise<void> | ||
export async function signIn( | ||
provider?: ProviderId, | ||
options?: SignInOptions<false>, | ||
authorizationParams?: SignInAuthorizationParams | ||
): Promise<SignInResponse> | ||
export async function signIn<Redirect extends boolean = true>( | ||
provider?: ProviderId, | ||
options?: SignInOptions<Redirect>, | ||
authorizationParams?: SignInAuthorizationParams | ||
): Promise<SignInResponse | void> { | ||
const { callbackUrl, ...rest } = options ?? {} | ||
const { | ||
redirect = true, | ||
redirectTo = callbackUrl ?? window.location.href, | ||
...signInParams | ||
} = rest | ||
// TODO: Support custom providers | ||
const isCredentials = providerId === "credentials" | ||
const isEmail = providerId === "email" | ||
const isSupportingReturn = isCredentials || isEmail | ||
const baseUrl = base ?? "" | ||
const basePath = base ?? "" | ||
const signInUrl = `${basePath}/auth/${ | ||
isCredentials ? "callback" : "signin" | ||
}/${providerId}` | ||
const signInUrl = `${baseUrl}/${ | ||
provider === "credentials" ? "callback" : "signin" | ||
}/${provider}` | ||
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}` | ||
const res = await fetch( | ||
`${signInUrl}?${new URLSearchParams(authorizationParams)}`, | ||
{ | ||
method: "post", | ||
headers: { | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
"X-Auth-Return-Redirect": "1", | ||
}, | ||
body: new URLSearchParams({ | ||
...signInParams, | ||
callbackUrl: redirectTo, | ||
}), | ||
} | ||
) | ||
const res = await fetch(_signInUrl, { | ||
method: "post", | ||
headers: { | ||
"Content-Type": "application/x-www-form-urlencoded", | ||
"X-Auth-Return-Redirect": "1", | ||
}, | ||
// @ts-ignore | ||
body: new URLSearchParams({ | ||
...options, | ||
callbackUrl, | ||
}), | ||
}) | ||
const data = await res.json() | ||
const data = await res.clone().json() | ||
if (redirect || !isSupportingReturn) { | ||
// TODO: Do not redirect for Credentials and Email providers by default in next major | ||
window.location.href = data.url ?? callbackUrl | ||
if (redirect) { | ||
const url = data.url ?? redirectTo | ||
window.location.href = url | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (data.url.includes("#")) window.location.reload() | ||
if (url.includes("#")) window.location.reload() | ||
return | ||
} | ||
return res | ||
const error = new URL(data.url).searchParams.get("error") ?? undefined | ||
const code = new URL(data.url).searchParams.get("code") ?? undefined | ||
return { | ||
error, | ||
code, | ||
status: res.status, | ||
ok: res.ok, | ||
url: error ? null : data.url, | ||
} | ||
} | ||
export interface SignOutResponse { | ||
url: string | ||
} | ||
/** | ||
* Signs the user out, by removing the session cookie. | ||
* Initiate a signout, by destroying the current session. | ||
* Handles CSRF protection. | ||
* | ||
* [Documentation](https://authjs.dev/reference/sveltekit/client#signout) | ||
* @note This method can only be used from Client Components ("use client" or Pages Router). | ||
* For Server Actions, use the `signOut` method imported from the `auth` config. | ||
*/ | ||
export async function signOut(options?: SignOutParams) { | ||
const { callbackUrl = window.location.href } = options ?? {} | ||
const basePath = base ?? "" | ||
const res = await fetch(`${basePath}/auth/signout`, { | ||
export async function signOut(options?: SignOutParams<true>): Promise<void> | ||
export async function signOut( | ||
options?: SignOutParams<false> | ||
): Promise<SignOutResponse> | ||
export async function signOut<R extends boolean = true>( | ||
options?: SignOutParams<R> | ||
): Promise<SignOutResponse | void> { | ||
const { | ||
redirect = true, | ||
redirectTo = options?.callbackUrl ?? window.location.href, | ||
} = options ?? {} | ||
const baseUrl = base ?? "" | ||
const res = await fetch(`${baseUrl}/signout`, { | ||
method: "post", | ||
@@ -109,3 +162,3 @@ headers: { | ||
body: new URLSearchParams({ | ||
callbackUrl, | ||
callbackUrl: redirectTo, | ||
}), | ||
@@ -115,6 +168,11 @@ }) | ||
const url = data.url ?? callbackUrl | ||
window.location.href = url | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (url.includes("#")) window.location.reload() | ||
if (redirect) { | ||
const url = data.url ?? redirectTo | ||
window.location.href = url | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (url.includes("#")) window.location.reload() | ||
return | ||
} | ||
return data | ||
} |
@@ -68,8 +68,10 @@ /** | ||
* | ||
* `<SignIn />` and `<SignOut />` are components that `@auth/sveltekit` provides out of the box - they handle the sign-in/signout flow, and can be used as-is as a starting point or customized for your own components. This is an example of how to use the `SignIn` and `SignOut` components to login and logout using SvelteKit's server-side form actions. You will need two things to make this work: | ||
* `<SignIn />` and `<SignOut />` are components that `@auth/sveltekit` provides out of the box - they handle the sign-in/signout flow, and can be used as-is as a starting point or customized for your own components. This is an example of how to use the `SignIn` and `SignOut` components to login and logout using SvelteKit's server-side form actions. Another example is available on [our svelte-auth-example repo](https://github.com/nextauthjs/sveltekit-auth-example). | ||
* | ||
* 1. Using the components in your SvelteKit app's frontend | ||
* You will need two things to make this work: | ||
* | ||
* 1. Using the components in your SvelteKit app's frontend (for instance `src/routes/+page.svelte`) | ||
* 2. Add the required `page.server.ts` at `/signin` (for `SignIn`) and `/signout` (for `SignOut`) to handle the form actions | ||
* | ||
* ```ts | ||
* ```ts title="src/routes/+page.svelte" | ||
* <script> | ||
@@ -127,4 +129,7 @@ * import { SignIn, SignOut } from "@auth/sveltekit/components" | ||
* | ||
* ```ts title="src/routes/index.svelte" | ||
* import { signIn, signOut } from "@auth/sveltekit/client" | ||
* ```ts title="src/routes/+page.svelte" | ||
* <script> | ||
* import { signIn, signOut } from "@auth/sveltekit/client" | ||
* let password | ||
* </script> | ||
* | ||
@@ -157,3 +162,3 @@ * <nav> | ||
* </button> | ||
* <button on:click={() => signOut()})}> | ||
* <button on:click={() => signOut()}> | ||
* Sign Out | ||
@@ -183,3 +188,3 @@ * </button> | ||
* What you return in the function `LayoutServerLoad` will be available inside the `$page` store, in the `data` property: `$page.data`. | ||
* In this case we return an object with the 'session' property which is what we are accessing in the other code paths. | ||
* In this case we return an object with the `session` property which is what we are accessing in the other code paths. | ||
* | ||
@@ -193,3 +198,3 @@ * ## Handling authorization | ||
* The simplest case is protecting a single page, in which case you should put the logic in the `+page.server.ts` file. | ||
* Notice in this case that you could also await event.parent and grab the session from there, however this implementation works even if you haven't done the above in your root `+layout.server.ts` | ||
* Notice in this case that you could also `await event.parent` and grab the session from there, however this implementation works even if you haven't done the above in your root `+layout.server.ts` | ||
* | ||
@@ -210,3 +215,3 @@ * ```ts | ||
* Not doing so can lead to users being able to incorrectly access protected information in the case the `+layout.server.ts` does not run for that page load. | ||
* This code sample already implements the correct method by using `const { session } = await parent();`. For more information on SvelteKit's `load` functionality behaviour and its implications on authentication, see this [SvelteKit docs section](https://kit.svelte.dev/docs/load#implications-for-authentication). | ||
* For more information on SvelteKit's `load` functionality behaviour and its implications on authentication, see this [SvelteKit docs section](https://kit.svelte.dev/docs/load#implications-for-authentication). | ||
* ::: | ||
@@ -213,0 +218,0 @@ * |
import type { AuthConfig } from "@auth/core" | ||
import type { BuiltInProviderType } from "@auth/core/providers" | ||
import type { ProviderId } from "@auth/core/providers" | ||
import type { Session } from "@auth/core/types" | ||
export type LiteralUnion<T extends U, U = string> = | ||
| T | ||
| (U & Record<never, never>) | ||
/** Configure the {@link SvelteKitAuth} method. */ | ||
@@ -19,8 +15,5 @@ export interface SvelteKitAuthConfig extends Omit<AuthConfig, "raw"> {} | ||
getSession(): Promise<Session | null> | ||
signIn: < | ||
P extends BuiltInProviderType | (string & NonNullable<unknown>), | ||
R extends boolean = true, | ||
>( | ||
signIn: <Redirect extends boolean = true>( | ||
/** Provider to sign in to */ | ||
provider?: P, // See: https://github.com/microsoft/TypeScript/issues/29729 | ||
provider?: ProviderId, // See: https://github.com/microsoft/TypeScript/issues/29729 | ||
options?: | ||
@@ -32,3 +25,3 @@ | FormData | ||
/** If set to `false`, the `signIn` method will return the URL to redirect to instead of redirecting automatically. */ | ||
redirect?: R | ||
redirect?: Redirect | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -42,3 +35,3 @@ } & Record<string, any>), | ||
) => Promise< | ||
R extends false | ||
Redirect extends false | ||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -48,9 +41,9 @@ any | ||
> | ||
signOut: <R extends boolean = true>(options?: { | ||
signOut: <Redirect extends boolean = true>(options?: { | ||
/** The URL to redirect to after signing out. By default, the user is redirected to the current page. */ | ||
redirectTo?: string | ||
/** If set to `false`, the `signOut` method will return the URL to redirect to instead of redirecting automatically. */ | ||
redirect?: R | ||
redirect?: Redirect | ||
}) => Promise< | ||
R extends false | ||
Redirect extends false | ||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
@@ -57,0 +50,0 @@ any |
import { base } from "$app/paths" | ||
import { startAuthentication, startRegistration } from "@simplewebauthn/browser" | ||
import type { LoggerInstance } from "@auth/core/types" | ||
import type { WebAuthnOptionsResponseBody } from "@auth/core/types" | ||
import type { ProviderId } from "@auth/core/providers" | ||
import type { | ||
BuiltInProviderType, | ||
RedirectableProviderType, | ||
} from "@auth/core/providers" | ||
import type { WebAuthnOptionsResponseBody } from "@auth/core/types" | ||
import type { SignInOptions, SignInAuthorizationParams } from "./client.js" | ||
import type { LiteralUnion } from "./types.js" | ||
SignInAuthorizationParams, | ||
SignInOptions, | ||
SignInResponse, | ||
} from "./client.js" | ||
const logger: LoggerInstance = { | ||
debug: console.debug, | ||
error: console.error, | ||
warn: console.warn, | ||
} | ||
/** | ||
* Fetch webauthn options from server and prompt user for authentication or registration. | ||
* Returns either the completed WebAuthn response or an error request. | ||
* | ||
* @param providerId provider ID | ||
* @param options SignInOptions | ||
* @returns WebAuthn response or error | ||
*/ | ||
async function webAuthnOptions(providerId: string, options?: SignInOptions) { | ||
const baseUrl = `${base}/auth` | ||
async function webAuthnOptions( | ||
providerID: ProviderId, | ||
options?: Omit<SignInOptions, "redirect"> | ||
) { | ||
const baseUrl = base ?? "" | ||
@@ -27,3 +33,3 @@ // @ts-expect-error | ||
const optionsResp = await fetch( | ||
`${baseUrl}/webauthn-options/${providerId}?${params}` | ||
`${baseUrl}/webauthn-options/${providerID}?${params}` | ||
) | ||
@@ -45,46 +51,50 @@ if (!optionsResp.ok) { | ||
/** | ||
* Client-side method to initiate a webauthn signin flow | ||
* or send the user to the signin page listing all possible providers. | ||
* | ||
* [Documentation](https://authjs.dev/reference/sveltekit/client#signin) | ||
* Initiate a WebAuthn signin flow. | ||
* @see https://authjs.dev/getting-started/authentication/webauthn | ||
*/ | ||
export async function signIn< | ||
P extends RedirectableProviderType | undefined = undefined, | ||
>( | ||
providerId?: LiteralUnion< | ||
P extends RedirectableProviderType | ||
? P | BuiltInProviderType | ||
: BuiltInProviderType | ||
>, | ||
options?: SignInOptions, | ||
export async function signIn( | ||
provider?: ProviderId, | ||
options?: SignInOptions<true>, | ||
authorizationParams?: SignInAuthorizationParams | ||
) { | ||
const { callbackUrl = window.location.href, redirect = true } = options ?? {} | ||
): Promise<void> | ||
export async function signIn( | ||
provider?: ProviderId, | ||
options?: SignInOptions<false>, | ||
authorizationParams?: SignInAuthorizationParams | ||
): Promise<SignInResponse> | ||
export async function signIn<Redirect extends boolean = true>( | ||
provider?: ProviderId, | ||
options?: SignInOptions<Redirect>, | ||
authorizationParams?: SignInAuthorizationParams | ||
): Promise<SignInResponse | void> { | ||
const { callbackUrl, ...rest } = options ?? {} | ||
const { | ||
redirectTo = callbackUrl ?? window.location.href, | ||
redirect = true, | ||
...signInParams | ||
} = rest | ||
// TODO: Support custom providers | ||
const isCredentials = providerId === "credentials" | ||
const isEmail = providerId === "email" | ||
const isWebAuthn = providerId === "webauthn" | ||
const isSupportingReturn = isCredentials || isEmail || isWebAuthn | ||
const baseUrl = base ?? "" | ||
const basePath = base ?? "" | ||
const signInUrl = `${basePath}/auth/${ | ||
isCredentials || isWebAuthn ? "callback" : "signin" | ||
}/${providerId}` | ||
if (!provider || provider !== "webauthn") { | ||
// TODO: Add docs link with explanation | ||
throw new TypeError( | ||
[ | ||
`Provider id "${provider}" does not refer to a WebAuthn provider.`, | ||
'Please use `import { signIn } from "@auth/sveltekit/client"` instead.', | ||
].join("\n") | ||
) | ||
} | ||
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}` | ||
// Execute WebAuthn client flow if needed | ||
const webAuthnBody: Record<string, unknown> = {} | ||
if (isWebAuthn) { | ||
const { data, error, action } = await webAuthnOptions(providerId, options) | ||
if (error) { | ||
// logger.error(new Error(await error.text())) | ||
return | ||
} | ||
webAuthnBody.data = JSON.stringify(data) | ||
webAuthnBody.action = action | ||
const webAuthnResponse = await webAuthnOptions(provider, signInParams) | ||
if (webAuthnResponse.error) { | ||
logger.error(new Error(await webAuthnResponse.error.text())) | ||
return | ||
} | ||
webAuthnBody.data = JSON.stringify(webAuthnResponse.data) | ||
webAuthnBody.action = webAuthnResponse.action | ||
const res = await fetch(_signInUrl, { | ||
const signInUrl = `${baseUrl}/callback/${provider}?${new URLSearchParams(authorizationParams)}` | ||
const res = await fetch(signInUrl, { | ||
method: "post", | ||
@@ -95,21 +105,29 @@ headers: { | ||
}, | ||
// @ts-ignore | ||
body: new URLSearchParams({ | ||
...options, | ||
callbackUrl, | ||
...signInParams, | ||
...webAuthnBody, | ||
callbackUrl: redirectTo, | ||
}), | ||
}) | ||
const data = await res.clone().json() | ||
const data = await res.json() | ||
if (redirect || !isSupportingReturn) { | ||
// TODO: Do not redirect for Credentials and Email providers by default in next major | ||
window.location.href = data.url ?? callbackUrl | ||
if (redirect) { | ||
const url = data.url ?? callbackUrl | ||
window.location.href = url | ||
// If url contains a hash, the browser does not reload the page. We reload manually | ||
if (data.url.includes("#")) window.location.reload() | ||
if (url.includes("#")) window.location.reload() | ||
return | ||
} | ||
return res | ||
const error = new URL(data.url).searchParams.get("error") | ||
const code = new URL(data.url).searchParams.get("code") | ||
return { | ||
error, | ||
code, | ||
status: res.status, | ||
ok: res.ok, | ||
url: error ? null : data.url, | ||
} as any | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
139426
355
2343
+ Added@auth/core@0.38.0(transitive)
+ Addedjose@6.0.8(transitive)
- Removed@auth/core@0.37.4(transitive)
- Removedjose@5.10.0(transitive)
Updated@auth/core@0.38.0