前回の記事 「Laravel 5.8からMicrosoft Graph APIを利用する (公式ドキュメント編)」では、Micsoroft公式のチュートリアル ドキュメントに沿って、LaravelアプリケーションからMicrosoft Graph APIをOAuth認証プロバイダとして利用する手順について確認しました。
このチュートリアルは、OAuthの認証ロジックをleague/oauth2-clientを使って独自に実装していましたが、実際はLaravel公式のOAuth認証パッケージLaravel Socialiteを使いたいところです。
Laravel Socialiteは非常に多くの認証プロバイダーに対応しています。対応プロバイダーは、Socialite Providersで確認できます。
Microsoft Graphについてももちろん対応しています。
この記事では、Laravel Socialiteを利用してAzure ADのアカウントで認証を実装する手順について確認します。
Auth Scaffoldは使わず、つまりデータベース認証は使わずに、OAuth認証後にLaravel標準のAuthユーザとしてログインし、Auth middlewareを使って要認証のルーティングを保護する手順についても説明しています。
この記事に含まないこと
Azure ADに登録されたアカウントによる認証にのみフォーカスして説明しています。
Microsoft Graph APIを利用したデータの取得については説明しません。
よって、アクセストークンの更新についても言及していません。
Laravelプロジェクトの前提条件
必須ではないですが、Laravel Homestead上でプロジェクトを動かしています。
ホスト名はデフォルトの homestead.test
です。
Laravel Homestead環境のセットアップについては、「2019版 Laravel Homestead セットアップからhttpsによるアクセス手順まで」を参照ください。
Azure ADへアプリケーションを登録
前回の記事の「Azure Active Directory 管理センターでアプリを登録する」の手順に従って、アプリケーションを作成してください。
homestead.test
で動作を確認する場合は、リダイレクトURIにhttps://homestead.test/authorize
を追加してください。
socialiteproviders/microsoft-graphのインストール
Laravelプロジェクトに、Microsoft Graph用Socialiteパッケージsocialiteproviders/microsoft-graph
をインストールします。
Socialite自体は依存関係でインストールされるので明示的にインストールする必要はありません。
$ composer require socialiteproviders/microsoft-graph
providers
に\SocialiteProviders\Manager\ServiceProvider::class
を追加します。
'providers' => [
...
\SocialiteProviders\Manager\ServiceProvider::class,
],
続いて、イベント\SocialiteProviders\Manager\SocialiteWasCalled::class
をリスナーSocialiteProviders\\Graph\\GraphExtendSocialite@handle
を指定して追加します。
// App/Providers/EventServiceProvider
<?php
namespace App\Providers;
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class,
],
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
// add your listeners (aka providers) here
'SocialiteProviders\\Graph\\GraphExtendSocialite@handle',
],
];
// 略
}
アプリケーションID・シークレット・リダイレクトURIを設定
config/services.php
に、以下の設定を追加します。
'graph' => [
'client_id' => env('GRAPH_KEY'),
'client_secret' => env('GRAPH_SECRET'),
'redirect' => env('GRAPH_REDIRECT_URI')
],
.env
にアプリケーションID(クライアントID)、シークレット、リダイレクトURIを追加します。
GRAPH_KEY=xxxxxxxxx-0000-0000-0000-xxxxxxx
GRAPH_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX
GRAPH_REDIRECT_URI=http://homestead.test/authorize
OAuth認証の実装
LoginControllerを生成して、認証ロジックを追加していきます。
$ php artisan make:controller LoginController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use Socialite;
class LoginController extends Controller
{
/**
* Redirect the user to the graph authentication page.
*
* @return \Illuminate\Http\Response
*/
public function redirectToProvider()
{
return Socialite::driver('graph')->redirect();
}
/**
* Obtain the user information from graph.
*
* @return \Illuminate\Http\Response
*/
public function handleProviderCallback()
{
$socialiteUser = Socialite::driver('graph')->user();
dd($socialiteUser);
}
}
一旦、Socialiteのuser
メソッドから取得したインスタンスのダンプを表示して、ログインしたアカウント情報を確認するようにしています。
ルーティングroutes/web.php
にコントローラLoginController
のアクションを追加
Route::get('login', 'LoginController@redirectToProvider')->name('login');
Route::get('authorize', 'LoginController@handleProviderCallback')->name('authorize');
Laravel標準のAuthミドルウェアは、未ログイン時にはlogin
というルーティング名にリダイレクトするので、ルーティング名を指定しておきます。
参照: Authenticationの[Redirecting Unauthenticated Users]
ここまでで、https://homestead.test/login
をブラウザで開きMicrosoft アカウントでログインすると以下のように取得したユーザの情報を確認できます。
ユーザレコードの追加とAuth::login
LoginController
のhandleProviderCallback
に、Userモデルのレコード保存と生成して、LaravelのAuthのユーザとしてログインする処理を追加していきます。
handleProviderCallback
を以下のように書き換えます。
public function handleProviderCallback()
{
$socialiteUser = Socialite::driver('graph')->user();
$user = User::where(['email' => $socialiteUser->getEmail()])->first();
// 新規ユーザの場合は、レコードを追加
if(!$user){
$user = new User;
$user->name = $socialiteUser->getName();
$user->email = $socialiteUser->getEmail();
$user->save();
}
Auth::login($user);
return redirect()->route('/');
}
マイグレーションを実行して、Userモデル用のテーブルを追加しておきます。
パスワードは不要なので、とりあえず2014_10_12_000000_create_users_table.php
を開きnullable
にしておきます。
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password')->nullable();
$table->rememberToken();
$table->timestamps();
});
}
マイグレーションを実行します。
$ artisan migrate
Migration table created successfully.
Migrating: 2014_10_12_000000_create_users_table
Migrated: 2014_10_12_000000_create_users_table (0.08 seconds)
Migrating: 2014_10_12_100000_create_password_resets_table
Migrated: 2014_10_12_100000_create_password_resets_table (0.1 seconds)
確認用に、resources/views/welcom.blade.php
を開き以下の箇所を修正します。
Before
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">Login</a>
@if (Route::has('register'))
<a href="{{ route('register') }}">Register</a>
@endif
@endauth
</div>
@endif
After
<div class="top-right links">
@auth
<span>{{Auth::user()->name}}</span>
@else
<a href="{{ route('login') }}">Login</a>
@endauth
</div>
再びhttps://homestead.test/login
をブラウザで開きMicrosoft アカウントでログインするとWelcomeページにリダイレクトされます。
右上にログインしたユーザが表示されています。
データベースのusers
テーブルには、以下の通りレコードが追加されています。
mysql> select * from users;
+----+---------------+----------------------------------+-------------------+----------+----------------+---------------------+---------------------+
| id | name | email | email_verified_at | password | remember_token | created_at | updated_at |
+----+---------------+----------------------------------+-------------------+----------+----------------+---------------------+---------------------+
| 1 | 鈴木 一郎 | suzuki@o365trial.onmicrosoft.com | NULL | NULL | NULL | 2019-08-13 05:46:50 | 2019-08-13 05:46:50 |
+----+---------------+----------------------------------+-------------------+----------+----------------+---------------------+---------------------+
1 row in set (0.00 sec)
auth middlewareの設定
パス /
を要認証にしてみます。routes/web.php
を以下のように書き換えます。
通常は、要認証のルーティングは複数あるはずはので、以下は1つですがRoute::group
を使っています。
Route::group(['middleware' => 'auth'], function() {
Route::get('/', function () {
return view('welcome');
});
});
未認証でhttps://homestead.test
にアクセスすると、自動的にMicrosoftアカウントの認証ページにリダイレクトするようになります。
シングルテナントモードへの対応
アプリケーションを新規作成時に[サポートされているアカウントの種類]で「この組織ディレクトリのみに含まれるアカウント (株式会社XXXXX のみ – シングル テナント)」を選択した場合、認証エンドポイントはテナントIdを指定したURL「https://login.microsoftonline.com/<テナントId>/oauth2/v2.0/authorize」になります。つまり「https://login.microsoftonline.com/common/oauth2/v2.0/authorize」は使えません。
シングルテナントモードの場合は、Using a Custom Tenant Idに記載されている通りsetTenantId
でテナントIdをセットすればOKです。
テナントIdはAzure Active Directory管理センターのアプリケーションの概要で確認できます。
環境変数ADD_TENANT_IDにテナントIDをセットするようにしてみます。
// .env
ADD_TENANT_ID=xxxxxxxx
LoginController.phpの場合は以下のように修正します。
class LoginController extends Controller
{
private $tenantId;
public function __construct(){
$this->tenantId = env('ADD_TENANT_ID');
}
/**
* Redirect the user to the graph authentication page.
*
* @return \Illuminate\Http\Response
*/
public function redirectToProvider()
{
return Socialite::driver('graph')->setTenantId($tenantId)->redirect();
}
/**
* Obtain the user information from graph.
*
* @return \Illuminate\Http\Response
*/
public function handleProviderCallback()
{
$socialiteUser = Socialite::driver('graph')->setTenantId($tenantId)->user();
// 中略
return redirect('/');
}
}