アドオンへのシングルサインオン
最終更新日 2024年02月06日(火)
Table of Contents
クラウドサービスでは、リソースを管理するためにユーザーがログインする Web UI 管理パネルを提供することがあります。たとえば、Memcache クラウドサービスでは、Web ベースで使用状況を分析したりキャッシュをフラッシュしたりするためのダッシュボードを提供します。
このドキュメントの説明に従ってシングルサインオンを実装すれば、Heroku の顧客がダッシュボードにアクセスできるようになります。
この記事では、現行のアドオンパートナー API v3 環境で SSO を実装する方法について説明しており、この前提はほとんどの例に反映されています。従来の v1 統合の場合の相違点についても、必要に応じて言及しています。アドオンで使用している API のバージョンは、パートナーポータルで 「Settings」 (設定) -> 「Provisioning API」 (プロビジョニング API) から確認できます。
まとめ
Heroku では、ソルト (共有秘密鍵)、タイムスタンプ、およびリソース ID を組み合わせることによってシングルサインオントークンを生成します。ユーザーのブラウザは、このトークンを使用してサイトにリダイレクトされます。サイトでは、トークンの真正性を確認した後、ユーザーセッションを作成し、リソースの管理パネルにユーザーをルーティングすることができます。
SSO のテスト
SSO やその他のアドオン統合機能をテストするために、アドオンの <your slug>-staging
バージョンを作成することをお勧めします。この <your slug>-staging
アドオンは、統合インフラストラクチャのステージングバージョンを指すようにしてください。
ソルト、タイムスタンプ、トークン
アドオンマニフェストは、アドオンを記述する JSON ドキュメントです。
アドオン (および、結果としてそのマニフェスト) を作成すると、ランダムに生成された sso_salt がマニフェストのフィールドに含められます。
SSO リクエストには 2 つのトークンがあり、使用しているアドオンパートナー API のバージョンに応じて、どちらか 1 つを使用してユーザーをログインさせることができます。v3 トークンは resource_token
という名前です。従来の v1 トークンは token
という名前です。
v3 でのリソーストークンの作成
アドオンパートナー API の v3 では、resource_token は次の式を使用して作成されます。
resource_token = sha1(resource_uuid + ':' + salt + ':' + timestamp)
resource_uuid
は、プロビジョニングリクエストでリソースに対して指定する UUID です。salt
はアドオンマニフェストから取得されます。timestamp
は POST リクエストのパラメータに含められます。
たとえば、入力が次のようであるとします。
resource_uuid = 11111111-1111-1111-1111-111111111111
salt = 2f97bfa52ca102f8874716e2eb1d3b4920ad0be4
timestamp = 1267597772
…SSO resource_token は次のようになります。
SHA1("11111111-1111-1111-1111-111111111111:2f97bfa52ca102f8874716e2eb1d3b4920ad0be4:1267597772") =
4e9ce13ca328c6f3e2857b7de1724fd6c7c1c423
v1 でのリソーストークンの作成
アドオンパートナー API の v1 では、トークンは次の式を使用して作成されます。
token = sha1(provider_id + ':' + salt + ':' + timestamp)
provider_id
は、プロビジョニングリクエストへの応答中にクラウドサービスから作成して返した値です。salt
はアドオンマニフェストから取得されます。timestamp
は POST リクエストのパラメータに含められます。
たとえば、入力が次のようであるとします。
provider_id = 123
salt = 2f97bfa52ca102f8874716e2eb1d3b4920ad0be4
timestamp = 1267597772
…SSO トークンは次のようになります。
SHA1("123:2f97bfa52ca102f8874716e2eb1d3b4920ad0be4:1267597772") =
bb466eb1d6bc345d11072c3cd25c311f21be130d
リダイレクト時のユーザーのサインイン
アドオンメニューでアドオンをクリックしたユーザーは、マニフェストで定義された URL に HTTP POST 経由で転送されます。
リクエストは次のようになります。
POST <production/sso_url>
resource_id=<resource_id>&resource_token=<resource_token>&id=<id>&token=<token>×tamp=<timestamp>&nav-data=<nav data>&email=<user's email address>
示されているように、データは POST 本体でフォームエンコードされます。
sso_url
はアドオンマニフェストから取得されます。resource_id
は、プロビジョニング呼び出しでリソースに対して指定する UUID です。timestamp
は UNIX エポックタイムスタンプです。resource_token
は上記の式を使用して計算されます。resource_id
とresource_token
を使用するときは、id
とtoken
の値は無視してください。
安全に無視できる従来のフィールドは次のとおりです。
id
とtoken
– v1 ソルトトークンにのみ使用されます。アドオンパートナー API の v3 を使用している場合は無視してください。nav-data
には、Heroku のレイアウトで現在のアプリに適したビューを構築できるよう、現在のアプリ名やインストールされているアドオンなどの情報が含まれています。
計算した SHA1 ハッシュが、resource_token
(v1 を使用している場合は token
) で渡されたものと一致しない場合、ユーザーには HTTP ステータスコード 403 のページが表示されます。timestamp
が 5 分前より古い場合にも 403 が表示されます。
HTTP ステータスコード 403 は、ユーザーがこのページへのアクセスを許可されなかったことを示します。このコードを返した上で、(リクエストが正当なものと確信している場合はサポートに連絡するようユーザーに提案するなどの) わかりやすいメッセージを記した通常のページを表示することができます。
timestamp
が現在の時刻で SHA1 ハッシュが一致する場合、ユーザーは対象のリソースへのアクセスを許可されます。
通常使用する何らかの方法でユーザーセッションを作成する必要があり、場合によっては Cookie を設定する必要があります。Heroku の顧客の場合、通常のスタンドアロンサービス経由でログインするユーザーとは表示内容が多少異なります。そのため、セッションでは、これが Heroku のシングルサインオンであることを保存する必要もあります。ユーザーアクセスの状況は随時変化するため、この SSO データが信頼できるのはセッションが終了するまでの間だけです。セッションの有効期間を 90 分に制限することもお勧めします。
サイトのレイアウトにユーザー情報を表示する
SSO リクエストで送信され、サイトでユーザーに合ったコンテキストを表示するために役立つ、フォームエンコードされたパラメータは他にもあります。これらには以下のものが含まれます。
user
- 現在のユーザーのメールアドレスapp
- ユーザーの転送元であるスコープ内の Heroku アプリ名。
これらを使用して、テンプレートにユーザー情報を入力したり (場合によっては、ユーザーの Gravatar を取得したり)、コンテキスト内のアプリのダッシュボードにリンクしたりできます。そのためには、パラメータが &app=your-app-name
のように渡された場合、https://dashboard.heroku.com/apps/your-app-name
にリンクします。
サンプルコード
次に示すのは、Ruby/Sinatra で記述された v3 シングルサインオンエンドポイントのサンプル実装です。
post "/heroku/sso" do
pre_token = params[:resource_id] + ':' + HEROKU_SSO_SALT + ':' + params[:timestamp]
token = Digest::SHA1.hexdigest(pre_token).to_s
halt 403 if token != params[:resource_token]
halt 403 if params[:timestamp].to_i < (Time.now - 2*60).to_i
account = Account.find(params[:resource_id])
halt 404 unless account
session[:user] = account.id
session[:heroku_sso] = true
redirect "/dashboard"
end
関連性のないページ要素の削除
サイトでシングルサインオンリクエストを受理したら、最後の手順として、Heroku の顧客の SSO セッションに関連しないページ要素を非表示化または無効化します。次のような例があります。
- パスワードの変更
- アカウント名の変更
- 請求情報の更新
- ログアウト
これらの項目はすべて、Heroku の顧客が Heroku Dashboard および CLI から管理します。これらの情報は Heroku のインフラストラクチャにあり、アドオンからは更新できません。