SvelteKitとAuthJsを使ってOAuthログイン機能を作る
9/12/2023
はじめに
https://github.com/nextauthjs/next-auth SvelteKitでOAuth認証を使うとなると今のところ自力で実装するか AuthJsしか選択肢がないと思います。(sk-authはメンテされてなくAuthJsとほぼ同じ) 一応 SupabaseとSvault https://github.com/oslabs-beta/Svault がありますが、プロバイダが少なかったりします。 :::message 追記 luciaがあるらしいです! https://lucia-auth.com プロバイダも多いのでこちらもありかも! :::
Google,GitHub,DiscordだけだったらSvaultのほうがいいのかもしれない
使いましょうAuthJs。
https://authjs.dev/
機能的にはNextAuthベースなのでプロバイダとかを簡単に設定できます。
基本的には documentをなぞればいいだけですがたまに next-authのままだったり、わかりにくかったりするので一応簡単に説明します。
準備
インスコ
npm install @auth/core @auth/sveltekit
:::message User情報をデータベースに保存する前提で説明します。 JWT tokenを利用する場合は必要ありません。 :::
データベース用Adapter
AuthJsがもともと用意しているAdapter
npm i @auth/<任意>-adapter
https://authjs.dev/reference/adapters
Adapterを自作 https://authjs.dev/guides/adapters/creating-a-database-adapter
データベース構造
各自のデータベースにあわせて設定して下さい
設定
プロバイダはそれごとに違うので省略しますが、だいたい.env
にclientId
とclientSecret
置いとけばどうにかなります
詳しくは
https://authjs.dev/reference/core/providers_oauth
:::details src/hooks.server.ts
import {
AUTH_SECRET,
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
GITHUB_CLIENT_ID,
GITHUB_CLIENT_SECRET
} from '$env/static/private';
import Google from '@auth/core/providers/google';
const auth = SvelteKitAuth(async ({ locals }) => {
return {
adapter: Adapter(db), //JWTの場合不要
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
}),
GitHub({
clientId: GITHUB_CLIENT_ID,
clientSecret: GITHUB_CLIENT_SECRET
})
],
secret: AUTH_SECRET,
trustHost: true,
callbacks: {
session: async ({ session, user }) => {
//idとかをSessionに含める場合
if (session.user) {
session.user.id = user.id;
//session.user.role="admin"
}
return session;
}
},
pages: {
signIn: '/signIn', //`signIn()`(プロバイダ指定なし)の時に飛ぶ
newUser: '/setup', //初ログインの時にリダイレクトする。,
error: '/auth/error' //認証中のエラー発生時にリダイレクト
}
};
}) satisfies Handle;
export const handle = sequence(auth);
:::
:::details src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types';
export const load = (async ({ locals }) => {
return {
session: await locals.getSession()
};
}) satisfies LayoutServerLoad;
:::
https://authjs.dev/getting-started/typescript :::details Sessionの型を追加
import { DefaultSession } from '@auth/core/types';
declare module '@auth/core/types' {
interface Session {
user: {
//id等を追加する場合
id: string;
} & DefaultSession.user;
}
}
:::
ログインページの例
<script lang="ts">
import { signIn, signOut } from '@auth/sveltekit/client';
import { page } from '$app/stores';
</script>
{#if $page.data.session?.user}
<h1>Welcome {$page.data.session.user.name}</h1>
<button on:click="{signOut}">SignOut</button>
{:else}
<h1>Select provider to sign in</h1>
<button on:click="{()" ="">signIn("google")}>Google</button>
<button on:click="{()" ="">signIn("github")}>GitHub</button>
{/if}
ページ保護
:::details ページごとに保護する https://authjs.dev/reference/sveltekit#handling-authorization
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async (event) => {
const session = await event.locals.getSession();
if (!session?.user) throw redirect(303, '/auth');
return {};
};
:::
:::details パスごとに https://authjs.dev/reference/sveltekit#handling-authorization
const protect = (async ({ event, resolve }) => {
const protectedPages = ['/protected', '/admin'];
if (protectedPages.some((s) => event.url.pathname.startsWith(s))) {
const session = await event.locals.getSession();
if (!session) {
throw redirect(303, '/signIn');
}
}
return resolve(event);
}) satisfies Handle;
export const handle = sequence(auth, protect);
:::
:::message +layout.server.tsで保護をしないでくださいとのこと :::
おわり
さすがNextAuth簡単ですね。 Svelte大好き人間なのでライブラリが増えれば増えるほど俺が喜びます。任せた。 ほな