サイトロゴ
SvelteKit Created: 2026/06/03 Updated: 2026/06/09

SvelteKitでOAuthログインを実装する:GitHub連携と本番運用チェックリスト

SvelteKitでGitHub OAuthログインを手書き実装し、Google OAuthとの差分、既存アカウント連携、本番運用前の確認項目を整理します。

シリーズ:SvelteKit 認証完全ガイド

01

bcrypt・Prisma・サインアップ

02

セッションDB永続化・有効期限

03

CSRF 対策・Cookie セキュリティ

04

OAuth・GitHub・本番チェックリスト

第3回までで bcrypt・DB セッション・CSRF 対策・セキュリティヘッダーが揃い、パスワード認証が本番レベルに達しました。最終回の今回は OAuth によるソーシャルログイン を追加し、シリーズ全体を 本番運用チェックリスト で締めくくります。 OAuth の実装はライブラリを使わず fetch で認可コードフローを手書きします。仕組みを理解することで、どのプロバイダーにも対応できる応用力が身につきます。

💡 前提条件

第1〜3回で実装した Prisma・セッション管理・hooks.server.ts が存在する状態で進めます。GitHub アカウントが必要です(OAuth アプリの登録に使います)。

OAuth の仕組み ― 認可コードフロー

OAuth は「外部サービス(GitHub など)のアカウント情報を使ってログインする」仕組みです。パスワードをこちらのサーバーで扱わないため、パスワード管理の責任が外部サービスに移ります。

ブラウザ

GitHub

自サーバー

① ログインボタンをクリック

② GitHub の認可ページへリダイレクト

③ ユーザーがアクセスを許可

④ code 付きでコールバックURL へリダイレクト

⑤ code をサーバーに渡す

⑥ code → access token 交換

⑦ token でユーザー情報を取得 → セッション作成

処理の主役はサーバーです。 ② のリダイレクト先URL の生成 と、 ⑥ の認可コード(code)→ access token の交換 ⑦ のユーザー情報取得 を SvelteKit のサーバーで実装します。


GitHub OAuth アプリを登録する

まず GitHub に OAuth アプリを登録して、Client ID と Client Secret を取得します。

1

GitHub → Settings → Developer settings → OAuth Apps → New OAuth App

GitHub にログインし、右上のアイコン → Settings → 左サイドバー下部の Developer settings → OAuth Apps の順に進みます。

2

アプリ情報を入力する

Application name に任意の名前、Homepage URL にhttp://localhost:5173 Authorization callback URL にhttp://localhost:5173/auth/github/callback を入力して「Register application」をクリックします。

3

Client ID と Client Secret を取得する

登録後の画面に Client ID が表示されます。「Generate a new client secret」をクリックして Client Secret を生成し、 必ず控えてください (このページを離れると二度と表示されません)。

4

.env に追記する

取得した値を.env ファイルに追記します。

bash
DATABASE_URL="file:./dev.db"

# GitHub OAuth
GITHUB_CLIENT_ID="your_client_id_here"
GITHUB_CLIENT_SECRET="your_client_secret_here"

# コールバック URL(本番では本番ドメインに変更する)
GITHUB_REDIRECT_URI="http://localhost:5173/auth/github/callback"

# state 検証用のランダムシークレット
OAUTH_STATE_SECRET="any_random_long_string_here"

スキーマに OAuthAccount モデルを追加する

1ユーザーが複数のソーシャルログイン(GitHub・Google など)を持てるよう、OAuthAccount モデルを追加します。

Prisma Schemaprisma/schema.prisma(OAuthAccount を追加)

prisma
model User {
  id             String         @id @default(cuid())
  email          String         @unique
  name           String
  // OAuth ユーザーはパスワードを持たないので nullable に
    hashedPassword String?
  createdAt      DateTime       @default(now())
  updatedAt      DateTime       @updatedAt
  sessions       Session[]
    oauthAccounts  OAuthAccount[]
}

model Session {
  id        String   @id @default(cuid())
  userId    String
  user      User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  expiresAt DateTime
  createdAt DateTime @default(now())
}

model OAuthAccount {
  id             String   @id @default(cuid())
  userId         String
  user           User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  provider       String            // 'github' | 'google' など
  providerUserId String            // プロバイダー側のユーザーID
  createdAt      DateTime @default(now())
  @@unique([provider, providerUserId])  // 同じプロバイダーの同じIDは1つまで
}
bash
$ npx prisma migrate dev --name add-oauth-account
✔ Generated Prisma Client (v6.x.x)

