Laravel 5에서 OpenAM SAML 인증 지원

OpenAM과 연계하여 Laravel에서 작성한 웹 애플리케이션을 SAML2 인증에 대응시키는 방법입니다.

아래 샘플을 만들 때 리포지토리를 github에서 공개하고 있습니다.
htps : // 기주 b. 코 m / 타츠야 우에다 / ぁらゔぇl ML2_ mp ぇ

이미지의 크기가 너무 큰 것은 마음이 바뀌면 수정합니다.

라라벨 환경 준비



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 또는 파일로 지정하고 [확인]을 클릭합니다.
웹 브라우저에서/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 버전 업



    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' => '[email protected]'
            ),
            'support' => array(
                'givenName' => 'Support',
                'emailAddress' => '[email protected]'
            ),
        ),
        'organization' => array(
            'en-US' => array(
                'name' => 'Name',
                'displayname' => 'Display Name',
            ),
        ),
       'wantAssertionsSigned' => true,
       'wantNameIdEncrypted' => false,
    );
    

    SAML2 로그인 시 URL



    지금까지는/saml2/login 이었지만/saml2/[idpName]/login 입니다.
    위의 구성 파일은/saml2/kcprd/login입니다.

    좋은 웹페이지 즐겨찾기