Rubyで理解するOpenID Connect
はじめに
外部サービスと連携する際に、OpenID Connect に準拠したクライアントを実装をすることがあります。本記事では、Rubyでのコード例に触れながら、OpenID Connectについて理解を深めることを目的とします。
OpenID Connect
OpenID Connect Core 1.0 incorporating errata set 1 に記載の通り、OpenID Connect は、OAuth2.0をベースとした拡張プロトコルです。認証・認可方法の約束に加えて、ユーザプロフィール取得API(REST形式)についても定義されています。
OpenID Connect 1.0 は, OAuth 2.0 [RFC6749] プロトコルの上にシンプルなアイデンティティレイヤーを付与したものである. このプロトコルは Client が Authorization Server の認証結果に基づいて End-User のアイデンティティを検証可能にする. また同時に End-User の必要最低限のプロフィール情報を, 相互運用可能かつ RESTful な形で取得することも可能にする.[1]
OpenID Connectでは、認証機能を提供する側を「OpenID Provider(OP)」、認証機能を利用する側を「Relying Party(RP)」と呼んでいます。
OpenID Connectでは、OP、RP間の認証方法に以下の3種類が存在します。[2]
- Authorization Code Flow
- Implicit Flow
- Hybrid Flow
フロー毎の特徴についての解説は、Yahoo!社のサイトで分かりやすく解説されているため、そちらのサイト「Yahoo! ID連携 v2」に譲ります。
本記事では、Relying Party側の視点からOpenID Connectを理解することを目的としているため、Authorization Code Flowについて解説していきます。
Authorization Code Flow Steps
以下では、下記前提条件のもと説明を行います。
・ ユーザは、Provider側のサイトにアカウント情報を持っている
・ User-Agentには、Providerとのセッション/Cookie情報が残っていない
・ ユーザはClientへのスコープ認可済
Authorization Code Flow のシーケンス例を基に、処理フローを追っていきます。
処理毎にRubyでの実装例も記載しますので、参考になれば幸いです。
Step1. Authentication Request(1〜10)
はじめに、ユーザの認証コードを取得します。
ユーザがリンククリック等でAuthentication Request[3]を行うと、ユーザはProviderのログイン画面にリダイレクトされます。ユーザがログインに成功すると、ユーザはAuthentication Requestのクエリパラメータに設定したリダイレクト先にリダイレクト[4]されます。
# これ以降のコード例では、Railsとoauth2 gemを使って解説していきます。
# cf. https://github.com/oauth-xx/oauth2
# クライアントインスタンスを作成
client = OAuth2::Client.new(
ENV['CLIENT_ID'],
ENV['CLIENT_SECRET'],
:site => 'https://server.example.com', # PorviderのURI
:authorize_url => '/authorize',
:token_url => '/token'
)
# Authentication Request用のエンドポイント生成(ユーザにはこのURIにアクセスしてもらいます)
url = client.auth_code.authorize_url(
:redirect_uri => 'ログイン成功後のリダイレクト先URL',
:state => 'CSRF対策用のトークン'
)
このとき、リダイレクトURL(⑩)のクエリパラメータ(code)に認証コードが埋め込まれていますので、RP側のサーバで認証コードを取得します。
def oidc_callback
code = params[:code] # 認証コード
state = params[:state] # CSRF対策用トークン
# ...後続処理(CSRF対策用トークンのチェックなど)
# Step2, Step3の処理
end
Step2. Token Request(11〜12)
次に、ユーザ情報を取得するために必要なアクセストークンを取得します。
Step1で取得した認証コードを使って、Token Request[5]を行うと、アクセストークンを取得することができます。
def oidc_callback
code = params[:code] # 認証コード
state = params[:state] # CSRF対策用トークン
# Step2の処理
client = OAuth2::Client.new(
ENV['CLIENT_ID'],
ENV['CLIENT_SECRET'],
:site => 'https://server.example.com', # PorviderのURI
:authorize_url => '/authorize',
:token_url => '/token'
)
response = client.auth_code.get_token(
'認証コード',
:redirect_uri => 'リダイレクト先URL',
:headers => {'Authorization' => "Basic #{ENV['BASIC_TOKEN']}"}
)
# アクセストークン
access_token = response[:access_token]
# Step3の処理
end
Step3. UserInfo Request(13〜14)
最後に、ユーザ情報を取得します。
Step2で取得したアクセストークンを使って、下記のようにUserInfo Requestを行うと、ユーザ情報を取得することができます。このとき、Authorizationヘッダーには、Bearer Token としてアクセストークンをセットします。
GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer {アクセストークン}
おわりに
今回は、OpenID ConnectをRPの視点から眺めてみました。
次回以降の記事では、Providerの実装方法についても解説できればと思います。
-
OpenID Connect Core 1.0
cf. http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html ↩︎ -
OpenID Connect Core 1.0 / 3. Authentication
cf. http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#Authentication ↩︎ -
OpenID Connect Core 1.0 / 3.1.2.1. Authentication Request
cf. http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#AuthRequest ↩︎ -
OpenID Connect Core 1.0 / 3.1.2.5. Successful Authentication Response
cf. http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#AuthResponse ↩︎ -
OpenID Connect Core 1.0 / 3.1.3.1. Token Request
http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#TokenRequest ↩︎
Discussion