GitHub OAuth の実装

Step 1 ― ログインボタン(リダイレクト先URLの生成)

「GitHub でログイン」ボタンをクリックすると、/auth/github に遷移し、GitHub の認可ページへリダイレクトします。 state パラメータ は CSRF 対策として必須です。

ts
import { redirect } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { randomBytes } from 'node:crypto';

export const GET: RequestHandler = async (event) => {
  // CSRF 対策用の state をランダム生成してCookieに保存
    const state = randomBytes(16).toString('hex');
  event.cookies.set('oauth_state', state, {
    path: '/', httpOnly: true, sameSite: 'lax',
    maxAge: 60 * 10, // 10分で失効
  });

  // GitHub の認可ページへリダイレクトする URL を組み立てる
  const params = new URLSearchParams({
    client_id:    process.env.GITHUB_CLIENT_ID!,
    redirect_uri: process.env.GITHUB_REDIRECT_URI!,
    scope:        'read:user user:email', // 取得するスコープ
        state,
  });

    redirect(302, `https://github.com/login/oauth/authorize?${params}`);
};

Step 2 ― コールバック処理(code → token → ユーザー情報)

GitHub がリダイレクトしてくるコールバックURLで、認可コードを受け取り access token に交換し、ユーザー情報を取得します。

ts
import { redirect, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { db } from '$lib/server/db';
import { createSession } from '$lib/server/session';
import { SESSION_COOKIE_OPTIONS } from '$lib/server/cookie';

export const GET: RequestHandler = async (event) => {
  const code  = event.url.searchParams.get('code');
  const state = event.url.searchParams.get('state');
  const savedState = event.cookies.get('oauth_state');

  // ── state 検証(CSRF 対策) ──
    if (!state || state !== savedState) error(400, 'Invalid state');
  event.cookies.delete('oauth_state', { path: '/' });
  if (!code) error(400, 'Missing code');

  // ── code → access token に交換 ──
    const tokenRes = await fetch('https://github.com/login/oauth/access_token', {
    method: 'POST',
    headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      client_id:     process.env.GITHUB_CLIENT_ID,
      client_secret: process.env.GITHUB_CLIENT_SECRET,
      code,
    }),
    });
  const { access_token } = await tokenRes.json();
  if (!access_token) error(400, 'Failed to get access token');

  // ── GitHub API でユーザー情報を取得 ──
    const ghUser = await fetch('https://api.github.com/user', {
    headers: { 'Authorization': `Bearer ${access_token}`, 'User-Agent': 'svelte-shop' },
    }).then((r) => r.json());

  // メールは別エンドポイントで取得(非公開設定のユーザーに対応)
  const emails: { email: string; primary: boolean }[] = await fetch(
    'https://api.github.com/user/emails',
    { headers: { 'Authorization': `Bearer ${access_token}`, 'User-Agent': 'svelte-shop' } }
  ).then((r) => r.json());
  const primaryEmail = emails.find((e) => e.primary)?.email ?? ghUser.email;
  if (!primaryEmail) error(400, 'Email not found');

  // ── DB でユーザーを検索 or 作成 ──
    let user = await findOrCreateOAuthUser({
    provider:       'github',
    providerUserId: String(ghUser.id),
    email:          primaryEmail,
    name:           ghUser.name || ghUser.login,
    });

  // ── セッション作成・Cookie 発行 ──
  const sessionId = await createSession({
    id: user.id, name: user.name, email: user.email
  });
  event.cookies.set('session_id', sessionId, SESSION_COOKIE_OPTIONS);
  redirect(303, '/mypage');
};

// OAuthAccount を使ったユーザー検索 or 作成(既存アカウント連携を含む)
async function findOrCreateOAuthUser(params: {
  provider: string; providerUserId: string;
  email: string;     name: string;
}) {
  // ① OAuthAccount から既存ユーザーを検索
    const existing = await db.oAuthAccount.findUnique({
    where: { provider_providerUserId: { provider: params.provider, providerUserId: params.providerUserId } },
    include: { user: true },
    });
  if (existing) return existing.user;

  // ② 同じメールアドレスの既存ユーザーがいれば紐づける(アカウント連携)
    const userByEmail = await db.user.findUnique({ where: { email: params.email } });
  if (userByEmail) {
    await db.oAuthAccount.create({
      data: { userId: userByEmail.id, provider: params.provider, providerUserId: params.providerUserId },
    });
    return userByEmail;
  }

  // ③ まったく新しいユーザーを作成
    const newUser = await db.user.create({
    data: {
      email: params.email,
      name:  params.name,
      // OAuth ユーザーはパスワードなし(nullable にしたので省略可)
      oauthAccounts: {
        create: { provider: params.provider, providerUserId: params.providerUserId },
      },
    },
    });
  return newUser;
}

