Laravel Socialite を使って Entra ID で SSO できるようにする(実装編)

前回の記事を書いてから半年以上が経ってしまいましたが、Laravel Socialite を使って Entra ID で SSO できるようにするの Laravel 側の実装編です。

基本的には https://socialiteproviders.com/Saml2/ の説明に沿って進めていきます。

Saml2 Service Provider のインストール

composer require socialiteproviders/saml2

設定ファイルの編集

config/services.php に必要な設定を追加しますが、Entra ID の場合、実際に必要なのは以下の2項目だけです。

    'saml2' => [
        'metadata' => '(準備編で取得した「アプリのフェデレーションメタデータURL」)', // IdPのMetadata
        'sp_default_binding_method' => \LightSaml\SamlConstants::BINDING_SAML2_HTTP_POST,
    ],

サービスプロバイダの登録

bootstrap/providers.php\SocialiteProviders\Manager\ServiceProvider::class を追加します。他にサービスプロバイダを追加していなければ、bootstrap/providers.php はこんな感じになるはずです。

<?php

return [
    App\Providers\AppServiceProvider::class,
    \SocialiteProviders\Manager\ServiceProvider::class,
];

イベントリスナの追加

app/Providers/AppServiceProvider.php の boot メソッドにイベントリスナを追加します。

public function boot(): void
{
    Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
        $event->extendSocialite('saml2', \SocialiteProviders\Saml2\Provider::class);
    });
}

ルートの追加

上記までで準備ができたので、routes/web.php に認証に必要なルートを追加します。

Route::get('/auth/redirect', function () {
    return Socialite::driver('saml2')->redirect();
})->name('saml2.login');
Route::post('/auth/callback', function () {
    $entraUser = Socialite::driver('saml2')->stateless()->user();
    // Entra で認証済みのユーザー情報でユーザーインスタンスを作成・更新
    $user = User::updateOrCreate([
        'entra_id' => $entraUser->id,
    ], [
        'name' => $entraUser->last_name . ' ' . $entraUser->first_name,
        'email' => $entraUser->email,
    ]);
    Auth::login($user);
    return redirect()->route('home');
});

/auth/redirect へのリンクをクリックすると、Microsoft アカウントへのログイン画面が表示されます。認証後、/auth/callback に戻ってくるので、取得したユーザー情報を使ってユーザーインスタンスを作成・更新し、Auth::login メソッドに渡して認証しています。

CSRF エラー対応

上記までの作業を行ってテストを行うと、CSRF のエラーが発生します。Entra から戻ってくる /auth/callback の POST リクエストに CSRF トークンが含まれていないからです。そこで、bootstrap/app.php に下記を追加して、CSRF のチェックを無効にします。

    ->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            'auth/callback',
        ]);
    })

まとめてみると、比較的簡単に SAML によるシングルサインオンが実装できるのですが、修正するファイルがあちこちに散らばっているので、修正漏れ→動かないとなりがちです。そんなときにこの記事を思い出していただければと思います。

この記事を書いた人
グッドネイバー

“ Webに悩むお客さまの「よき隣人」でありたい ” をモットーに、Web システム開発(主に Laravel)、Web マーケティング支援の仕事をしています。お仕事のご依頼・ご相談はこちらからお気軽にどうぞ。