mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-10-21 10:17:22 +02:00
feat: allow arbitrary claims from the IDToken and IdentityProvider UserInfo endpoint to be added to the session state (#2685)
* feat: support additional claims Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * docs: clarify that AdditionalClaims may come from id_token or userinfo endpoint Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * feat: include AdditionalClaims in /oauth2/userinfo response (#834) Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * refactor: extract coerceClaim logic into util Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> * doc: add changelog entry for #2685 Signed-off-by: Jan Larwig <jan@larwig.com> * refactor: added more verbose comments to some struct fields and minor code cleanup Signed-off-by: Jan Larwig <jan@larwig.com> --------- Signed-off-by: afsu <suaf2020@163.com> Signed-off-by: af su <saf@zjuici.com> Signed-off-by: Jan Larwig <jan@larwig.com> Co-authored-by: af su <saf@zjuici.com> Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
co-authored by
af su
Jan Larwig
parent
c6355ee402
commit
e59f7c1549
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption"
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
|
||||
"github.com/pierrec/lz4/v4"
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
@@ -28,6 +29,9 @@ type SessionState struct {
|
||||
Groups []string `msgpack:"g,omitempty"`
|
||||
PreferredUsername string `msgpack:"pu,omitempty"`
|
||||
|
||||
// Additional claims
|
||||
AdditionalClaims map[string]interface{} `msgpack:"ac,omitempty"`
|
||||
|
||||
// Internal helpers, not serialized
|
||||
Clock func() time.Time `msgpack:"-"` // override for time.Now, for testing
|
||||
Lock Lock `msgpack:"-"`
|
||||
@@ -156,10 +160,20 @@ func (s *SessionState) GetClaim(claim string) []string {
|
||||
case "preferred_username":
|
||||
return []string{s.PreferredUsername}
|
||||
default:
|
||||
return []string{}
|
||||
return s.getAdditionalClaim(claim)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SessionState) getAdditionalClaim(claim string) []string {
|
||||
if value, ok := s.AdditionalClaims[claim]; ok {
|
||||
var result []string
|
||||
if err := util.CoerceClaim(value, &result); err == nil {
|
||||
return result
|
||||
}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// CheckNonce compares the Nonce against a potential hash of it
|
||||
func (s *SessionState) CheckNonce(hashed string) bool {
|
||||
return encryption.CheckNonce(s.Nonce, hashed)
|
||||
|
||||
@@ -222,6 +222,23 @@ func TestEncodeAndDecodeSessionState(t *testing.T) {
|
||||
Nonce: []byte("abcdef1234567890abcdef1234567890"),
|
||||
Groups: []string{"group-a", "group-b"},
|
||||
},
|
||||
"With additional claims": {
|
||||
Email: "username@example.com",
|
||||
User: "username",
|
||||
PreferredUsername: "preferred.username",
|
||||
AccessToken: "AccessToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7",
|
||||
IDToken: "IDToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7",
|
||||
CreatedAt: &created,
|
||||
ExpiresOn: &expires,
|
||||
RefreshToken: "RefreshToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7",
|
||||
Nonce: []byte("abcdef1234567890abcdef1234567890"),
|
||||
Groups: []string{"group-a", "group-b"},
|
||||
AdditionalClaims: map[string]interface{}{
|
||||
"custom_claim_1": "value1",
|
||||
"custom_claim_2": true,
|
||||
"custom_claim_3": []interface{}{"item1", "item2"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, secretSize := range []int{16, 24, 32} {
|
||||
@@ -289,3 +306,50 @@ func compareSessionStates(t *testing.T, expected *SessionState, actual *SessionS
|
||||
act.ExpiresOn = nil
|
||||
assert.Equal(t, exp, act)
|
||||
}
|
||||
|
||||
func TestGetClaim(t *testing.T) {
|
||||
createdAt := time.Now()
|
||||
expiresOn := createdAt.Add(1 * time.Hour)
|
||||
|
||||
ss := &SessionState{
|
||||
CreatedAt: &createdAt,
|
||||
ExpiresOn: &expiresOn,
|
||||
AccessToken: "AccessToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7",
|
||||
IDToken: "IDToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7",
|
||||
RefreshToken: "RefreshToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7",
|
||||
Email: "user@example.com",
|
||||
User: "user123",
|
||||
Groups: []string{"group1", "group2"},
|
||||
PreferredUsername: "preferred_user",
|
||||
AdditionalClaims: map[string]interface{}{
|
||||
"custom_claim_1": "value1",
|
||||
"custom_claim_2": true,
|
||||
"custom_claim_3": []string{"item1", "item2"},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
claim string
|
||||
want []string
|
||||
}{
|
||||
{"access_token", []string{"AccessToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7"}},
|
||||
{"id_token", []string{"IDToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7"}},
|
||||
{"refresh_token", []string{"RefreshToken.12349871293847fdsaihf9238h4f91h8fr.1349f831y98fd7"}},
|
||||
{"created_at", []string{createdAt.String()}},
|
||||
{"expires_on", []string{expiresOn.String()}},
|
||||
{"email", []string{"user@example.com"}},
|
||||
{"user", []string{"user123"}},
|
||||
{"groups", []string{"group1", "group2"}},
|
||||
{"preferred_username", []string{"preferred_user"}},
|
||||
{"custom_claim_1", []string{"value1"}},
|
||||
{"custom_claim_2", []string{"true"}},
|
||||
{"custom_claim_3", []string{"[\"item1\",\"item2\"]"}},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.claim, func(t *testing.T) {
|
||||
gs := NewWithT(t)
|
||||
gs.Expect(ss.GetClaim(tt.claim)).To(Equal(tt.want))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user