diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index b1e4bbd..542db5f 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -49,9 +49,6 @@ module Authentication user.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session| Current.session = session - # Store auth_time in session for OIDC max_age support - session[:auth_time] = Time.now.to_i - # Extract root domain for cross-subdomain cookies (required for forward auth) domain = extract_root_domain(request.host) diff --git a/app/controllers/oidc_controller.rb b/app/controllers/oidc_controller.rb index aa1dff5..e93578f 100644 --- a/app/controllers/oidc_controller.rb +++ b/app/controllers/oidc_controller.rb @@ -412,13 +412,14 @@ class OidcController < ApplicationController end # Generate ID token (JWT) with pairwise SID, at_hash, and auth_time + # auth_time comes from the Session model's created_at (when user logged in) id_token = OidcJwtService.generate_id_token( user, application, consent: consent, nonce: auth_code.nonce, access_token: access_token_record.plaintext_token, - auth_time: session[:auth_time] + auth_time: Current.session.created_at.to_i ) # Return tokens @@ -536,12 +537,13 @@ class OidcController < ApplicationController end # Generate new ID token (JWT with pairwise SID, at_hash, and auth_time; no nonce for refresh grants) + # auth_time comes from the Session model's created_at (when user logged in) id_token = OidcJwtService.generate_id_token( user, application, consent: consent, access_token: new_access_token.plaintext_token, - auth_time: session[:auth_time] + auth_time: Current.session.created_at.to_i ) # Return new tokens diff --git a/test/controllers/oidc_pkce_controller_test.rb b/test/controllers/oidc_pkce_controller_test.rb index 80acb05..ba4502e 100644 --- a/test/controllers/oidc_pkce_controller_test.rb +++ b/test/controllers/oidc_pkce_controller_test.rb @@ -532,7 +532,7 @@ class OidcPkceControllerTest < ActionDispatch::IntegrationTest # AUTH_TIME CLAIM TESTS # ==================== - test "ID token includes auth_time claim from session" do + test "ID token includes auth_time claim from session created_at" do # Create consent OidcUserConsent.create!( user: @user, @@ -548,8 +548,8 @@ class OidcPkceControllerTest < ActionDispatch::IntegrationTest .tr("+/", "-_") .tr("=", "") - # Set auth_time in session (simulating user login) - session[:auth_time] = Time.now.to_i - 300 # 5 minutes ago + # Get the expected auth_time from the session's created_at + expected_auth_time = Current.session.created_at.to_i # Create authorization code auth_code = OidcAuthorizationCode.create!( @@ -577,10 +577,10 @@ class OidcPkceControllerTest < ActionDispatch::IntegrationTest tokens = JSON.parse(@response.body) assert tokens.key?("id_token") - # Decode and verify auth_time is present + # Decode and verify auth_time is present and matches session created_at decoded = JWT.decode(tokens["id_token"], nil, false).first assert_includes decoded.keys, "auth_time", "ID token should include auth_time" - assert_equal session[:auth_time], decoded["auth_time"], "auth_time should match session value" + assert_equal expected_auth_time, decoded["auth_time"], "auth_time should match session created_at" end test "ID token includes auth_time in refresh token flow" do @@ -593,8 +593,8 @@ class OidcPkceControllerTest < ActionDispatch::IntegrationTest sid: "test-sid-refresh-auth-time" ) - # Set auth_time in session - session[:auth_time] = Time.now.to_i - 600 # 10 minutes ago + # Get the expected auth_time from the session's created_at + expected_auth_time = Current.session.created_at.to_i # Create initial access and refresh tokens (bypass PKCE for this test) auth_code = OidcAuthorizationCode.create!( @@ -638,10 +638,10 @@ class OidcPkceControllerTest < ActionDispatch::IntegrationTest new_tokens = JSON.parse(@response.body) assert new_tokens.key?("id_token") - # Decode and verify auth_time is still present from refresh + # Decode and verify auth_time is still present from session created_at decoded = JWT.decode(new_tokens["id_token"], nil, false).first assert_includes decoded.keys, "auth_time", "Refreshed ID token should include auth_time" - assert_equal session[:auth_time], decoded["auth_time"], "auth_time should persist from original session" + assert_equal expected_auth_time, decoded["auth_time"], "auth_time should match session created_at" end test "at_hash is correctly computed and included in ID token" do