✨ findOrCreateOAuthUser の 3パターン

① 同じ GitHub アカウントで再ログイン →OAuthAccount に一致レコードがあるのでそのユーザーを返す。② 別の方法(メールアドレス)でアカウントを持っているユーザーが GitHub ログインを試みる → 同じメールで紐づけてアカウント連携。③ 初めて GitHub でログインするユーザー → 新規ユーザーとして登録。

Step 3 ― ログインページにボタンを追加

svelte
<!-- 既存のメール・パスワードフォームの下に追加 -->
<div class="divider">または</div>

<!-- href で GET リクエストを送るだけ。フォームは不要 -->
<a href="/auth/github" class="btn-github">
  GitHub でログイン
</a>

🔍 動作確認

http://localhost:5173/login を開く。② 「GitHub でログイン」をクリック。③ GitHub の認可ページでアクセスを許可。④ コールバックが処理されてマイページへリダイレクトされることを確認します。初回は新規ユーザーが作成され、npx prisma studio で User・OAuthAccount テーブルを確認できます。


Google OAuth との差分(補足)

GitHub OAuth を理解していれば Google OAuth も同じ構造で実装できます。主な差分は以下の通りです。

項目 GitHub Google
認可エンドポイント github.com/login/oauth/authorize accounts.google.com/o/oauth2/v2/auth
token エンドポイント github.com/login/oauth/access_token oauth2.googleapis.com/token
ユーザー情報 API api.github.com/user www.googleapis.com/oauth2/v3/userinfo
scope(メール取得) read:user user:email openid email profile
token_type bearer(小文字) Bearer(大文字)
メール取得方法 別エンドポイント(/user/emails) userinfo レスポンスに含まれる
追加パラメータ なし access_type=offline(任意)

💡 Google の実装手順

Google Cloud Console でプロジェクトを作成 → OAuth 同意画面を設定 → 認証情報 → OAuth 2.0 クライアント ID を作成 →GOOGLE_CLIENT_ID GOOGLE_CLIENT_SECRET を取得します。実装は GitHub と同じ構造で、上記の差分箇所を置き換えるだけです。コールバックは/auth/google/callback に向けてください。


本番運用チェックリスト

これまでのシリーズを通じて実装してきた内容の最終確認です。デプロイ前に全項目を確認してください。

🔐 認証・セキュリティ

パスワードは bcrypt でハッシュ化している

平文パスワードを DB に保存していないこと。SALT_ROUNDS は 10 以上(推奨 12)。

セッションは DB に永続化されている

サーバー再起動でセッションが消えないこと。有効期限と自動延長を実装済みであること。

Cookie に httpOnly・sameSite: lax を設定している

SESSION_COOKIE_OPTIONS を全箇所で統一使用していること。

!

本番環境で Cookie の secure: true を確認する

NODE_ENV=production が設定されていれば自動でtrue になる。HTTPS 環境であることを確認。

セキュリティヘッダーを hooks.server.ts で一括設定している

X-Frame-Options・X-Content-Type-Options・Referrer-Policy・CSP が設定済みであること。

OAuth の state パラメータを検証している

コールバックで Cookie の state と URL の state が一致するかを確認していること。

🗄 データベース

!

本番用 DB に切り替えている(SQLite → PostgreSQL など)

SQLite はシングルサーバー向け。複数サーバー構成や高負荷では PostgreSQL / MySQL を使う。schema.prisma provider DATABASE_URL を変更してマイグレーションを実行。

期限切れセッションのクリーンアップ cron を設定している

コールバックで Cookie の state と URL の state が一致するかを確認していること。

!

DB のバックアップを設定している

/api/cleanup-sessions を定期実行する仕組みがあること。

🌍 環境変数・デプロイ

!

.env を .gitignore に追加している

