tohokuaikiのチラシの裏

技術的ネタとか。

Laravel11にAzure Entra IDのOpenIDのサインインを使う

なんか色々と試行錯誤したのでメモしておく。

Azure Entra管理センターでの設定

アプリの登録

「この組織ディレクトリのみに含まれるアカウント」のシングルテナントにする。
リダイレクトURLはWEBを選択して後でURLも自分のLaravelアプリケーションのURLを設定する。これは後で「概要」から変更できるし、テスト用に2つ目を追加できたりもする。

作成後に出てくる「アプリケーション (クライアント) ID」と「ディレクトリ (テナント) ID」をメモしておく。(このアプリケーションは記事作成後に消しています)

証明書とシークレットを作る。

作成したら、「値」の方をメモしておく。この値は作成直後しか表示されないので注意。(このシークレットは当然(略…)

APIのアクセス制限とかそのまま

APIのアクセス制限でデフォルトでUser.Readがあるけど、どうもこれは要らないみたい。あってもいいのかもしれない。そのままにしておく。

Laravel11側の設定

ライブラリのインストール

composer require  laravel/socialite socialiteproviders/microsoft-azure

Service Providerに登録

Event Facadeで登録しておく。

<?php

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Event::listen(function (\SocialiteProviders\Manager\SocialiteWasCalled $event) {
            $event->extendSocialite('azure', \SocialiteProviders\Azure\Provider::class);
        });
    }
}
<?php
return [
    SocialiteProviders\Manager\ServiceProvider::class,
];

Configureを追加

Azure用の設定値

<?php
return [
    'azure' => [
        'client_id' => env('AZURE_CLIENT_ID'),
        'client_secret' => env('AZURE_CLIENT_SECRET'),
        'redirect' => env('AZURE_REDIRECT_URI'),
        'tenant' => env('AZURE_TENANT_ID'),
    ],
];

.envに先ほどのAzureで登録した値を追加。

AZURE_CLIENT_ID=「アプリケーション (クライアント) ID」
AZURE_CLIENT_SECRET=「シークレットの値」
AZURE_REDIRECT_URI=「リダイレクトURL」
AZURE_TENANT_ID=「ディレクトリ (テナント) ID」

Routingを追加

最初のログインアクセスURLを登録。 http://laravel.example.com/azure/login にアクセスするとMicrosoftのログイン画面にリダイレクトされる。

<?php
Route::get('/azure/login', function () {
    return Socialite::driver('azure')->redirect();
})->name('microsoft.login');

認証を経て返ってきたデータを処理する。以下はAzureから返されるユーザー情報を表示するだけだが、実際はLoginしたり、初めてのログインならユーザー登録をしたりする。

うまくいかなかった時や、callbackされたURLそのままでリロードした時のエラー処理などもしておくとよい。

<?php
Route::get('/azure/callback', function () {
    try {
        $user = Socialite::driver('azure')->user();
        // とりあえずAzureから返されるユーザー情報を表示するだけ
        dd($user);
        exit;
        // 認証成功時の処理
    } catch (ClientException $e) {
        // Guzzleのレスポンス本文を全文取得して表示
        $response = $e->getResponse();
        $body = $response ? $response->getBody()->getContents() : 'No response body';

        \Log::error('Azure callback Guzzle error: ' . $body);
        return response("Guzzleエラー: <pre>$body</pre>", 500);
    } catch (\Exception $e) {
        // その他のエラー
        \Log::error('Callback error: ' . $e->getMessage());
        return response("例外: {$e->getMessage()}", 500);
    }    
    // Auth::login();
    return redirect(config('app.admin_path'));
})->name('microsoft.callback');