3
2

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 3 years have passed since last update.

Laravel Passport と LdapRecord-Laravel を利用した API 認証基盤の構築方法

Last updated at Posted at 2021-01-26

実証環境

  • OS
    • Amazon Linux 2
  • PHP
    • 7.2.34
  • Laravel
    • 7.30.4
  • LDAP
    • OpenLDAP 2.4.44

環境構築

OpenLDAP

yum を使ってインストール

$ sudo yum -y install openldap-clients openldap-servers

設定ファイルを用意

$ sudo cp /usr/share/openldap-servers/DB_CONFIG.example /var/lib/ldap/
$ sudo chown ldap.ldap /var/lib/ldap/DB_CONFIG

起動と自動起動設定

OpenLDAP サーバーの起動と、自動起動を有効化する

$ sudo systemctl start slapd
$ sudo systemctl enable slapd

OpenLDAP サーバーの起動設定が有効化できているかの確認をする

$ sudo systemctl is-enabled slapd
enabled

OpenLDAP サーバーが LISTEN しているかを確認する

$ netstat -atnl | grep ':389.*LISTEN'
tcp        0      0 0.0.0.0:389             0.0.0.0:*               LISTEN     
tcp6       0      0 :::389                  :::*                    LISTEN

LDAP リポジトリの実装

管理者パスワードを設定する

slappasswd を使って OpenLDAP 管理者パスワードを生成する(後ほど利用する)

$ slappasswd 
New password: 
Re-enter new password: 
{SSHA}ALgM7/V/tIQv53Dx4KSpGY6RW4AtnRPS

管理者アカウント用の LDIF を用意する(パスは任意だが分かりやすいように ${HOME} 環境変数直下に ldif ディレクトリを作成した)

  • ${HOME}/ldif/add-root-account.ldif
dn: olcDatabase={0}config,cn=config
changetype: modify
add: olcRootPW
olcRootPW: {SSHA}ALgM7/V/tIQv53Dx4KSpGY6RW4AtnRPS

ldapadd コマンドで LDAP サーバー(slapd)へ反映

$ ldapadd -Y EXTERNAL -H ldapi:/// -f ${HOME}/ldif/add-root-account.ldif

ドメインリポジトリ(example.com)を実装する

ドメインリポジトリの実装を進める前提として、リポジトリマネージャーのパスワードを slappasswd で設定し、LDIF で利用する

$ slappasswd 
New password: 
Re-enter new password: 
{SSHA}UESMaOwll1zRkb15o9IiSm27bS0CZ+bC
  • ${HOME}/ldif/add-repository-manager.ldif
dn: olcDatabase={1}monitor,cn=config
changetype: modify
replace: olcAccess
olcAccess: {0}to * by dn.base="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth"
  read by dn.base="cn=Manager,dc=example,dc=com" read by * none

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=example,dc=com

dn: olcDatabase={2}hdb,cn=config
changetype: modify
replace: olcRootDN
olcRootDN: cn=Manager,dc=example,dc=com

dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcRootPW
olcRootPW: {SSHA}UESMaOwll1zRkb15o9IiSm27bS0CZ+bC

dn: olcDatabase={2}hdb,cn=config
changetype: modify
add: olcAccess
olcAccess: {0}to attrs=userPassword,shadowLastChange by
  dn="cn=Manager,dc=example,dc=com" write by anonymous auth by self write by * none
olcAccess: {1}to dn.base="" by * read
olcAccess: {2}to * by dn="cn=Manager,dc=example,dc=com" write by * read

ldapadd コマンドで LDAP サーバー(slapd)へ反映

$ ldapadd -Y EXTERNAL -H ldapi:/// -f ${HOME}/ldif/add-repository-manager.ldif

ドメインリポジトリを実装するため、${HOME}/ldif/add-dc=example,dc=com.ldif を用意する

  • ${HOME}/ldif/add-dc=example,dc=com.ldif
