GridDBでAuthlibを使用する

はじめに

このブログでは、authlibをGridDBにインストールして使用する手順を紹介します。Authlibは、OAuth(とその他)をサーバーやアプリケーションにインストールするためのPythonライブラリです。

Authlibとは

Open Authorization (OAuth)は、アプリケーションの安全性を確保するためのトークンシステムの使用を前提としたオープンスタンダードです。一般的な流れとしては、ユーザーがユーザー名とパスワードを使ってログインすると、アプリケーションはX時間後に失効する特別なトークンを提供します。このトークンは、ユーザーがアプリケーションにアクセスするたびにチェックされます。トークンの有効期限が切れると、ユーザーは再度ログインして、新しいトークンを発行してもらう必要があります。

通常、このような実装はSQLベースのデータベースで行われますが、状況によっては、1つのアプリケーションのために複数の異なるデータベースを使い分けなければなりません。

Authlibの使用に向く場合

一般的に、アプリケーションの安全性を何らかの形でを確保する必要がある場合に、OAuthを使用すると良いでしょう。OAuthは、柔軟性と有効性の面で膨大な実績を持つオープンスタンダードであるため、このような場合の使用に向いています。また、HTTPで動作するため、さまざまなアプリケーションで利用でき、多くの開発者に理解されています。厳密に言えば、新しいアプリケーションでは、OAuth (ひいてはAuthlib)の使用に向かない場合があります。

AuthlibでGridDBを使用する

すでに述べたように、開発者や組織がアプリケーションのユーザと認証をGridDBで処理する利点として、使用するデータベースの数を減らすことができます。GridDBに多くの 利点を感じてすでにアプリケーションでGridDBを使用している場合は、認証のための機能を追加することでさらなる利点が得られるでしょう。

実装方法

AuthlibをGridDBに実装するには、Sample OAuth Serverに沿って、いくつかの重要な変更を加えて動作させることができます。ほとんどの変更の目的は、SQLベースのデータベースの使用から、GridDBが必要とする特定のコードに変更することです。

まずは、 (griddb.py)

import griddb_python
griddb = griddb_python
factory = griddb.StoreFactory.get_instance()

gridstore = factory.get_store(
    host="239.0.0.1",
    port=31999,
    cluster_name="defaultCluster",
    username="admin",
    password="admin"
)

oauth.py

oauth.pyファイルは、パスワードとトークンを扱い、データベースとの照合を行います。

まず、GridDBの認証情報をインポートします。

from .griddb import griddb, gridstore 

そして、OAuth機能が動作することを確認します。まず、ユーザーとそのパスワードをDB内で確認し、認証情報が一致していることを確認します。GridDBでこれを行うには、クエリを実行して、保存されたユーザーをチェックします。

        try:

            con = gridstore.get_container("USERS")
            q = con.query("select * where username = '"+username+"'")
            rs = q.fetch(False)
            if rs.has_next():
                u = rs.next()
        except:
            print("Exception finding user")
            abort(401, "UNAUTHORIZED")
        if u:
            user = User(u)
            if user.check_password(password):
                return user
            print("pass doesnt match")

        abort(401, "UNAUTHORIZED")

次に、OAuthで発行されたトークンをGridDBに保存できるようにします。

保存のプロセスを開始するにはまず、TOKENSという名前のcollection containerのスキーマを作成します。このコンテナには、トークン文字列、期限の時間、発行された時間、トークンに関連付けられたユーザーIDの4つの保存行が必要です。GridDBのPythonコンテナを使うと、以下のようにコンテナのスキーマを変数として設定することができます。

def save_token(token, request):

    conInfo = griddb.ContainerInfo("TOKENS",
        [["token", griddb.Type.STRING],
        ["expires_at", griddb.Type.TIMESTAMP],
        ["issued_at", griddb.Type.TIMESTAMP],
        ["userid", griddb.Type.STRING]],
        griddb.ContainerType.COLLECTION, True)
    cn = gridstore.put_container(conInfo)

    cn.set_auto_commit(False)
    cn.create_index("token")
    cn.put([token['access_token'], datetime.datetime.utcfromtimestamp(time.time()+token['expires_in']), datetime.datetime.utcnow(), request.user.username])
    cn.commit()

