Closed26
SvelteKit で @auth/sveltekit を使って Google ログインできるようにする
このスクラップについて
このスクラップでは SvelteKit で @auth/sveltekit を使って Google 認証をできるようにするまでの過程を記録する。
@auth/sveltekit どころか Auth.js を使うの自体初めてだ。
プロジェクト作成
コマンド
npm create svelte@latest sveltekit-auth
- Skeleton project
- TypeScript
- ESLint, Prettier
開発サーバー起動
コマンド
cd sveltekit-auth
npm install
npm run dev
npm パッケージのインストール
コマンド
npm install @auth/core @auth/sveltekit
環境変数ファイルの作成
コマンド
touch .env.local
.env.local
GOOGLE_CLIENT_ID="xxxx"
GOOGLE_CLIENT_SECRET="yyyy"
認証情報の取得
GCP の API とサービスのページから取得できる。
せっかくなのでこの機会に作っておこう。
フックの作成
src/hooks.server.ts
import { SvelteKitAuth } from "@auth/sveltekit";
import Google from "@auth/core/providers/google";
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from "$env/static/private";
export const handle = SvelteKitAuth({
providers: [Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
})]
})
ログイン/ログアウトページの作成
src/routes/+page.svelte
<script>
import { signIn, signOut } from "@auth/sveltekit/client";
import { page } from '$app/stores';
</script>
<h1>SvelteKit Auth Example</h1>
<p>
{#if $page.data.session}
{#if $page.data.session.user?.image}
<span style="background-image: url('{$page.data.session.user.image}')" class="avatar"></span>
{/if}
<span class="signedInText">
<small>Signed in as</small><br>
<strong>{$page.data.session.user?.name ?? "User"}</strong>
</span>
<button on:click={() => signOut()} class="button">Sign out</button>
{:else}
<span class="notSignedIntText">You are not signed in</span>
<button on:click={() => signIn("google")}>Sign in with Google</button>
{/if}
</p>
ログイン/ログアウトページのスクリーンショット
ログインしていないのでログインボタンが表示されている。
ボタンを押したが何も起こらない
コンソールに下記のメッセージが出力されている。
[auth][error] MissingSecret: Please define a
secret
. .Read more at https://errors.authjs.dev#missingsecret
Missing Secret のページ
おそらくシークレットを指定する必要があるようだ。
シークレットの生成
コマンド
openssl rand -hex 32
.env.local(追記)
AUTH_SECRET="0000000000000000000000000000000000000000000000000000000000000000"
シークレットの設定
src/hooks.server.ts
import { SvelteKitAuth } from '@auth/sveltekit';
import Google from '@auth/core/providers/google';
import { AUTH_SECRET, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private';
export const handle = SvelteKitAuth({
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
})
],
secret: AUTH_SECRET,
});
今度は表示された
Google ログインページ
今度はログインしたことにならない
なかなかスムーズにいかないものだ。
レイアウトを忘れていた
src/routes/+layout.server.ts
import type { LayoutServerLoad } from "./$types";
export const load: LayoutServerLoad = async (event) => {
return {
session: await event.locals.getSession()
}
}
これで無事に表示されるようになった。
ログアウト
ログインセッションが終了した。
これで終わりだが
せっかくなので認証されたユーザー向けのページを作ってみよう。
コマンド
mkdir src/routes/authenticated
touch src/routes/authenticated/+page.svelte
src/routes/authenticated/+page.svelte
<h1>This page is for authenticated users only</h1>
今は普通にアクセスできる
何も設定していないので当然の結果。
認証を必須にする
src/hooks.server.ts
import { SvelteKitAuth } from '@auth/sveltekit';
import Google from '@auth/core/providers/google';
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private';
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
const authorization: Handle = async ({ event, resolve }) => {
if (event.url.pathname.startsWith('/authenticated')) {
const session = await event.locals.getSession();
if (!session) {
throw redirect(303, '/');
}
}
return resolve(event);
}
export const handle = sequence(
SvelteKitAuth({
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
})
],
}),
authorization
);
これでログインしていない状態で /authenticated にアクセスするとリダイレクトされるようになる。
ユーザー識別子
頑張ればサブジェクトも取得できるがそうでなければメールアドレスを使えば十分そうだ。
src/hooks.server.ts
import { SvelteKitAuth } from '@auth/sveltekit';
import Google from '@auth/core/providers/google';
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private';
import { redirect, type Handle } from '@sveltejs/kit';
import { sequence } from '@sveltejs/kit/hooks';
const authorization: Handle = async ({ event, resolve }) => {
if (event.url.pathname.startsWith('/authenticated')) {
const session = await event.locals.getSession();
if (!session) {
throw redirect(303, '/');
}
}
return resolve(event);
};
export const handle = sequence(
SvelteKitAuth({
providers: [
Google({
clientId: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET
})
],
callbacks: {
session: ({ session, token }) => {
console.log(token);
return session;
}
}
}),
authorization
);
リポジトリ
おわりに
比較的スムーズに進んで良かった。
機会があれば Auth0 も試してみたい。
今さらだがリポジトリ名を sveltekit-google-auth-example にすればよかった。
認証を必須にする別の方法
Route groups を使う方が良いかも知れない。
と思ったけど
API ルートやフォームアクションで parent() 関数を使えないことを考えるとやっぱり locals を使った方が良いかも知れない。
このスクラップは2023/11/22にクローズされました