dn: dc=example,dc=com
objectClass: top
objectClass: dcObject
objectclass: organization
o: example
dc: example

dn: cn=Manager,dc=example,dc=com
objectClass: organizationalRole
cn: Manager
description: Directory Manager

dn: ou=People,dc=example,dc=com
objectClass: organizationalUnit
ou: People

dn: ou=Group,dc=example,dc=com
objectClass: organizationalUnit
ou: Group

ldapadd コマンドで LDAP サーバー(slapd)へ反映

$ ldapadd -x -D cn=Manager,dc=example,dc=com -w ${YOUR_REPOSITORY_MANAGER_PASSWORD} -f ${HOME}/ldif/add-dc=example,dc=com.ldif

LDAP 認証連携用のユーザーを登録する

認証試験用のユーザーパスワードを slappasswd で設定し、LDIF で利用する

$ slappasswd 
New password: 
Re-enter new password: 
{SSHA}twe1y8JFGBVJv9fLtl62oKj6wwt5q8WZ
  • ${HOME}/ldif/dc=example,dc=com/ou=People/add-user.ldif
dn: uid=user,ou=People,dc=example,dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
uid: user
cn: user
sn: user
o: People
userPassword: {SSHA}twe1y8JFGBVJv9fLtl62oKj6wwt5q8WZ
  • userPassword1qazxsw23edc で設定(各位の環境に合わせること)

ldapmodify コマンドで LDAP サーバー(slapd)へ反映

$ ldapadd -x -D cn=Manager,dc=example,dc=com -w ${YOUR_REPOSITORY_MANAGER_PASSWORD} -f ${HOME}/ldif/dc=example,dc=com/ou=People/add-user.ldif

Laravel

php-ldap をインストールする

Laravel から LDAP サーバーへアクセスするために php-ldap 拡張モジュールをインストールする必要があるため、yum intall でインストールする

$ sudo yum -y install php-ldap

Composer を使ってプロジェクトを作成する

$ composer create-project --prefer-dist laravel/laravel ldap-record-laravel-passport

LdapRecord-Laravel

Composer を使ってインストール

  • APP_ROOT へ cd してから LdapRecord-Laravel パッケージをインストールする
$ cd ldap-record-laravel-passport/
$ composer require directorytree/ldaprecord-laravel
  • artisan コマンドを使って設定ファイルを publish する
$ php artisan vendor:publish --provider="LdapRecord\Laravel\LdapServiceProvider"
  • ldap.php が publish されていることを確認
$ ls -1 config/ldap.php
config/ldap.php
  • artisan コマンドを使って認証用データベースマイグレーションファイルを publish する
$ php artisan vendor:publish --provider="LdapRecord\Laravel\LdapAuthServiceProvider"
  • マイグレーションファイルが publish されていることを確認
$ ls -1 database/migrations/
2021_01_22_065947_add_ldap_columns_to_users_table.php
  • YYYY_MM_DD_HHMMSS は、実行タイミングによる

  • artisan コマンドで migrate する

$ php artisan migrate

各種設定

  • .env ファイルへ以下の設定を追加する
LDAP_LOGGING=true
LDAP_CONNECTION=default
LDAP_HOST=127.0.0.1
LDAP_USERNAME="cn=Manager,dc=example,dc=com"
LDAP_PASSWORD=${YOUR_REPOSITORY_MANAGER_PASSWORD}
LDAP_PORT=389
LDAP_BASE_DN="dc=example,dc=com"
LDAP_TIMEOUT=5
LDAP_SSL=false
LDAP_TLS=false
  • APP_ROOT/app/User.php へ trait と interface を追記する
use LdapRecord\Laravel\Auth\LdapAuthenticatable;
use LdapRecord\Laravel\Auth\AuthenticatesWithLdap;

