Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix local JWT auth #111

Merged
merged 3 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion lib/stytch/b2b_sessions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ def authenticate_jwt(
)
end

decoded_jwt = authenticate_jwt_local(session_jwt: session_jwt, authorization_check: authorization_check)
decoded_jwt = authenticate_jwt_local(session_jwt, max_token_age_seconds: max_token_age_seconds, authorization_check: authorization_check)
return decoded_jwt unless decoded_jwt.nil?

authenticate(
Expand Down
24 changes: 14 additions & 10 deletions lib/stytch/sessions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def authenticate_jwt(
)
end

session = authenticate_jwt_local(session_jwt)
session = authenticate_jwt_local(session_jwt, max_token_age_seconds: max_token_age_seconds)
if !session.nil?
{ 'session' => session }
else
Expand All @@ -237,6 +237,7 @@ def authenticate_jwt(
# Parse a JWT and verify the signature locally (without calling /authenticate in the API)
# Uses the cached value to get the JWK but if it is unavailable, it calls the get_jwks()
# function to get the JWK
# This method never authenticates a JWT directly with the API
# If max_token_age_seconds is not supplied 300 seconds will be used as the default.
def authenticate_jwt_local(session_jwt, max_token_age_seconds: nil)
max_token_age_seconds = 300 if max_token_age_seconds.nil?
Expand All @@ -245,6 +246,7 @@ def authenticate_jwt_local(session_jwt, max_token_age_seconds: nil)
begin
decoded_token = JWT.decode session_jwt, nil, true,
{ jwks: @jwks_loader, iss: issuer, verify_iss: true, aud: @project_id, verify_aud: true, algorithms: ['RS256'] }

session = decoded_token[0]
iat_time = Time.at(session['iat']).to_datetime
return nil unless iat_time + max_token_age_seconds >= Time.now
Expand Down Expand Up @@ -272,15 +274,17 @@ def marshal_jwt_into_session(jwt)
reserved_claims = ['aud', 'exp', 'iat', 'iss', 'jti', 'nbf', 'sub', stytch_claim]
custom_claims = jwt.reject { |key, _| reserved_claims.include?(key) }
{
'session_id' => jwt[stytch_claim]['id'],
'user_id' => jwt['sub'],
'started_at' => jwt[stytch_claim]['started_at'],
'last_accessed_at' => jwt[stytch_claim]['last_accessed_at'],
# For JWTs that include it, prefer the inner expires_at claim.
'expires_at' => expires_at,
'attributes' => jwt[stytch_claim]['attributes'],
'authentication_factors' => jwt[stytch_claim]['authentication_factors'],
'custom_claims' => custom_claims
'session' => {
'session_id' => jwt[stytch_claim]['id'],
'user_id' => jwt['sub'],
'started_at' => jwt[stytch_claim]['started_at'],
'last_accessed_at' => jwt[stytch_claim]['last_accessed_at'],
# For JWTs that include it, prefer the inner expires_at claim.
'expires_at' => expires_at,
'attributes' => jwt[stytch_claim]['attributes'],
'authentication_factors' => jwt[stytch_claim]['authentication_factors'],
'custom_claims' => custom_claims
}
}
end
# ENDMANUAL(Sessions::authenticate_jwt)
Expand Down
2 changes: 1 addition & 1 deletion lib/stytch/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Stytch
VERSION = '7.0.1'
VERSION = '7.0.2'
end
4 changes: 2 additions & 2 deletions spec/stytch/sessions_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@

session = sessions.marshal_jwt_into_session(claims)
# The session expires an hour after `now`.
expect(session['expires_at']).to eq('2022-05-03T19:51:41Z')
expect(session['session']['expires_at']).to eq('2022-05-03T19:51:41Z')
end

it 'marshals JWT into session (old format)' do
Expand All @@ -73,7 +73,7 @@
session = sessions.marshal_jwt_into_session(claims)

# The "exp" claim is five minutes after `now`.
expect(session['expires_at']).to eq('2022-05-03T18:56:41Z')
expect(session['session']['expires_at']).to eq('2022-05-03T18:56:41Z')
end

private
Expand Down