Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/omniauth/strategies/openid_connect.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the AccessToken#expires_at method with an expires_latency option?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the tl;dr answer to your question is, "yes... should we?" 🙂

I don't think Rack::OAuth2::AccessToken has an #expires_at method... https://github.com/nov/rack-oauth2/blob/master/lib/rack/oauth2/access_token.rb, or am I looking in the wrong place? I could submit a PR for that upstream.

This is the question that I was referring to in the PR description:

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

That was the justification I gave on Dec 6th. But, in retrospect, I may have also been motivated to do it this way because it meant one PR in one repo rather than two in two repos. 😉

It makes intuitive sense to me that expires_latency should be an optional parameter to #expires_at, like you suggest. I think one benefit of applying expires_latency during construction might be that it allows code that uses access_token to remain naive, and thus simplifies application of a uniform policy. 🤷🏻 I don't feel strongly about it one way or the other, and I'll gladly apply expires_latency in the initializer (like the oauth2 gem), as an argument to the #expires_at method, or both.

What do you think? Should I make a PR to https://github.com/nov/rack-oauth2 first, and then update this PR after a new version is released there?

@access_token_expires_at -= options.expires_latency if options.expires_latency
end

@access_token
end

Expand Down
11 changes: 9 additions & 2 deletions test/lib/omniauth/strategies/openid_connect_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -530,14 +531,20 @@ 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)

client.expects(:access_token!).returns(access_token)
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,
Expand All @@ -546,7 +553,7 @@ def test_credentials
expires_in: access_token.expires_in,
scope: access_token.scope,
},
strategy.credentials
creds
)
end

Expand Down