社内システムにシングルサインオン導入 その2

前回の続き、SSO(シングルサインオン)導入について書いていこうと思います。

プロトコル

前回、SSOの流れを説明いたしましたが、SSOを実現するためのプロトコルを決める必要があります。いくつかの選択肢がありますが、代表的なものを記載します。

SSOの代表的プロトコル
・SAML          古くからある企業向けSSOプロトコル(XMLベース、SaaS連携に多い)
・OpenID Connect(OIDC) モダンなWeb・モバイル対応の認証プロトコル(OAuth 2.0ベース、JSON)
・OAuth 2.0      ユーザーに代わってAPIアクセスを許可するプロトコル(認可が目的)
・Kerberos      社内ネットワーク用のWindows系SSOプロトコル

今回はMicrosoft AD認証を考えていますので、推奨としているOIDCかOAuth 2.0が候補。また、PHPのWebアプリであれば、ライブラリが豊富で扱いやすいOIDCの選択が良いように思えます。

前回、SSOの全体構成と流れを記載しましたが、OIDCがサポートする部分について書いておきたいと思います。

SSOの全体構成と流れ
ユーザー
  ↓
SSOポータル / SP
  ↓
(未認証時 / 認証済の場合はアプリ利用開始)
  ↓
IdP(Identity Provider) ★Microsoft ADがカバー
  ↓
ユーザーストア ★Microsoft ADがカバー
  ↓
トークン発行 ★OIDC開始 
  ↓
SPにリダイレクト戻り ★OIDCコアプロトコル
  ↓
SPで認証成功 ★OIDCでIDトークンを検証
  ↓
アプリ利用開始

という事で、OIDCを組み込んでいきます。

その前にMicrosoft EntraID でアプリを登録します。

登録の手順はこんな感じです

手順

  1. Microsoft Entra 管理センター にログイン
  2. 「アプリの登録」 > 「新規登録」 で、今回対象のアプリを登録
  3. リダイレクト URI(Web)として、コールバックのURLを登録(今回は<アプリケーションURL>/oidc-callback.phpとする)
  4. 登録後、「クライアントID」(アプリケーションID)、「ディレクトリID」(テナントID)の値を控える(後で使う)
  5. 「証明書とシークレット」→「新しいクライアントシークレット」を作成し、値を控える(後で使う)
  6. 「APIのアクセス許可」→「Microsoft Graph」→「openid」「email」「profile」を追加

それではいよいよ、PHPアプリケーションにSSOの組み込みです。

OIDC ライブラリ導入

まずはOIDC ライブラリを導入します。有名どころで、jumbojett/OpenID-Connect-PHP を使用する事にします。

composer require jumbojett/openid-connect-php

で、さくっとライブラリの導入終了です。

PHPの組み込み

次に、PHPアプリケーションの組み込みです。とは言っても、実装する内容はそれほど多くありません。次の2つを組み込めば、ほぼ完了です。

oidc-login.php(ログイン開始)
<?php
use Jumbojett\OpenIDConnectClient;

// Azure ADの設定
$tenantId = '<テナントID>';
$clientId = '<クライアントID>';
$clientSecret = '<クライアントシークレット>';
$redirectUri = '<アプリケーションURL>/oidc-callback.php';

try {
    $oidc = new OpenIDConnectClient("https://login.microsoftonline.com/$tenantId/v2.0", $clientId, $clientSecret);
    
    echo "OIDC クライアント作成成功";

    $oidc->setRedirectURL($redirectUri);
    $oidc->addScope(['openid', 'profile', 'email']);

    $oidc->authenticate(); 
    echo "認証成功!";

} catch (Exception $e) {
    echo "エラー発生: " . $e->getMessage();
}
oidc-callback.php(認証後の処理)
<?php
require_once __DIR__ . '/vendor/autoload.php';

session_start();

use Jumbojett\OpenIDConnectClient;

// OIDC設定
$tenantId = '<テナントID>';
$clientId = '<クライアントID>';
$clientSecret = '<クライアントシークレット>';
$redirectUri = '<アプリケーションURL>/oidc-callback.php';

try {
    $oidc = new OpenIDConnectClient("https://login.microsoftonline.com/$tenantId/v2.0", $clientId, $clientSecret);
    $oidc->setRedirectURL($redirectUri);
    $oidc->addScope(['openid', 'profile', 'email']);
    $oidc->authenticate();

    $idToken = $oidc->getIdToken();
    $tokenParts = explode('.', $idToken);
    $payload = base64_decode(strtr($tokenParts[1], '-_', '+/'));
    $payloadData = json_decode($payload);
    //UPNをメールアドレスとしてアプリケーションに登録済み
    $email = $payloadData->upn;
    if (!$email) {
        throw new Exception("メールアドレスが取得できませんでした。");
    }
    // メールアドレスでアプリケーションのユーザーを検索
    $sql = '<メールアドレスでアプリケーションのユーザーを検索>';
    $result = $adb->pquery($sql, array($email, mb_strstr($email, '@', true)));

    if ($adb->num_rows($result) > 0) {
        $userId = $adb->query_result($result, 0, 'id');
	$username = $adb->query_result($result, 0, 'user_name');

        // セッションにユーザー情報をセット
	$_SESSION['authenticated_user_id'] = $userid;

        // ログイン成功後にリダイレクト
        header('Location: index.php');
        exit;

    } else {
        // ユーザーが存在しない場合の処理
        echo "ユーザーが見つかりません。管理者にお問い合わせください。";
        exit;
    }

} catch (Exception $e) {
    echo "エラー発生: " . $e->getMessage();
}

後は、アプリケーションのログイン画面や社内のポータルサイトに、「Microsoft ID でログイン」ボタンを追加し、oidc-login.phpへ誘導すればOKです。

ざっくりですが、こんな感じでSSOの組み込みが完了しました。
流れさえわかっていれば、それほど難しい内容ではないですね。