🔐

OAuth、OIDCについてまとめてみた

2025/03/05に公開

OAuth

  • OAuthとは → ユーザー名やパスワードを渡さずに、あるサービスが別のサービスにアクセスを許可する仕組み。例えば、Googleアカウントの許可を得て、他のアプリがユーザーの情報にアクセスできるようにする仕組み
  • OAuthは認可の仕組み → 認証の仕組み(ログイン)はアプリ側で実装する必要がある
  • OAuthの登場人物
    • リソースオーナー: アクセスを許可するユーザー
    • クライアント: ユーザーのデータを取得したいアプリ
    • 認可サーバー: ユーザーの認可を管理し、アクセストークンを発行
    • リソースサーバー: ユーザーのデータを管理し、アクセストークンを使ってアクセスを制御
  • 説明では、以下の役割で整理する
    • ブラウザ(= リソースオーナー)
    • Webアプリ(= クライアント)
      • Webサーバー
      • データベース
      • セッションストア(例: Redis)
    • 認可・リソースサーバー(例: GitHub)

OAuth認可フロー

  • ユーザーが認可・リソースサーバーの認証画面に遷移し、認証後、アクセストークンが生成・発行されるまでの流れ
  • 事前に必要な設定
    • 認可・リソースサーバーの管理画面からclient_id, client_secretを取得し、Webアプリに設定する
    • 認可・リソースサーバーにWeb アプリのcallback_uri(リダイレクト先)を許可されたリダイレクトURIとして登録する
コード例
  • 認可・リソースサーバー
    • GET /oauth/authorize
    • POST /oauth/authorize
    • POST /oauth/access_token
import express from 'express';
import jwt from 'jsonwebtoken';

...

const app = express();

const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
const redirectUri = process.env.REDIRECT_URI;

app.get("/oauth/authorize", (req, res) => {
  const { client_id } = req.query;
  const client = await getClientInfo(client_id);

  if (!client) {
    return res.status(400).json({ error: 'Invalid client_id' });
  }

  // 認証画面
  res.send('<form action="/oauth/authorize" method="POST">
        <input name="email" type="email" placeholder="メールアドレス" required />
        <button type="submit">ログイン</button>
      </form>');
});

app.post("/oauth/authorize", (req, res) => {
  const { email, password } = req.body;

  // データベースからユーザー情報を取得
  const user = await db.getUserByEmail(email);
  if (!user) {
    res.status(401).json({ message: 'Unauthorized' });
  }

  // パスワード照合
  const isPasswordValid = await bcrypt.compare(password, user.password_hash);
  if (!isPasswordValid) {
    res.status(401).json({ message: 'Unauthorized' });
  }

  // 認可コードをUUIDとして発行し、DBに保存
  const authCode = uuidv4();
  await storeAuthCode(authCode, email);

  res.redirect(`${redirectUri}?code=${authCode}`);
});

app.post("/oauth/access_token", (req, res) => {
  const { code } = req.body;

  try {
    const email = await getEmailFromAuthCode(code);

    if (!email) {
      return res.status(400).json({ error: "無効または期限切れの認可コード" });
    }

    await deleteAuthCode(code);

    // アクセストークンを生成、発行
    const accessToken = jwt.sign({ email }, process.env.CLIENT_SECRET, { expiresIn: "1h" });

    res.json({ access_token: accessToken });
  } catch (error) {
    console.error("エラー:", error);
    res.status(500).json({ error: "サーバーエラー" });
  }
});
  • Webサーバー
    • GET /oauth/callback
import express from 'express';
import jwt from 'express-session';

app.get("/oauth/callback", async (req, res) => {
  const { code } = req.query;

  if (!code) {
    return res.status(400).json({ error: "認可コードが必要です" });
  }

  try {
    // 認可コードに紐づいたクライアント情報をDBから取得
    const clientInfo = await getClientInfoFromCode(code);

    if (!clientInfo) {
      return res.status(400).json({ error: "無効または期限切れの認可コード" });
    }

    const { client_id, client_secret, redirect_uri } = clientInfo;

    // 認可コードを使ってアクセストークンを取得
    const tokenResponse = await axios.post("https://auth.example.com/oauth/access_token", {
      code,
      client_id,
      client_secret,
      grant_type: "authorization_code",
      redirect_uri,
    });
  } catch (error) {
    console.error("OAuth エラー:", error);
    res.status(500).send("認証エラー");
  }
});

OAuthログインフロー

  • 発行されたアクセストークンを使ってユーザー情報を取得し、ブラウザにセッションIDが設定されるまで
OAuthを使わない場合

ユーザーがemailとpasswordを送信し、ブラウザにセッションIDが設定されるまでの流れ

OIDC(OpenID Connect)

  • OIDCとは → OAuth2.0に「認証機能」を追加した仕組み
  • OAuth2.0では、ユーザー情報を取得するために、アクセストークンを使って認可サーバーに問い合わせる必要があった
  • OIDCでは「IDトークン」が発行され、ユーザー情報が含まれる
  • Webアプリは IDトークンを確認するだけで、ユーザーを識別できる
    • 認可サーバーに毎回アクセスする必要がない
    • JWK(公開鍵)はキャッシュできるため、署名検証のたびに取得する必要がない
  • 毎回認可サーバーに問い合わせなくてもログインできる
  • OIDCの登場人物
    • エンドユーザー: アクセスを許可するユーザー
    • Relying Party: ユーザーのデータを取得したいアプリ
    • IdP: ユーザーの認証を管理し、IDトークンを発行、OAuthだと認可・リソースサーバー
  • 説明では、以下の役割で整理する
    • ブラウザ(= エンドユーザー)
    • Webアプリ(= Relying Party)
      • Webサーバー
      • データベース
      • セッションストア(例: Redis)
    • IdP(例: GitHub)

OIDCログインフロー

OAuthとOIDCのログインフローを比較

参考資料

https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be
https://qiita.com/TakahikoKawasaki/items/498ca08bbfcc341691fe
https://learn.microsoft.com/ja-jp/entra/identity-platform/v2-oauth2-auth-code-flow
https://auth0.com/jp/intro-to-iam/what-is-openid-connect-oidc

Discussion