class User extends Authenticatable implements LdapAuthenticatable
{
    use Notifiable, AuthenticatesWithLdap;
  • config/auth.php に LdapRecord 連携へ設定変更する
    'providers' => [
        'users' => [
            'driver' => 'ldap',
            'model' => LdapRecord\Models\OpenLDAP\User::class,
            'rules' => [],
            'database' => [
                'model' => App\User::class,
                'sync_passwords' => false,
                'sync_attributes' => [
                    'name' => 'cn',
                    'email' => 'mail',
                ],
            ],
        ],
    ],

Laravel Passport

データベースの用意する

sqlite を利用するので、データベースファイルを作成後に .env ファイル修正

$ touch database/database.sqlite
  • .env
DB_CONNECTION=sqlite

Composer を使ってインストール

$ composer require laravel/passport
  • artisan コマンドで migrate する
$ php artisan migrate

設定変更

修正後の全体ファイルを掲載する

  • APP_ROOT/app/User.php
<?php

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use LdapRecord\Laravel\Auth\LdapAuthenticatable;
use LdapRecord\Laravel\Auth\AuthenticatesWithLdap;
use Laravel\Passport\HasApiTokens;

class User extends Authenticatable implements LdapAuthenticatable
{
    use HasApiTokens, Notifiable, AuthenticatesWithLdap;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

  • APP_ROOT/app/Providers/AuthServiceProvider.php
<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
use Laravel\Passport\Passport;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        'App\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        Passport::routes();
    }
}

検証

トークン生成

artisan tinker を開き、Auth::attempt() メソッドを LDAP へ登録したユーザーとパスワードで実行の後、Auth::user(); を利用してインスタンス化

>>> Auth::attempt(['uid' => 'user', 'password' => '1qazxsw23edc'], true)
=> true
>>> $user = Auth::user();
=> App\User {#4311
     id: "1",
     name: "user",
     email: "user@example.com",
     email_verified_at: null,ACA
     created_at: "2021-01-22 07:36:07",
     updated_at: "2021-01-22 07:36:07",
     guid: "928a247a-e98e-103a-998b-b183875012cd",
     domain: "default",
   }

インスタンスから createToken('ANY_NAME')->accessToken; メソッドを呼び出しトークンを取得する

>>> $user->createToken('foobar')->accessToken;
=> "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiMmRmYTgzZWEwNTI1OTBjMjQyZTEyNzZmNGY2MWIxODJlOWQ3M2U4M2VkMDk3MjE4ZGE5YjA3MGY0NWY1YjY2NGE5MTIwYzQwNzMyY2UyZjEiLCJpYXQiOjE2MTE1NTM5MDAsIm5iZiI6MTYxMTU1MzkwMCwiZXhwIjoxNjQzMDg5OTAwLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.KP2JVn4KHAHKgmFi1w12mwY5bzYZnCAbJ0HcMOPwIORtFfHRuPqrr0BjlHQzkJGRvXNutRrRa3jQUqyFrv61G2RxNEfpv4aq8PWC7x8zduC6M94yBzNLqPTLPLZDMeMe_STseCjGsMcGmlRoA_Uf-jK17SRDLppDiQPTwFZ1N_w88DMCt_tmh25zHfum4vJaP_ZMIfEHvRJmOSkySLGAHuYwHtN7tiZ8pAQ6w_B8mTXZ3tmbfHPfk5Fo_hqCTTDauFKKgRWJowEW6_qEcO7xXPOmX5jVXI9j_CFl6Nqk2-INhUTk_lYP5-ughNTuftjacLNubk5GulG0Qf2D6AyyXyryieUNa1XVK35CEb_CumB1D-7I9LWfH9hzqK9DHU9BcD-06DCwB-6YO6i-QWMOudJMkuu9Yx3FFnyuvHHvEIaEPq0NA_gmDYcyQSUmyXu_Ii_jtWB-bZgNxneSo5va_bOCo8GEzCd-FAa4Lm6WmghRxJlo9O6l0oDEqaJ69IXUzjyaYSSQDuK5hkX4jJK17tCpJ2GUN0clsVF7G9KMiAR73O8EcjkqVn2BcpQK-0SromPw9NAgbBpaJuMi7Kv-kCRThh8BSHuMDwg_kNkwV2uaM4aIbaVx2ZwsvIYWPzU9G_TplpdBnNgwCpat7v1NRCDoQHBhTQ5dIPo13QtNaEQ"
  • トークンは環境ごとに異なる

