📖

Entra ID×OIDCによるWebアプリ認証:ハンズオン

に公開

1. OIDC認証とは?

OIDC(OpenID Connect)は、OAuth 2.0ベースの「SSO(シングルサインオン)標準」です。
JSON形式のIDトークン(JWT)で「この人はEntra IDで認証済み!」をWebアプリ間でやりとりできます。

1.1 認証の仕組み

認証と認可とは

  • 認証(Authentication): ユーザーが誰であるかの確認をする(例: ユーザー名とパスワードで本人確認)。
  • 認可(Authorization): 認証されたユーザーがどのような権限を持つかを確認する(例: 管理者の権限であるとか一般ユーザの権限であるとか)。

1.2 OIDC認証の基本フロー(図解)

OIDC認証フロー図
┌────────────┐     ┌────────────┐     ┌──────────────┐
│   ユーザー │     │   Webアプリ │     │   IdP(Entra ID)│
└────────────┘     └────────────┘     └──────────────┘
(1) アクセス&ログイン要求
        │────────────>│
(2) 認証要求リダイレクト
        │────────────>│
                               │────────────>│
(3) 認証画面表示・ユーザー認証
                               │<────────────│
        │<────────────│
(4) 認証コード受領(リダイレクト)
        │<────────────│
(5) 認証コードを使いID/アクセストークン要求(バックエンド通信)
        │────────────>│
                               │────────────>│
(6) ID/アクセストークン返却(JSONレスポンス)
        │<────────────│
(7) Webアプリでトークン検証・セッション確立&サービス提供

この“IDトークン(JWT)”がOIDCのポイント。アプリ側はこれで「正しく本人」と判定されます

2. OIDC認証のハンズオン

2.1 Azure側のアプリ登録

  1. Azureポータルにサインイン
    「Entra ID」>「アプリの登録」>「新規登録」

  2. アプリの登録内容
    「アプリ名」
     →OIDC Flask App
    「リダイレクトURI」には
     → http://localhost:5000/oidc/callback を入力

  3. 各種IDの確認
    登録完了後、「アプリケーション(クライアント)ID」と「ディレクトリ(テナント)ID」を控える

  4. 証明書の作成
    「証明書とシークレット」>「新しいクライアントシークレット」を作成

  5. 証明書のシークレット値の確認
    作成したシークレットの値を控える

2.2 アプリケーション(Flask OIDCクライアント)作成

■ 構成

OIDC/
├── app.py                # Flaskアプリ本体
├── .env                  # 設定(クライアントID等)

■ ライブラリ

pip install flask requests python-dotenv authlib

■ .envの中身

OIDC_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
OIDC_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OIDC_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
REDIRECT_URI=http://localhost:5000/oidc/callback

■ app.py(超シンプル実装例)

import os
from flask import Flask, redirect, request, session, url_for
from authlib.integrations.flask_client import OAuth
from dotenv import load_dotenv

load_dotenv()

app = Flask(__name__)
app.secret_key = os.urandom(24)

oauth = OAuth(app)
oauth.register(
    name='azure',
    client_id=os.environ['OIDC_CLIENT_ID'],
    client_secret=os.environ['OIDC_CLIENT_SECRET'],
    server_metadata_url=f'https://login.microsoftonline.com/{os.environ["OIDC_TENANT_ID"]}/v2.0/.well-known/openid-configuration',
    client_kwargs={
        'scope': 'openid profile email'
    }
)

@app.route('/')
def index():
    user = session.get('user')
    if user:
        return f'ようこそ、{user["name"]} さん!<br><a href="/logout">ログアウト</a>'
    return '<a href="/login">OIDCでログイン</a>'

@app.route('/login')
def login():
    redirect_uri = url_for('auth_callback', _external=True)
    return oauth.azure.authorize_redirect(redirect_uri)

@app.route('/oidc/callback')
def auth_callback():
    token = oauth.azure.authorize_access_token()
    userinfo = oauth.azure.parse_id_token(token)
    session['user'] = userinfo
    return redirect('/')

@app.route('/logout')
def logout():
    session.clear()
    return redirect('/')

if __name__ == '__main__':
    app.run(debug=True)

2.3 動作確認

  1. アプリケーション起動
python app.py

  1. ブラウザアクセス
    ブラウザで http://localhost:5000/ へアクセスして、「OIDCでログイン」をクリック

  2. Azure Entra IDのログイン画面へリダイレクト

4.ログイン確認
ようこそ、[名前] さん! と表示されなます

3. OIDCフローとコードの紐づけ

フロー コード/エンドポイント ポイント(現場で何が起きているか)
(1) サービス要求 / 「OIDCでログイン」リンク表示
(2) 認証要求リダイレクト /login authorize_redirectでIdP(Entra ID)へリダイレクト、nonce生成
(3) 認証画面(ユーザー認証) Entra ID Azureのログイン画面でID入力・パスワード認証
(4) 認証コード受領(リダイレクト) /oidc/callback Entra IDから「認証コード」をクエリパラメータで受け取る(GETアクセス)
(5) トークンリクエスト(バックエンド通信) /oidc/callback サーバーが認証コードを使いEntra IDに「ID/アクセストークン」をリクエスト(POST通信)
(6) トークン検証・セッション格納 /oidc/callback IDトークン(JWT)を検証し、ユーザー情報をsession['user']に格納
(7) サービス提供 / ログイン済みユーザー情報でページ表示

4. まとめ

OIDCはSAMLよりシンプル
Python/FlaskとAuthlibを使えば、10分でデモが作れる

Discussion