シークレットキーがリポジトリにコミットされていないこと。

!

本番環境の環境変数をホスティングサービスに設定している

DATABASE_URL GITHUB_CLIENT_ID GITHUB_CLIENT_SECRET GITHUB_REDIRECT_URI (本番ドメイン)などを設定。

!

GitHub OAuth アプリのコールバック URL を本番ドメインに更新している

GitHub の OAuth アプリ設定でAuthorization callback URL を本番の URL に変更すること。

!

NODE_ENV=production でビルドを確認している

npm run build && npm run preview で本番相当の動作を確認してからデプロイ。

📋 コード品質

エラーメッセージでメールアドレスの存在を漏洩していない

「メールアドレスまたはパスワードが違います」に統一していること。

handleError でスタックトレースをブラウザに返していない

ユーザーには簡潔なメッセージのみを返し、詳細はサーバーログに記録していること。

src/lib/server/ のファイルをブラウザ側から import していない

DB・セッション・パスワード関連は.server.ts またはsrc/lib/server/ からのみ使用。


トラブルシュート

❓ GitHub OAuth のコールバックで「Invalid state」エラーになる

oauth_state Cookie が届いていない可能性があります。sameSite: 'lax' では GitHub からのリダイレクト(GET)で Cookie は付与されるはずですが、secure: true が開発環境で設定されていると HTTP では Cookie が送られません。開発環境ではsecure: false になっているか確認してください。

❓ GitHub でのログイン後にメールアドレスが取得できない

GitHub の設定でメールアドレスが非公開になっているユーザーは/user API のレスポンスにemail が含まれません。/user/emails エンドポイントを使ってプライマリメールを取得する実装になっているか確認してください(今回の実装では対応済みです)。

❓ 本番デプロイ後に OAuth が動かない

GitHub OAuth アプリの「Authorization callback URL」が本番ドメインに更新されているか確認してください。またGITHUB_REDIRECT_URI 環境変数も本番の URL に変更する必要があります。localhost のままだとエラーになります。


第4回の完成コード一覧

text
prisma/
 └─ schema.prisma UPDATE ← OAuthAccount モデル追加・hashedPassword nullable 

src/routes/auth/
           ├─ github/
           ├─ +server.ts NEW ← state 生成・GitHub 認可ページへリダイレクト
           └─ callback/
                └─ +server.ts NEW ← code→token 交換・ユーザー作成・セッション発行

 src/routes/login/
             └─ +page.svelte UPDATE ← GitHub ログインボタン追加

第4回・シリーズ全体のまとめ

今回学んだこと

  • OAuth の認可コードフローは「リダイレクト → 認可 → code → token → ユーザー情報取得」の5ステップ。ライブラリなしで fetch だけで実装できる
  • state パラメータは OAuth の CSRF 対策として必須。Cookie に保存してコールバックで照合する
  • OAuthAccount モデルで同じメールアドレスを持つ既存アカウントと連携できる。1ユーザーが複数プロバイダーを持てる構造が重要
  • Google OAuth は GitHub と同じ構造で、エンドポイント URL・scope・メール取得方法の差分を置き換えるだけで実装できる
  • 本番デプロイ前には Cookie のsecure: true ・環境変数・OAuth アプリのコールバック URL 更新・DB の本番移行を必ず確認する

🎉

シリーズ完走、おめでとうございます!

前々シリーズ 前シリーズ ・本シリーズ を通じて、SvelteKit の基礎から本番レベルの認証まで体系的に学びました。
bcrypt・Prisma・セッション管理・CSRF 対策・OAuth が揃った商品カタログアプリは、そのまま本番デプロイできる状態です。

✅ bcrypt パスワードハッシュ化

✅ Prisma + SQLite DB

✅ サインアップ・ログイン

✅ セッションDB永続化・有効期限

✅ CSRF 対策・セキュリティヘッダー

✅ GitHub OAuth・アカウント連携

第1回(前々回)

サーバー・クライアント動作入門

  • ファイルベースルーティング
  • load 関数・API Route
  • SSR と .server.ts の境界

第2回(前回)

Hooks 完全ガイド

  • handle・locals・reroute
  • event.cookies・Form Actions
  • handleError・handleFetch

第3回(本シリーズ)

認証完全ガイド

  • bcrypt・Prisma・サインアップ
  • セッション永続化・CSRF 対策
  • GitHub OAuth・本番チェック