【st.login】GoogleアカウントでログインできるStreamlitアプリの開発方法と仕組みをわかりやすく解説
前置き
こんにちは!データエンジニアの山口歩夢です。
Streamlitにはたくさんの便利な関数が用意されています。
簡単にアプリケーションの開発ができ、OpenID Connect(OIDC)を活用したユーザー認証でさえも、とても簡単に実装することができます。
これにより、皆さんがよく見るGoogleアカウントなどを使用したユーザー認証を簡単に実装できます。
本記事では、その仕組みから設定方法、実装例などをわかりやすく紹介します。
OpenID Connectとは?
まず、Streamlitst.login()
がどのような仕組みで動いているのかを理解するために、OpenID Connect(以下OIDC)について簡単に触れておきます。
OIDC(OpenID Connect)は、OAuth 2.0を拡張してユーザー認証を行うためのプロトコルです。OIDCを使うことで、GoogleやMicrosoftなどのIDプロバイダーが提供するアカウントを利用して、他のアプリケーションに安全にログインできるようになります。
ユーザーがIDプロバイダーにログインすると、その認証結果をIDトークンとともに安全にアプリケーションへ送信されます。アプリケーション側では、このIDトークンをもとにユーザーを識別し、ログイン処理を行うことが可能になります。
本記事では、GoogleのIDプロバイダーを使ってログイン認証機能を実装するので、
上記をざっくり言うと、次のようなことが実現できるイメージです。
Googleアカウントを使って安全にログインできるStreamlitアプリが作れるようになります。
また、Streamlitアプリ側でユーザー登録やパスワード管理が不要になります。
この仕組みを図にすると、以下のようなイメージです。
さらに、OIDCはSSO(Single Sign-On)を実現するためにもよく利用されます。一度ログインすれば、他の対応アプリにもシームレスにアクセスできるようになります。
OIDCの仕組みは非常に奥が深く、学んでみると難しく感じますが、以下の書籍を読むとイメージを掴むことができました。
Streamlitでの認証関連関数の紹介
OIDCについてイメージがついたので、
Streamlitが提供する認証関連の関数について見ていきましょう。
Streamlitは以下の3つの認証関連の関数を提供しています。
st.login()
st.logout()
st.experimental_user
これらの機能を一つひとつ解説していきます。
st.login()
st.login()
は、ユーザーをOIDCプロバイダーにリダイレクトして認証を行い、認証完了後にアプリへ戻す機能を提供します。
つまり、ユーザー認証を行ってくれるということで、冒頭の図の①~⑥は、主にst.login
が担っています。
st.login()
を機能させるためには、st.secrets.toml
に色々と設定を行う必要があります。そちらは、後ほど解説させていただきます。
st.experimental_user
ログイン中のユーザー情報にアクセスできる読み取り専用オブジェクトです。
st.login()
関数を使ってユーザー認証が完了したら、ユーザー情報がCookieに保存され、st.experimental_user
からアクセスできるようになります。
Streamlitが冒頭の図の③で返されたIDトークンを解析し、その情報をst.experimental_user
に格納するイメージです。
st.experimental_user
には、ユーザー情報が辞書型で格納されます。
例えば、GoogleのIDプロバイダーを利用した場合、以下のような情報が取得できます。
{
"is_logged_in": true,
"iss": "https://accounts.google.com",
"azp": "{client_id}.apps.googleusercontent.com",
"aud": "{client_id}.apps.googleusercontent.com",
"sub": "{unique_user_id}",
"email": "{user}@gmail.com",
"email_verified": true,
"at_hash": "{access_token_hash}",
"nonce": "{nonce_string}",
"name": "{full_name}",
"picture": "https://lh3.googleusercontent.com/a/{content_path}",
"given_name": "{given_name}",
"family_name": "{family_name}",
"iat": {issued_time},
"exp": {expiration_time}
}
st.logout()
st.logout()
はユーザーをログアウトさせるための関数です。
呼び出すと、Cookie、st.experimental_user
からユーザー情報を削除し、アプリにログイン前の画面を表示します。
secrets.toml
の設定
st.login()
を使ってユーザー認証機能を実装するには、secrets.toml
の設定が必須です。
以下のような設定をする必要があります。
共通設定
すべてのプロバイダーに共通して必要な設定は以下の通りです。
secrets.toml
の [auth]
セクションに、次の設定を記述します。
-
redirect_uri
: アプリケーションのURL +/oauth2callback
-
cookie_secret
: ランダムかつ強力な文字列(セッション保護用)
プロバイダーごとの設定
上記の共通設定に加えて、プロバイダーごとに以下の情報を設定します。
-
client_id
: プロバイダーから取得 -
client_secret
: プロバイダーから取得 -
server_metadata_url
:
プロバイダー毎に設定する値が異なるため、公式ドキュメントを確認しましょう。
例えば、Googleであれば、"https://accounts.google.com/.well-known/openid-configuration"
を設定します。
その他(OIDCの細かい設定)
基本的な設定はStreamlitが自動で行ってくれますが、
上記に加えて、OIDCの細かい設定を行うことも可能です。
OIDCはログイン体験を制御するための多くのパラメータ(例:scope
、prompt
、response_type
など)を設定できます。
これらを、secrets.toml
のclient_kwargs
パラメータに記入することで設定できます。
設定できるパラメータなどは、以下を参照してください。
ちなみに先ほど申した通り、基本的な設定はStreamlitが自動で行ってくれています。
公式ドキュメントによると、デフォルトでscope
にopenid profile email
、prompt
にはselect_account
が設定されています。
secrets.toml
に書き起こすと、以下のようなイメージです。
[auth]
redirect_uri = "http://localhost:8501/oauth2callback"
cookie_secret = "xxx"
client_id = "xxx"
client_secret = "xxx"
server_metadata_url = "xxx"
client_kwargs = {
scope = "openid profile email", # ユーザーの基本情報を取得
prompt` = "select_account" # 複数アカウントからの選択を促す
}
scope
は取得したい情報の種類を指定しており、prompt="select_account"
では、ユーザーにアカウント選択を促す画面を表示するという設定がされています。
また、state
やnonce
といったセキュリティ対策用のパラメータは自動的に処理されるため、指定する必要がありません。secrets.toml
で認証を設定すると、StreamlitはCORSとXSRF保護を自動的に有効化します。
こんなところでもStreamlitは開発者が開発しやすいように、セキュリティ面での設定まで自動で行ってくれており、非常に便利だなと思いました。
実装手順
Streamlitが提供している機能や必要な設定が分かってきたので、
Google Identity(IDプロバイダー)とStreamlitを使用した、ログイン機能の実装を行っていきます。
必要なディレクトリやライブラリのセットアップ
必要なディレクトリやライブラリを準備していきます。
まずは、以下のような構成でディレクトリを用意します。
project/
├── requirements.txt
├── .streamlit
| └── secrets.toml
└── streamlit_app.py
そして、以下のようにrequirements.txt
には必要なライブラリを書き、
Streamlit>=1.42.0
Authlib>=1.3.2
以下のように$ pip install
コマンドなどでインストールしておきましょう。
$ pip install -r requirements.txt
secrets.toml
のセットアップ
次に.streamlit/secrets.toml
を設定していきます。
本記事では、ローカル開発環境でGoogle Identityを使ったユーザー認証を実装するための設定を行います。
client_id
とclient_secret
の取得
まずは、client_id
とclient_secret
をGoogle Cloud Consoleから取得します。
以下のドキュメントに従って取得しています。
最初に「API とサービス」の「認証情報」ページに遷移し、「同意画面を構成」というボタンをクリックします。
表示される画面で「開始」をクリックします。
設定画面に遷移し、アプリケーション名やメールアドレスの登録を行います。
次にどのユーザーがログインできるかを設定をします。
「内部」は組織内のユーザー、「外部」は「テストユーザー」に設定されたユーザーのみログイン可能となります。
最後に、プロジェクトの変更通知先メールアドレスの入力が求められるので入力します。
以上で、プロジェクト構成の用意ができたので、今回ログイン可能にする「テストユーザー」の登録をしていきます。「APIとサービス」ページに戻り、「OAuth同意画面」をクリックします。
「対象」をクリックし、「+ Add users」ボタンを押して、Streamlitアプリでログイン可能にするユーザーのメールアドレスを入力し、保存します。(今回は自分のGmailアドレスを登録しました。)
その後、再度「API とサービス」ページに戻り、画面上部の「+ 認証情報を作成」をクリックします。
「OAuthクライアントID」を選択し、client_id
を作成します。
最後に以下の画像のように設定を行います。アプリケーションの種類は「ウェブ アプリケーション」を選択し、「名前」は管理しやすい名称を入力します。「承認済みのリダイレクト URL」には OIDC が認証後にリダイレクトする URL を設定します。
今回はローカル環境で検証を行うので、http://localhost:8501/oauth2callback
を設定します。
設定するURLは、アプリをホストしている場所によって異なります。アプリのURLを適切に設定する必要があります。
「作成」をクリックすると、client_id
とclient_secret
が表示されます。
これらのクライアントIDの情報をsecrets.toml
に設定するので、メモなどに控えておきます。
cookie_secret
の取得
次にcookie_secret
を取得します。
こちらは、Cookieを暗号化して署名するために使用される文字列です。
以下のコマンドを実行することで、ランダムな文字列を取得します。
python -c 'import os,base64; print(base64.urlsafe_b64encode(os.urandom(32)).decode())'
secrets.toml
を設定
これで、取得する必要がある情報を取得し終わったので、secrets.toml
に設定をします。
以下のように記述します。
[auth]
redirect_uri = "http://localhost:8501/oauth2callback"
cookie_secret = "[ランダムに生成した文字列]"
client_id = "[Googleで取得したクライアントID]"
client_secret = "[Googleで取得したクライアントシークレット]"
server_metadata_url = "https://accounts.google.com/.well-known/openid-configuration"
redirect_uri
とserver_metadata_url
に設定する値は以下のように決まっています。
redirect_uri
は、ユーザーがGoogleアカウントで認証が終わった後にリダイレクトされるURLです。今回はローカル環境でユーザー認証を行うため、http://localhost:8501/oauth2callback
を設定しています。
そして、server_metadata_url
はGoogleのOpenID Connect (OIDC) 設定のURLです。
GoogleのIDプロバイダーを仕様する場合は、https://accounts.google.com/.well-known/openid-configuration
を設定します。
こちらは、OIDCプロバイダーの詳細(認可、トークン、取り消し、ユーザー情報、公開鍵エンドポイントのURIなど)を提供するキーと値のペアが含まれています。ブラウザで実行してみるとどんな値が入っているかが確認できます。
streamlit_app.py
にコードを書く
secrets.toml
の設定が完了したので、
streamlit_app.py
ファイルにコードを書きます。
以下のコードを書くだけでログイン機能が完成です。
import streamlit as st
# ログイン状態の確認
if not st.experimental_user.is_logged_in:
st.title("ログイン画面")
if st.button("Googleアカウントでログイン", icon=":material/login:"):
st.login()
st.stop()
# ログアウトボタン
if st.button("ログアウトする", icon=":material/logout:"):
st.logout() # ログアウト処理
# ユーザー情報の表示
st.markdown(f"ようこそ! {st.experimental_user.name}さん!")
以下のコマンドを実行して、実際にアプリがどのような動きをするのか見てみます。
$ streamlit run streamlit_app.py
まずは、ブラウザで以下のような画面が表示されます。
「Googleアカウントでログイン」ボタンをクリックしてみると、
Googleにログインするための画面に遷移します。
先ほど「テストユーザー」に登録たアカウントを選択すると、
「st_login
にログイン」という画面に遷移します。画像のst_loginにログインには、先ほどOAuthクライアントIDの作成時に設定したアプリケーションの名称が表示されています。
「次へ」ボタンをクリックしてみましょう。
すると、無事ログイン後の画面が表示されるようになりました!
まとめ
Streamlitが提供する関数を活用することでGoogleアカウントを使ったユーザー認証機能を簡単に実装することができることが分かりました。
Google以外にも、Microsoft、Oktaなどの認証基盤とも連携できるため、非常に便利です。
自分的にはstate
やnonce
といったセキュリティ対策用のパラメータもStreamlitが自動で設定してくれているというところが特に印象的でした。
Streamlitには、まだまだ便利な機能がたくさんあります。
詳しくは、私が執筆した『Streamlit入門 Pythonで学ぶデータ可視化&アプリ開発ガイド』にて解説していますので、ぜひご覧ください!
Discussion