Compare commits

...

3 Commits

Author SHA1 Message Date
Thang Vu
42632c8894 Merge branch 'main' into feat/sveltekit-no-js-signin 2023-04-15 20:28:11 +07:00
Thang Vu
c286b6fea7 seems to work now 2023-03-04 22:25:17 +07:00
Thang Vu
d0fbea8027 feat: sveltekit login no js 2023-03-04 16:19:05 +07:00
9 changed files with 103 additions and 28 deletions

View File

@@ -1,12 +1,11 @@
<script lang="ts">
export let provider: any
export let callbackUrl: string
</script>
<form action={provider.signinUrl} method="POST">
{#if provider.callbackUrl}
<input type="hidden" name="callbackUrl" value={provider.callbackUrl} />
{/if}
<button type="submit" class="button">
<input type="hidden" name="callbackUrl" value={callbackUrl} />
<button type="submit">
<slot>Sign in with {provider.name}</slot>
</button>
</form>

View File

@@ -3,5 +3,7 @@ import type { LayoutServerLoad } from "./$types"
export const load: LayoutServerLoad = async (event) => {
return {
session: await event.locals.getSession(),
providers: await event.locals.getProviders(),
callbackUrl: new URL(event.request.url).host,
}
}

View File

@@ -31,6 +31,7 @@
<ul class="navItems">
<li class="navItem"><a href="/">Home</a></li>
<li class="navItem"><a href="/protected">Protected</a></li>
<li class="navItem"><a href="/js-disabled">No JS</a></li>
</ul>
</nav>
</header>

View File

@@ -0,0 +1,29 @@
<script lang="ts">
import { page } from "$app/stores"
import SignInButton from "$lib/SignInButton.svelte"
</script>
<h1>SvelteKit Auth Example</h1>
<p>Disable JS in this page and SvelteKit Auth would still work.</p>
{#if $page.data.session}
{#if $page.data.session.user?.image}
<span
style="background-image: url('{$page.data.session.user.image}')"
class="avatar"
/>
{/if}
<span class="signedInText">
<small>Signed in as</small><br />
<strong
>{$page.data.session.user?.email ?? $page.data.session.user?.name}</strong
>
</span>
<form action="/auth/signout" method="POST">
<button type="submit">Sign out</button>
</form>
{:else}
<span class="notSignedInText">You are not signed in</span>
{#each Object.values($page.data.providers) as provider}
<SignInButton {provider} callbackUrl={$page.data.callbackUrl} />
{/each}
{/if}

View File

@@ -337,4 +337,12 @@ export interface AuthConfig {
/** @todo */
trustHost?: boolean
skipCSRFCheck?: typeof skipCSRFCheck
/**
* Defines the base path for the auth routes.
* If you change the default value,
* you must also update the callback URL used by the [providers](https://authjs.dev/reference/core/modules/providers).
*
* @default "/api/auth"
*/
prefix?: string
}

View File

@@ -48,7 +48,8 @@ export async function init({
// TODO: move this to web.ts
const parsed = parseUrl(
reqUrl.origin +
reqUrl.pathname.replace(`/${action}`, "").replace(`/${providerId}`, "")
reqUrl.pathname.replace(`/${action}`, "").replace(`/${providerId}`, ""),
authOptions.prefix
)
const url = new URL(parsed.toString())

View File

@@ -12,7 +12,10 @@ interface InternalUrl {
}
/** Returns an `URL` like object to make requests/redirects from server-side */
export default function parseUrl(url?: string | URL): InternalUrl {
export default function parseUrl(
url?: string | URL,
prefix?: string
): InternalUrl {
const defaultUrl = new URL("http://localhost:3000/api/auth")
if (url && !url.toString().startsWith("http")) {
@@ -20,7 +23,9 @@ export default function parseUrl(url?: string | URL): InternalUrl {
}
const _url = new URL(url ?? defaultUrl)
const path = (_url.pathname === "/" ? defaultUrl.pathname : _url.pathname)
const path = (
_url.pathname === "/" ? prefix ?? defaultUrl.pathname : _url.pathname
)
// Remove trailing slash
.replace(/\/$/, "")

View File

@@ -41,11 +41,6 @@ export async function signIn<
const _signInUrl = `${signInUrl}?${new URLSearchParams(authorizationParams)}`
// TODO: Handle custom base path
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const csrfTokenResponse = await fetch("/auth/csrf")
const { csrfToken } = await csrfTokenResponse.json()
const res = await fetch(_signInUrl, {
method: "post",
headers: {
@@ -55,7 +50,6 @@ export async function signIn<
// @ts-expect-error -- ignore
body: new URLSearchParams({
...options,
csrfToken,
callbackUrl,
}),
})
@@ -84,8 +78,6 @@ export async function signOut(options?: SignOutParams) {
const { callbackUrl = window.location.href } = options ?? {}
// TODO: Custom base path
// TODO: Remove this since Sveltekit offers the CSRF protection via origin check
const csrfTokenResponse = await fetch("/auth/csrf")
const { csrfToken } = await csrfTokenResponse.json()
const res = await fetch(`/auth/signout`, {
method: "post",
headers: {
@@ -93,7 +85,6 @@ export async function signOut(options?: SignOutParams) {
"X-Auth-Return-Redirect": "1",
},
body: new URLSearchParams({
csrfToken,
callbackUrl,
}),
})

View File

@@ -26,9 +26,9 @@
* providers: [GitHub({ clientId: GITHUB_ID, clientSecret: GITHUB_SECRET })],
* })
* ```
*
*
* or to use sveltekit platform environment variables for platforms like Cloudflare
*
*
* ```ts title="src/hooks.server.ts"
* import { SvelteKitAuth } from "@auth/sveltekit"
* import GitHub from "@auth/core/providers/github"
@@ -206,17 +206,37 @@ import type { Handle, RequestEvent } from "@sveltejs/kit"
import { dev } from "$app/environment"
import { env } from "$env/dynamic/private"
import { Auth } from "@auth/core"
import { Auth, skipCSRFCheck } from "@auth/core"
import type { AuthAction, AuthConfig, Session } from "@auth/core/types"
import type { Provider } from "@auth/core/providers"
export async function getSession(
req: Request,
config: AuthConfig
config: SvelteKitAuthConfig
): ReturnType<App.Locals["getSession"]> {
config.secret ??= env.AUTH_SECRET
config.trustHost ??= true
const url = new URL("/api/auth/session", req.url)
const url = new URL(`${config.prefix}/session`, req.url)
const request = new Request(url, { headers: req.headers })
const response = await Auth(request, config)
const { status = 200 } = response
const data = await response.json()
if (!data || !Object.keys(data).length) return null
if (status === 200) return data
throw new Error(data.message)
}
export async function getProviders(
req: Request,
config: SvelteKitAuthConfig
): ReturnType<App.Locals["getProviders"]> {
config.secret ??= env.AUTH_SECRET
config.trustHost ??= true
const url = new URL(`${config.prefix}/providers`, req.url)
const request = new Request(url, { headers: req.headers })
const response = await Auth(request, config)
@@ -240,6 +260,9 @@ export interface SvelteKitAuthConfig extends AuthConfig {
prefix?: string
}
/** Configure the {@link SvelteKitAuth} method. */
export interface SvelteKitAuthConfig extends AuthConfig {}
const actions: AuthAction[] = [
"providers",
"session",
@@ -251,24 +274,36 @@ const actions: AuthAction[] = [
"error",
]
type DynamicSvelteKitAuthConfig = (event: RequestEvent) => PromiseLike<SvelteKitAuthConfig>
type DynamicSvelteKitAuthConfig = (
event: RequestEvent
) => PromiseLike<SvelteKitAuthConfig>
function AuthHandle(svelteKitAuthOptions: SvelteKitAuthConfig | DynamicSvelteKitAuthConfig): Handle {
function AuthHandle(
svelteKitAuthOptions: SvelteKitAuthConfig | DynamicSvelteKitAuthConfig
): Handle {
return async function ({ event, resolve }) {
const authOptions =
const _authOptions =
typeof svelteKitAuthOptions === "object"
? svelteKitAuthOptions
: await svelteKitAuthOptions(event)
const { prefix = "/auth" } = authOptions
const { url, request } = event
const authOptions = {
..._authOptions,
skipCSRFCheck,
prefix: _authOptions.prefix ?? "/auth",
} satisfies AuthConfig
event.locals.getSession ??= () => getSession(request, authOptions)
event.locals.getProviders ??= () => getProviders(request, authOptions)
const action = url.pathname
.slice(prefix.length + 1)
.slice(authOptions.prefix.length + 1)
.split("/")[0] as AuthAction
if (!actions.includes(action) || !url.pathname.startsWith(prefix + "/")) {
if (
!actions.includes(action) ||
!url.pathname.startsWith(authOptions.prefix + "/")
) {
return resolve(event)
}
@@ -280,7 +315,9 @@ function AuthHandle(svelteKitAuthOptions: SvelteKitAuthConfig | DynamicSvelteKit
* The main entry point to `@auth/sveltekit`
* @see https://sveltekit.authjs.dev
*/
export function SvelteKitAuth(options: SvelteKitAuthConfig | DynamicSvelteKitAuthConfig): Handle {
export function SvelteKitAuth(
options: SvelteKitAuthConfig | DynamicSvelteKitAuthConfig
): Handle {
if (typeof options === "object") {
options.secret ??= env.AUTH_SECRET
options.trustHost ??= !!(env.AUTH_TRUST_HOST ?? env.VERCEL ?? dev)
@@ -293,9 +330,11 @@ declare global {
namespace App {
interface Locals {
getSession(): Promise<Session | null>
getProviders(): Promise<Provider[] | null>
}
interface PageData {
session: Session | null
providers: Provider[] | null
}
}
}