fix: propagate AdditionalClaims on session refresh (#3547)

AdditionalClaims were only extracted during the initial login. On
cookie refresh, buildSessionFromClaims did extract them into a new
session, but redeemRefreshToken only copied Email, User, Groups and
PreferredUsername back to the existing session, discarding the
AdditionalClaims.

This affects OIDC and MS Entra ID providers. Any header
injection relying on additional claims goes stale until the user
re-authenticates.

Signed-off-by: Apollo3zehn <Apollo3zehn@users.noreply.github.com>
Co-authored-by: Apollo3zehn <Apollo3zehn@users.noreply.github.com>
This commit is contained in:
Apollo3zehn
2026-09-30 17:40:17 +02:00
committed by GitHub
co-authored by Apollo3zehn
parent 8f8d1f4eb3
commit b0d87093a8
4 changed files with 32 additions and 0 deletions
+1
View File
@@ -9,6 +9,7 @@
## Changes since v7.15.4
- [#3546](https://github.com/oauth2-proxy/oauth2-proxy/pull/3546) fix: strip the port from the request host when matching cookie domains @kirilju
- [#3547](https://github.com/oauth2-proxy/oauth2-proxy/pull/3547) fix: refresh additional claims for OIDC and MS Entra ID providers and properly populate additional claims during login (@Apollo3zehn)
# V7.15.4
+1
View File
@@ -196,6 +196,7 @@ func (p *MicrosoftEntraIDProvider) redeemRefreshTokenWithFederatedToken(ctx cont
s.User = newSession.User
s.Groups = newSession.Groups
s.PreferredUsername = newSession.PreferredUsername
s.AdditionalClaims = newSession.AdditionalClaims
}
s.AccessToken = newSession.AccessToken
+1
View File
@@ -196,6 +196,7 @@ func (p *OIDCProvider) redeemRefreshToken(ctx context.Context, s *sessions.Sessi
s.User = newSession.User
s.Groups = newSession.Groups
s.PreferredUsername = newSession.PreferredUsername
s.AdditionalClaims = newSession.AdditionalClaims
}
s.AccessToken = newSession.AccessToken
+29
View File
@@ -213,6 +213,35 @@ func TestOIDCProviderRefreshSessionIfNeededWithIdToken(t *testing.T) {
assert.Equal(t, refreshToken, existingSession.RefreshToken)
}
func TestOIDCProviderRefreshSessionIfNeededWithIdTokenUpdatesAdditionalClaims(t *testing.T) {
idToken, _ := newSignedTestIDToken(defaultIDToken)
body, _ := json.Marshal(redeemTokenResponse{
AccessToken: accessToken,
ExpiresIn: 10,
TokenType: "Bearer",
RefreshToken: refreshToken,
IDToken: idToken,
})
server, provider := newTestOIDCSetup(body)
provider.AdditionalClaims = []string{"phone_number"}
defer server.Close()
existingSession := &sessions.SessionState{
AccessToken: "changeit",
IDToken: "changeit",
RefreshToken: refreshToken,
AdditionalClaims: map[string]interface{}{
"phone_number": "stale",
},
}
refreshed, err := provider.RefreshSession(context.Background(), existingSession)
assert.Equal(t, nil, err)
assert.Equal(t, refreshed, true)
assert.Equal(t, defaultIDToken.Phone, existingSession.AdditionalClaims["phone_number"])
}
func TestOIDCProviderCreateSessionFromToken(t *testing.T) {
testCases := map[string]struct {
IDToken idTokenClaims