3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

dexidp/dexが発行したJWTをRubyで検証する

Last updated at Posted at 2019-12-10

はじめに

OpenID Connect Providerが発行するID TokenをRubyのJWTライブラリを利用して検証してみました。よくあるサンプルコードは自前のRSA鍵を利用して検証していたりするので、実際にdexidp/dexが発行したID TokenのJWTを検証するコードをRuby+JWTライブラリで実行してみました。

テストはDexを利用していますが(/.well-known/openid-configurationで情報を提供する)一般的なOpenID Connect Providerであれば同様に動くはずです。

環境

  • Ubuntu 24.04 LTS 64bit版
  • ruby (3.3.5)
  • ruby-bundler (2.5.16)
  • dexidp/dep (server, https://example.com/dex) + example-app (client)

JWTはDex付属のexample-appを利用して入手しています。Ubuntuパッケージのruby-jwtはバージョンが古く、サンプルがそのままでは実行できませんでしたので、bundlerを利用しています。

References

ライブラリの準備

作業用のディレクトリにGemfileを作成して、最新のJWTライブラリを利用しています。

Gemfile
source 'https://rubygems.org'

gem 'json'
gem 'jwt'
gem 'httpclient'
gem 'openssl'
gem 'mutex_m'

Gemfileのある作業用ディレクトリで、次のコマンドを実行し、./lib/ruby以下にライブラリファイルをダウンロードします。

$ bundle config set path lib
$ bundle install
Fetching gem metadata from https://rubygems.org/...
Resolving dependencies...
Fetching httpclient 2.8.3
Fetching base64 0.2.0
Installing base64 0.2.0
Fetching json 2.9.1
Installing httpclient 2.8.3
Installing json 2.9.1 with native extensions
Fetching mutex_m 0.3.0
Installing mutex_m 0.3.0
Fetching openssl 3.3.0
Installing openssl 3.3.0 with native extensions
Fetching jwt 2.10.1
Installing jwt 2.10.1
Bundle complete! 5 Gemfile dependencies, 7 gems now installed.
Bundled gems are installed into `./lib`

Dexが発行したID Tokenの準備

"idtoken.txt"ファイルにID Tokenの内容をコピーしておきます。ピリオドで3つのBase64エンコードされた情報が連結されていますが、Webブラウザに出力されたID Token(JWT)をそのままコピーしています。

idtoken.txt
eyJhbGc ... wOTMifQ.eyJpc3MiO ... EFiZSJ9.r_vHqAr3 ... hjvzhzf9w

ruby-jwtのサンプルコードを試す

次のようなファイルを準備し、bundle execコマンドで実行します。JWKS_URI変数とJWT_TOKEN_FILE変数の内容は適切なURL,ファイル名に変更してください。

verify-idtoken.rb
#!/usr/bin/ruby

require 'bundler/setup'
Bundler.require

## prepare JWT string
JWT_TOKEN_FILE = "./idtoken.txt"
jwt_text = open(JWT_TOKEN_FILE).read().strip() ## strip() is essential to avoid future verification error.

puts "--- unverified output ---"
claim,algo = JWT.decode(jwt_text, nil, false)
puts claim,algo ## the algo object will be used later.


## find jwks_uri from the unverified claim
oidc_config = JSON.parse(HTTPClient.new.get(claim["iss"] + "/.well-known/openid-configuration").body, symbolize_names: true)
JWKS_URI = oidc_config[:jwks_uri]

puts "--- verified output ---"
## from: https://github.com/jwt/ruby-jwt/tree/v2.2.1#json-web-key-jwk
jwk_loader = ->(options) do
  ## from: https://qiita.com/zakuroishikuro/items/5b08d65d4fef79982f19
  pub_keys = JSON.parse(HTTPClient.new.get(JWKS_URI).body, symbolize_names: true)
  @cached_keys = nil if options[:invalidate] # need to reload the keys
  @cached_keys ||= pub_keys
end
claim,algo = JWT.decode(jwt_text, nil, true, { algorithms: [algo["alg"]], jwks: jwk_loader }) ## algo["alg"] == "RS256"
puts claim,algo

実際には最初のclaim["iss"]が信頼できるサイトか確認する必要があります。

サンプルコードの実行

作成したコードを実行してみます。

idtoken.txtにID Tokenの内容を保存する
$ cat > idtoken.txt
eyJhbGci....

$ bundle exec ruby ./verify-idtoken.rb

正常に処理されれば次のように検証された内容が返されます。

実行結果(正常系)
--- unverified output ---
{"iss"=>"https://example.com/dex", "sub"=>"Cj....cA", "aud"=>"example-app", "exp"=>1576040265, ..., "iat"=>1575953865, "name"=>"Yasuhiro Abe"}
{"alg"=>"RS256", "kid"=>"be215b1....460975ac"}
--- verified output ---
{"iss"=>"https://example.com/dex", "sub"=>"Cj....cA", "aud"=>"example-app", "exp"=>1576040265, ..., "iat"=>1575953865, "name"=>"Yasuhiro Abe"}
{"alg"=>"RS256", "kid"=>"be215b1....460975ac"}

idtoken.txtの内容が古いものだと次のようにkidに対応する公開鍵がないとエラーになります。

古いtokenの検証結果(異常系)
.../lib/ruby/3.3.0/gems/jwt-2.10.1/lib/jwt/jwk/key_finder.rb:25:in `key_for': Could not find public key for kid 69ba8d782bacd99772120aa88153269876ca40f2 (JWT::DecodeError)
        from .../lib/ruby/3.3.0/gems/jwt-2.10.1/lib/jwt/decode.rb:63:in `set_key'
        from .../lib/ruby/3.3.0/gems/jwt-2.10.1/lib/jwt/decode.rb:34:in `decode_segments'
        from .../lib/ruby/3.3.0/gems/jwt-2.10.1/lib/jwt.rb:51:in `block in decode'
        from .../lib/ruby/3.3.0/gems/jwt-2.10.1/lib/jwt/deprecations.rb:9:in `context'
        from .../lib/ruby/3.3.0/gems/jwt-2.10.1/lib/jwt.rb:50:in `decode'
        from ./verify-idtoken.rb:27:in `<main>'
3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?