随感録 2017年8月

2017-08-11 (Fri)

シングルサインオン (SSO) のために, OpenID Connect について調べた。

OpenID Connect は, OAuth 2.0仕様の上に構築された認証 (authentication) 手順。認可 (authorization) とは異なる。

ただの OAuth 2.0 を「認証」に使おうとすると、セキュリティ上よくない。例えば, 単なる OAuth 2.0 を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができる | @_Nat Zone

各社が OAuth 2.0 の上に独自に認証の仕組みを作っていたのを標準化したものが OpenID Connect. 'OpenID 2.0' (OpenID Authentication 2.0) からの技術的な連続性はない。名前だけ。紛らわしい。

OpenID Connect に対応した認証サービス (OpenID Provider; OP) は、例えば、次がある;

組織 サービス
Google Google Federated Identity
Microsoft ADFS on Windows Server 2016
Microsoft Azure Active Directory
PayPal Login with PayPal
Salesforce Summer 2015 Release
Yahoo! Japan Yahoo! ID Federation v2

リストはこちら; OpenID Certification. Facebook がないのがつらい。引き続き独自の方法で対応が必要。

omniauth-openid-connect

Ruby on Rails での Googleへの対応では, omniauth-google-oauth2 を使っていたところ。今回, これを omniauth-openid-connect gem を使い、Google Identity Platform に OpenID Connect でログインできるようになった。まだ、プロフィールの更新は実現できていない。

'omniauth-openid-connect' gemパッケージは, 正直, あまり完成度が高くない。開発も2015年12月のが最新バージョンで, 最近の openid_connect パッケージに付いて行ってない。forkされまくりで, はて, 誰のforkがまともなのか? かなり困る。

一応, gemパッケージを使うとすると, オプションが特定の値でなければならないところが多い。罠か。

次のような設定ファイルで、何とかなる。

# -*- coding:utf-8; mode:ruby -*-

# omniauth を使う.
# See https://github.com/omniauth/omniauth

Rails.application.configure do |config|
  config.middleware.use OmniAuth::Builder do
    if !Rails.env.production?
      provider :developer 

      provider :openid_connect, {
        # /auth/<name> が認証開始のURLになる.       
        name: :nov_op_sample,

        # これがないと, エラー
        # 末尾の '/' は付けてはいけない.
        # <issuer>/.well-known/openid-configuration
        issuer: 'http://localhost:4000',
        
        # 'openid' で OpenID Connect
        scope: ["openid","profile","email","address","phone"],
        
        # 'code' is Basic flow. 'token id_token' is Implicit flow.
        response_type: :code,

        # 有効にしないと, public_key() 内で config.jwks が使われない.
        # 必ず true にすること。(デフォルトはfalse)
        discovery: true, 

        # rack-oauth2 パッケージ, Rack::OAuth2::Client.new() に渡される.
        # 初期値は, omniauth-openid-connect パッケージ内,
        # omniauth/strategies/openid_connect.rb
        client_options: {
          # omniauth-openid-connect パッケージ内,
          # omniauth/strategies/openid_connect.rb:issuer() 内で,
          # 上の issuer オプションは使わず, 次の3要素から issuer を作っている.
          scheme: "http",
          host: "localhost",
          port: 4000,  # 80番でも省略できない.

          # OP側と厳密に合わせる
          identifier: クライアントID,
          secret:     クライアントsecret,
          redirect_uri: "http://localhost:3000/auth/nov_op_sample/callback",
        },
      }
    end # !Rails.env.production?
    
    # google OpenID Connect
    provider :openid_connect, {
        name: :google_oauth2,
        issuer: 'https://accounts.google.com',
        scope: [:openid, :email, :profile],
        response_type: :code,
        discovery: true, 
        client_options: {
          scheme: "https",
          host: "accounts.google.com",
          port: 443,  # 省略不可.

          identifier: クライアントID,
          secret:     クライアントsecret,
          redirect_uri: "http://localhost:3000/auth/google_oauth2/callback",
        },
    }
  end 
end

# See http://qiita.com/nekogeruge_987/items/8bd307f9ae31f27c248a
#     http://stackoverflow.com/questions/10963286/callback-denied-with-omniauth
OmniAuth.config.on_failure = Proc.new do |env|
  OmniAuth::FailureEndpoint.new(env).redirect_to_failure
end

コメントに書いているが, discovery オプションは必ず true でなければならない。issuer と, client_options の (scheme, host, port) は必ず同じもの。

openid_connect_sample

テストのために, nov/openid_connect_sample を Ruby on Rails 4.2対応にしたものを用意した。horiq / openid_connect_sample · GitLab

'openid_connect' パッケージのバグ修正を含む。