OpenAMと連携し、Laravelで作成したWebアプリケーションをSAML2認証に対応させる方法です。
下記のサンプルを作った際のリポジトリをgithubで公開しています。
→ https://github.com/tatsuyaueda/LaravelSAML2_Sample
画像のサイズが大きすぎるのは、気が向いたら直します。
Laravelの環境を準備する
Laravelのプロジェクトを作成する
composer create-project laravel/laravel LaravelSAML2 --prefer-dist
プロジェクトのディレクトリに移動する
cd LaravelSAML2
SAML2のモジュールを追加する
composer require aacotroneo/laravel-saml2
Laravelで認証を有効にする
php artisan make:auth
.envファイルを編集し、データベースに接続出来るようにする
MySQLの場合はこんな感じ
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=foobar
DB_USERNAME=foobar
DB_PASSWORD=foobar
認証関係のテーブルをマイグレーションする
php artisan migrate
ここで動作確認用のユーザを登録し、そのユーザでログインできることを確認します。
設定ファイルの編集
config/app.php
'providers' => [
...
Aacotroneo\Saml2\Saml2ServiceProvider::class,
]
'alias' => [
...
'Saml2' => Aacotroneo\Saml2\Facades\Saml2Auth::class,
]
config/saml2_settings.php
下記のコマンドでテンプレートが自動生成されます。
php artisan vendor:publish
entityId は OpenAM と合わせる必要があります。
(違っていると、LaravelのログでSaml2 error ["invalid_response"]が出力されます。)
<?php
return $settings = array( 'useRoutes' => true,
'routesPrefix' => '/saml2',
'routesMiddleware' => ['saml'],
'retrieveParametersFromServer' => false,
'logoutRoute' => '/logout',
'loginRoute' => '/home',
'errorRoute' => '/error',
'strict' => true, //@todo: make this depend on laravel config
'debug' => true, //@todo: make this depend on laravel config
'sp' => array(
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
'x509cert' => 'foobar',
'privateKey' => 'foobar',
),
'idp' => array(
'entityId' => 'https://openam.example.com/OpenAM',
'singleSignOnService' => array(
'url' => 'https://openam.example.com/OpenAM/SSORedirect/metaAlias/idp',
),
'singleLogoutService' => array(
'url' => 'https://openam.example.com/OpenAM/IDPSloRedirect/metaAlias/idp',
),
'certFingerprint' => 'foobar',
),
'security' => array(
'nameIdEncrypted' => false,
'authnRequestsSigned' => true,
'logoutRequestSigned' => false,
'logoutResponseSigned' => false,
'signMetadata' => false,
'wantMessagesSigned' => false,
'wantAssertionsSigned' => false,
'wantNameIdEncrypted' => false,
'requestedAuthnContext' => true,
),
);
app/Http/Kernel.php
protected $middlewareGroups = [
...
'saml' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
app/Providers/EventServiceProvider.php
use App\User;
public function boot() {
....
Event::listen('Aacotroneo\Saml2\Events\Saml2LoginEvent', function ($event) {
$user = $event->getSaml2User();
$userData = [
'id' => $user->getUserId(),
'attributes' => $user->getAttributes(),
'assertion' => $user->getRawSamlAssertion()
];
$laravelUser = User::where('email', $user->getUserId())
->first();
Auth::login($laravelUser);
});
Event::listen('Aacotroneo\Saml2\Events\Saml2LogoutEvent', function ($event) {
//Auth::logout();
//Session::save();
});
}
動作確認
/saml2/login にアクセスすると、SAMLで認証が行われ、認証に成功すると /home、失敗すると /error にリダイレクトされます。
ログインページなどに、このURLへのリンクを作成すると便利だと思います。
OpenAMの設定
下記の操作はすでに、トラストサークルが作成されている状態から進めます。
エンティティーの作成
OpenAMの管理画面にログインし、「Federation」をクリックします。
エンティティープロバイダ」から「エンティティーのインポート」をクリックします。
メタデータファイルの場所をURLもしくは、ファイルで指定し、「了解」をクリックします。
Webブラウザで /saml2/metadata にアクセスした際に表示される内容がメタデータとなります。
すでに作成されているトラストサークルの名前をクリックします。下記の場合、「SimpleSAML」となります。
「エンティティープロバイダ」の「選択可能」に上記でインポートしたエンティティープロバイダが登録されているので、
それを選択し、「追加」をクリックします。続けて、右上の「了解」をクリックします。
ここまでの作業でOpenAM側の設定が完了です。
laravel-saml2 0.11系から2.10系にバージョンアップする
laravel-saml2がSAMLライブラリとして利用しているonelogin/php-samlは2.0系までphp-mcryptに依存しています。
php-mcryptはPHP 7.1.0で非推奨に、PHP 7.2.0で廃止されてしまいました。
- こんな感じ
- laravel-saml2 0.11系→php-saml 2.0系→php-mcrypt→PHP 7.1系
- laravel-saml2 2系→php-saml 3.0系→php-cryptへの依存無し
たいした変更じゃないですが、laravel-saml2 2.0系では複数のIDPに対応した関係で、設定ファイルの構成が変更となっているため、バージョンアップ時のmemoです。
laravel-saml2のバージョンアップ
"require": {
"aacotroneo/laravel-saml2": "^2.0",
}
# composer php update aacotroneo/laravel-saml2
設定ファイルの更新
config/saml2_settings.php
idpNamesの連想配列を追加し、strict/debug/sp/idp/securityのをそっくり削除
削除した部分は次の設定ファイルに移動します。
// ADD
// laravel-saml2 2.0系から複数のIDPを設定出来るようになったため、その識別子
'idpNames' => ['kcprd'],
// DELETE
'strict' => true, //@todo: make this depend on laravel config
'debug' => env('APP_DEBUG'),
'sp' => array(
....
),
'idp' => array(
....
),
'security' => array(
....
),
config/saml2/kcprd_idp_settings.php
ファイル名の kcprd はsaml2_settings.phpで設定した idpNames と合わせます。
$this_idp_env_id は環境変数からパラメタを定義する際の識別子で、idpNamesと合わせるのが良いかと思います。
基本的にOnelogin samlのファイルそのままです。
$settingsの中身はsaml2_settings.phpで削除した部分をそのまま入れれば大丈夫だと思います。
<?php
// If you choose to use ENV vars to define these values, give this IdP its own env var names
// so you can define different values for each IdP, all starting with 'SAML2_'.$this_idp_env_id
$this_idp_env_id = 'KCPRD';
//This is variable is for simplesaml example only.
// For real IdP, you must set the url values in the 'idp' config to conform to the IdP's real urls.
return $settings = array(
/*****
* One Login Settings
*/
'debug' => env('APP_DEBUG', false),
'sp' => array(
'NameIDFormat' => 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
'x509cert' => env('SAML2_'.$this_idp_env_id.'_SP_x509',''),
'privateKey' => env('SAML2_'.$this_idp_env_id.'_SP_PRIVATEKEY',''),
'entityId' => env('SAML2_'.$this_idp_env_id.'_SP_ENTITYID',''),
'assertionConsumerService' => array(
'url' => '',
),
'singleLogoutService' => array(
'url' => '',
),
),
'idp' => array(
'entityId' => env('SAML2_'.$this_idp_env_id.'_IDP_ENTITYID', $idp_host . '/saml2/idp/metadata.php'),
'singleSignOnService' => array(
'url' => env('SAML2_'.$this_idp_env_id.'_IDP_SSO_URL', $idp_host . '/saml2/idp/SSOService.php'),
),
'singleLogoutService' => array(
'url' => env('SAML2_'.$this_idp_env_id.'_IDP_SL_URL', $idp_host . '/saml2/idp/SingleLogoutService.php'),
),
'x509cert' => env('SAML2_'.$this_idp_env_id.'_IDP_x509', ''),
/*
* Instead of use the whole x509cert you can use a fingerprint
* (openssl x509 -noout -fingerprint -in "idp.crt" to generate it)
*/
),
/***
* OneLogin advanced settings
*/
'security' => array(
'nameIdEncrypted' => false,
'authnRequestsSigned' => false,
'logoutRequestSigned' => false,
'logoutResponseSigned' => false,
'signMetadata' => false,
'wantMessagesSigned' => false,
'wantAssertionsSigned' => false,
'wantNameIdEncrypted' => false,
'requestedAuthnContext' => true,
),
'contactPerson' => array(
'technical' => array(
'givenName' => 'name',
'emailAddress' => 'no@reply.com'
),
'support' => array(
'givenName' => 'Support',
'emailAddress' => 'no@reply.com'
),
),
'organization' => array(
'en-US' => array(
'name' => 'Name',
'displayname' => 'Display Name',
),
),
'wantAssertionsSigned' => true,
'wantNameIdEncrypted' => false,
);
SAML2ログイン時のURL
今までは /saml2/login でしたが、 /saml2/[idpName]/login となります。
上の設定ファイルでは、 /saml2/kcprd/login となります。