From 2e8758dcc5d89775db6398bddc6ef85d4d1be3d5 Mon Sep 17 00:00:00 2001 From: "nicholas a. evans" Date: Tue, 6 Dec 2022 14:04:59 -0500 Subject: [PATCH] Add expires_at, to match OmniAuth auth schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OmniAuth's [Auth Hash Schema] should return an `expires_at` field as a timestamp, but this gem returns `expires_in`. For compatibility with other `oauth2` OmniAuth strategies, we should also return `expires_at`. I'm not sure if the best place to fix it is here or upstream, in `Rack::OAuth2::AccessToken`. On the one hand, the `oauth2` gem handles it in `OAuth2::AccessToken`. On the other hand, the OmniAuth strategy is the only place we can ensure minimal latency between the server response and `expires_at` computation. I chose here. 🙂 [Auth Hash Schema]: https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema n.b. I would have assumed that "timestamp" in the schema meant a Time object, but all of the gems that inherit from `omniauth-oauth2` return `Time#to_i`, which is also appropriate. --- lib/omniauth/strategies/openid_connect.rb | 7 +++++++ test/lib/omniauth/strategies/openid_connect_test.rb | 11 +++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/omniauth/strategies/openid_connect.rb b/lib/omniauth/strategies/openid_connect.rb index cc857876..7831101f 100644 --- a/lib/omniauth/strategies/openid_connect.rb +++ b/lib/omniauth/strategies/openid_connect.rb @@ -65,6 +65,7 @@ class OpenIDConnect # rubocop:disable Metrics/ClassLength }, code_challenge_method: 'S256', } + option :expires_latency # seconds taken from credentials expires_at def uid user_info.raw_attributes[options.uid_field.to_sym] || user_info.sub @@ -95,6 +96,7 @@ def uid token: access_token.access_token, refresh_token: access_token.refresh_token, expires_in: access_token.expires_in, + expires_at: @access_token_expires_at, scope: access_token.scope, } end @@ -262,6 +264,11 @@ def access_token @access_token = client.access_token!(token_request_params) verify_id_token!(@access_token.id_token) if configured_response_type == 'code' + if @access_token.expires_in + @access_token_expires_at = Time.now.to_i + @access_token.expires_in + @access_token_expires_at -= options.expires_latency if options.expires_latency + end + @access_token end diff --git a/test/lib/omniauth/strategies/openid_connect_test.rb b/test/lib/omniauth/strategies/openid_connect_test.rb index f49c5bd6..df98bf56 100644 --- a/test/lib/omniauth/strategies/openid_connect_test.rb +++ b/test/lib/omniauth/strategies/openid_connect_test.rb @@ -522,6 +522,7 @@ def test_credentials strategy.options.issuer = 'example.com' strategy.options.client_signing_alg = :RS256 strategy.options.client_jwk_signing_key = jwks.to_json + strategy.options.expires_latency = 60 id_token = stub('OpenIDConnect::ResponseObject::IdToken') id_token.stubs(:verify!).returns(true) @@ -530,7 +531,7 @@ def test_credentials access_token = stub('OpenIDConnect::AccessToken') access_token.stubs(:access_token).returns(SecureRandom.hex(16)) access_token.stubs(:refresh_token).returns(SecureRandom.hex(16)) - access_token.stubs(:expires_in).returns(Time.now) + access_token.stubs(:expires_in).returns(3599) access_token.stubs(:scope).returns('openidconnect') access_token.stubs(:id_token).returns(jwt.to_s) @@ -538,6 +539,12 @@ def test_credentials access_token.expects(:refresh_token).returns(access_token.refresh_token) access_token.expects(:expires_in).returns(access_token.expires_in) + start = Time.now.to_i + access_token.expires_in - 60 + creds = strategy.credentials + stop = Time.now.to_i + access_token.expires_in - 60 + expires_at = creds.delete(:expires_at) + assert_includes start..stop, expires_at + assert_equal( { id_token: access_token.id_token, @@ -546,7 +553,7 @@ def test_credentials expires_in: access_token.expires_in, scope: access_token.scope, }, - strategy.credentials + creds ) end