💎

Rubyで理解するOpenID Connect

2023/02/27に公開

はじめに

外部サービスと連携する際に、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の実装方法についても解説できればと思います。

脚注
  1. OpenID Connect Core 1.0
    cf. http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html ↩︎

  2. OpenID Connect Core 1.0 / 3. Authentication
    cf. http://openid-foundation-japan.github.io/openid-connect-core-1_0.ja.html#Authentication ↩︎

  3. 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 ↩︎

  4. 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 ↩︎

  5. 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