9
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel 5 でOpenAMのSAML認証に対応する

Last updated at Posted at 2017-01-22

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」をクリックします。
001.png

エンティティープロバイダ」から「エンティティーのインポート」をクリックします。
002.png

メタデータファイルの場所をURLもしくは、ファイルで指定し、「了解」をクリックします。
Webブラウザで /saml2/metadata にアクセスした際に表示される内容がメタデータとなります。

003.png

すでに作成されているトラストサークルの名前をクリックします。下記の場合、「SimpleSAML」となります。

004.png

「エンティティープロバイダ」の「選択可能」に上記でインポートしたエンティティープロバイダが登録されているので、
それを選択し、「追加」をクリックします。続けて、右上の「了解」をクリックします。

005.png

ここまでの作業で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のバージョンアップ

composer.json
"require": {
        "aacotroneo/laravel-saml2": "^2.0",
}
# composer php update aacotroneo/laravel-saml2

設定ファイルの更新

config/saml2_settings.php

idpNamesの連想配列を追加し、strict/debug/sp/idp/securityのをそっくり削除
削除した部分は次の設定ファイルに移動します。

config/saml2_settings.php
    // 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で削除した部分をそのまま入れれば大丈夫だと思います。

config/saml2/kcprd_idp_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 となります。

9
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?