トークンを使って認証後にデータを取得する

まず、アクセス用プログラムを実装する(GuzzleHttp を利用した)

  • access.php
<?php

require 'vendor/autoload.php';

$client = new GuzzleHttp\Client;

$accessToken = getenv('ACCESS_TOKEN');

$response = $client->request('GET', 'http://127.0.0.1:8000/api/user', [
    'headers' => [
        'Accept' => 'application/json',
        'Authorization' => 'Bearer ' . $accessToken,
    ],
]);

var_dump(json_decode((string) $response->getBody(), true));

環境変数 ACCESS_TOKEN へトークンを設定後に、自身のデータを取得できることを確認する

$ export ACCESS_TOKEN="eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiMmRmYTgzZWEwNTI1OTBjMjQyZTEyNzZmNGY2MWIxODJlOWQ3M2U4M2VkMDk3MjE4ZGE5YjA3MGY0NWY1YjY2NGE5MTIwYzQwNzMyY2UyZjEiLCJpYXQiOjE2MTE1NTM5MDAsIm5iZiI6MTYxMTU1MzkwMCwiZXhwIjoxNjQzMDg5OTAwLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.KP2JVn4KHAHKgmFi1w12mwY5bzYZnCAbJ0HcMOPwIORtFfHRuPqrr0BjlHQzkJGRvXNutRrRa3jQUqyFrv61G2RxNEfpv4aq8PWC7x8zduC6M94yBzNLqPTLPLZDMeMe_STseCjGsMcGmlRoA_Uf-jK17SRDLppDiQPTwFZ1N_w88DMCt_tmh25zHfum4vJaP_ZMIfEHvRJmOSkySLGAHuYwHtN7tiZ8pAQ6w_B8mTXZ3tmbfHPfk5Fo_hqCTTDauFKKgRWJowEW6_qEcO7xXPOmX5jVXI9j_CFl6Nqk2-INhUTk_lYP5-ughNTuftjacLNubk5GulG0Qf2D6AyyXyryieUNa1XVK35CEb_CumB1D-7I9LWfH9hzqK9DHU9BcD-06DCwB-6YO6i-QWMOudJMkuu9Yx3FFnyuvHHvEIaEPq0NA_gmDYcyQSUmyXu_Ii_jtWB-bZgNxneSo5va_bOCo8GEzCd-FAa4Lm6WmghRxJlo9O6l0oDEqaJ69IXUzjyaYSSQDuK5hkX4jJK17tCpJ2GUN0clsVF7G9KMiAR73O8EcjkqVn2BcpQK-0SromPw9NAgbBpaJuMi7Kv-kCRThh8BSHuMDwg_kNkwV2uaM4aIbaVx2ZwsvIYWPzU9G_TplpdBnNgwCpat7v1NRCDoQHBhTQ5dIPo13QtNaEQ"
$ php access.php 
array(8) {
  ["id"]=>
  int(1)
  ["name"]=>
  string(7) "user"
  ["email"]=>
  string(18) "user@example.com"
  ["email_verified_at"]=>
  NULL
  ["created_at"]=>
  string(27) "2021-01-22T07:36:07.000000Z"
  ["updated_at"]=>
  string(27) "2021-01-22T07:36:07.000000Z"
  ["guid"]=>
  string(36) "928a247a-e98e-103a-998b-b183875012cd"
  ["domain"]=>
  string(7) "default"
}

参考情報

OpenLDAP

Laravel

Laravel Passport

LdapRecord-Laravel

3
2
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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?