サーバーは渡されたトークンの有効性をチェックする必要があります。そのために、BearerTokenValidatorクラスを使って、再びGridDBサーバに問い合わせを行います。

class MyBearerTokenValidator(BearerTokenValidator):
    def authenticate_token(self, token_string):
        try:
            cn = griddb.get_container("TOKENS")
            q = con.query("select * where token = '"+token_string+"'")
            rs = q.fetch(False)
            if rs.has_next():
                return OAuth2Token(rs.next())
            else:
                return None
        except:
            return None
        return None

渡されたトークンがGridDBのTOKENSコンテナに存在していれば、アプリケーションはそのトークンを有効なものとして受け入れ、そうでなければ無効なものとして返されます。

ユーザーがログインするたびに、これらの関数が実行され、まずパスワードが一致するかどうかを確認し、トークンが発行されると、ユーザーがセキュリティ保護されたコンテンツにアクセスしようとした場合、トークンが有効であるかどうかも確認します。

routes.py

routes.pyファイルは、サーバのエンドポイントやAPIコールを処理します。今回の例では、ホームエンドポイント(”/”)、サインアップ用エンドポイント(”/sign_up”)、トークンの動作確認用エンドポイント(”/private”)の3つのエンドポイントに注目してみましょう。

まず、/sign_upエンドポイントを見てみましょう。これを自分でテストするには、リポジトリをクローンして、APIサーバーflask run --host=0.0.0.0を実行します。実行したら、サインアップしてみましょう。このプロセスでは、GridDBサーバ上にUSERSコレクションコンテナが作成され、POSTしたユーザの認証情報が保存されます。

では、APIサーバーが起動したら、このcurlコマンドを試してみましょう。

curl --request POST \
--url 'http://52.250.124.168:5000/sign_up' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=password' \
--data 'scope=all' \
--data 'username=israel' \
--data 'password=imru'

今回の例では、新しいユーザー名israel、パスワードimruで作成します。

コマンドが成功したかどうかは、サーバーのログによって明らかになります。ユーザーのサインアップと登録が完了したら、ログインしてみましょう。そのためには、/エンドポイントと認証情報を使って、ユーザーがデータベースに存在するかどうかをテストします。

curl --location --request POST 'http://52.250.124.168:5000/' \
--form 'username="israel"' \
--form 'password="imru"' \
--form 'grant_type="password"' \
--form 'scope="all"'

ここまでの手順全てが成功すると、トークンの文字列が返ってくるはずです。これが、OAuthで発行された/privateエンドポイントにアクセスするためのトークンです。まずは、トークンなしでエンドポイントにアクセスしてみましょう。トークンが無効であったり、DBに見つからなかったりすると、401というサーバーエラーコードが返ってきます。

{
    "error": "invalid_token",
    "error_description": "The access token provided is expired, revoked, malformed, or invalid for other reasons."
}

そして、システム全体が期待通りに動作しているかどうかを確認するために、トークンを認証ヘッダーとして添付した状態で、次のように再度リクエストを行ってみましょう。

curl --location --request GET 'http://52.250.124.168:5000/private' \
--header 'Authorization: Bearer ZcoI1Rxgcw8rx8z0uFjCArdqacjM8lyE0K6fNWf2zz'

成功すると、APIサーバーは次のようなメッセージで応答します。This is a private page only viewable if you're logged in これで意図した通りに動作していることが分かります。

大まかな流れを図で表すと以下のようになります。

まとめ

バックエンドのAPIサーバーを作成する際には、アプリケーションのセキュリティを確保するためにAuthlibを使用することがよくあります。アプリケーションのデータ保存にGridDBがすでに使用されている場合、リソースをプールして、同じデータベースを使って認証も処理することができます。このブログに沿って設定して使ってみてください。

このブログで使用したコードについては、GridDBnet GitHub リポジトリを参照してください。

ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb

Leave a Reply

Your email address will not be published. Required fields are marked *