デラーズ・ブートキャンプ

ギークになりたい平凡プログラマの日記

Slim3でOAuth2サーバーを構築する (認証サーバー編)

お仕事でOAuth2のサーバーを作る事があったので備忘録です。Slim3league/oauth2-server を利用して作成しました。認証方式は ClientCredentialsGrant で作成しています。

環境構築

まずはSlim3のスケルトンでプロジェクトを作成。その後composerで必要なライブラリをポンポン入れていきます。ちなみにViewは使わないのでremoveしています。dependencies.phpPHP-Viewを設定している箇所があるのでその辺も取り除くこと。

composer create-project slim/slim-skeleton php-oauth2-server
composer remove slim/php-view
composer require league/oauth2-server

次にsrc配下をautoloadさせる設定を composer.json に追記して、composer dumpautoload を実行する。これでsrc配下が namespace 配下のクラスがnamespaceで使用できるようになります。

"autoload": {
    "psr-4": {
        "App\\": "src/"
    }
},

OAuth2ライブラリのホームページを参考にprivatekeyとpublickeyを作成します。

openssl genrsa -out private.key 1024
openssl rsa -in private.key -pubout -out public.key

データを保存するテーブルの作成。

CREATE TABLE `clients` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `client_id` varchar(255) NOT NULL DEFAULT '',
  `secret` varchar(255) NOT NULL DEFAULT '',
  `scope` text,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE `scopes` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `scope` varchar(255) NOT NULL DEFAULT '',
  `discription` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

実装

OAuth2プロジェクトのExamplesを参考にして実装していきます。githubのコードをそのまま使用すると、データが直書きされていたりするのでその辺りを書き換えたりとかします。

# Modelやら$this->dbとか使っていますが、この辺の説明は省略します。

# AccessTokenRepository.php
    public function getNewToken(ClientEntityInterface $clientEntity, array $scopes, $userIdentifier = null)
    {
        $accessToken = new AccessTokenEntity();
        $accessToken->setClient($clientEntity);
        foreach ($scopes as $scope) {
            $accessToken->addScope($scope);
        }
        $accessToken->setUserIdentifier($userIdentifier);
        return $accessToken;
    }

# ClientRepository.php
    public function getClientEntity($clientIdentifier, $grantType, $clientSecret = null, $mustValidateSecret = true)
    {
        # clientsからユーザが存在するか検索
        $model = new ClientModel($this->db);
        $clientArray = $model->findByClientId($clientIdentifier);
        if(empty($clientArray)){
            return;
        }

        if(password_verify($clientSecret, $clientArray['secret']) === false){
            return;
        }

        # client情報をreturn
        $client = new ClientEntity();
        $client->setIdentifier($clientIdentifier);
        $client->setScope($clientArray['scope']);

        return $client;
    }

# ScopeRepository.php
    public function getScopeEntityByIdentifier($scopeIdentifier)
    {
        $model = new ScopeModel($this->db);
        $scopeArray = $model->findByScope($scopeIdentifier);
        if(empty($scopeArray)){
            return;
        }
        
        $scope = new ScopeEntity();
        $scope->setIdentifier($scopeIdentifier);
        return $scope;
    }

    public function finalizeScopes(
        array $scopes,
        $grantType,
        ClientEntityInterface $clientEntity,
        $userIdentifier = null
    ) {
        // scopeが未指定の場合は client の scope をすべて設定
        // scopeが指定されている場合は client の scope に含まれている引数の socpe を追加して返却
        $clientScopes = $clientEntity->getScopes();
        $ret = [];
        if(count($scopes) === 0) {
            foreach($clientScopes as $s){
                $scope = new ScopeEntity();
                $scope->setIdentifier($s);
                $ret[] = $scope;
            }
        } else {
            foreach($scopes as $s){
                $paramScope = $s->getIdentifier();
                if(!in_array($s->getIdentifier(), $clientScopes)){
                    throw OAuthServerException::invalidScope($paramScope);
                }
                $scope = new ScopeEntity(); 
                $scope->setIdentifier($paramScope);
                $ret[] = $scope;
            }
        }
        return $ret;
    }

テスト

Postmanとかでテスト送信をしてみてトークンが返って来ればOKです。 f:id:Derabon:20170627125832p:plain

今回できたもの

https://github.com/dera0520/php-oauth2-server

参考

oauth2.thephpleague.com
github.com/thephpleague/oauth2-server
Slim 3 Framework でMySQLとテンプレつかってみる。その2