Skip to content

Commit 895bb00

Browse files
committed
test(oidc): test sourcing secondary claims from access token
1 parent b4275b3 commit 895bb00

File tree

2 files changed

+61
-6
lines changed

2 files changed

+61
-6
lines changed

coderd/coderdtest/oidctest/idp.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ type FakeIDP struct {
105105
// "Authorized Redirect URLs". This can be used to emulate that.
106106
hookValidRedirectURL func(redirectURL string) error
107107
hookUserInfo func(email string) (jwt.MapClaims, error)
108+
hookAccessTokenJWT func(email string, exp time.Time) jwt.MapClaims
108109
// defaultIDClaims is if a new client connects and we didn't preset
109110
// some claims.
110111
defaultIDClaims jwt.MapClaims
@@ -154,6 +155,12 @@ func WithMiddlewares(mws ...func(http.Handler) http.Handler) func(*FakeIDP) {
154155
}
155156
}
156157

158+
func WithAccessTokenJWTHook(hook func(email string, exp time.Time) jwt.MapClaims) func(*FakeIDP) {
159+
return func(f *FakeIDP) {
160+
f.hookAccessTokenJWT = hook
161+
}
162+
}
163+
157164
func WithHookWellKnown(hook func(r *http.Request, j *ProviderJSON) error) func(*FakeIDP) {
158165
return func(f *FakeIDP) {
159166
f.hookWellKnown = hook
@@ -316,8 +323,7 @@ const (
316323
func NewFakeIDP(t testing.TB, opts ...FakeIDPOpt) *FakeIDP {
317324
t.Helper()
318325

319-
block, _ := pem.Decode([]byte(testRSAPrivateKey))
320-
pkey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
326+
pkey, err := FakeIDPKey()
321327
require.NoError(t, err)
322328

323329
idp := &FakeIDP{
@@ -676,8 +682,13 @@ func (f *FakeIDP) newCode(state string) string {
676682

677683
// newToken enforces the access token exchanged is actually a valid access token
678684
// created by the IDP.
679-
func (f *FakeIDP) newToken(email string, expires time.Time) string {
685+
func (f *FakeIDP) newToken(t testing.TB, email string, expires time.Time) string {
680686
accessToken := uuid.NewString()
687+
if f.hookAccessTokenJWT != nil {
688+
claims := f.hookAccessTokenJWT(email, expires)
689+
accessToken = f.encodeClaims(t, claims)
690+
}
691+
681692
f.accessTokens.Store(accessToken, token{
682693
issued: time.Now(),
683694
email: email,
@@ -963,7 +974,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
963974
email := getEmail(claims)
964975
refreshToken := f.newRefreshTokens(email)
965976
token := map[string]interface{}{
966-
"access_token": f.newToken(email, exp),
977+
"access_token": f.newToken(t, email, exp),
967978
"refresh_token": refreshToken,
968979
"token_type": "Bearer",
969980
"expires_in": int64((f.defaultExpire).Seconds()),
@@ -1553,3 +1564,8 @@ d8h4Ht09E+f3nhTEc87mODkl7WJZpHL6V2sORfeq/eIkds+H6CJ4hy5w/bSw8tjf
15531564
sz9Di8sGIaUbLZI2rd0CQQCzlVwEtRtoNCyMJTTrkgUuNufLP19RZ5FpyXxBO5/u
15541565
QastnN77KfUwdj3SJt44U/uh1jAIv4oSLBr8HYUkbnI8
15551566
-----END RSA PRIVATE KEY-----`
1567+
1568+
func FakeIDPKey() (*rsa.PrivateKey, error) {
1569+
block, _ := pem.Decode([]byte(testRSAPrivateKey))
1570+
return x509.ParsePKCS1PrivateKey(block.Bytes)
1571+
}

coderd/userauth_test.go

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -892,13 +892,15 @@ func TestUserOIDC(t *testing.T) {
892892
Name string
893893
IDTokenClaims jwt.MapClaims
894894
UserInfoClaims jwt.MapClaims
895+
AccessTokenClaims jwt.MapClaims
895896
AllowSignups bool
896897
EmailDomain []string
897898
AssertUser func(t testing.TB, u codersdk.User)
898899
StatusCode int
899900
AssertResponse func(t testing.TB, resp *http.Response)
900901
IgnoreEmailVerified bool
901902
IgnoreUserInfo bool
903+
UseAccessToken bool
902904
}{
903905
{
904906
Name: "NoSub",
@@ -908,6 +910,32 @@ func TestUserOIDC(t *testing.T) {
908910
AllowSignups: true,
909911
StatusCode: http.StatusBadRequest,
910912
},
913+
{
914+
Name: "AccessTokenMerge",
915+
IDTokenClaims: jwt.MapClaims{
916+
"sub": uuid.NewString(),
917+
},
918+
AccessTokenClaims: jwt.MapClaims{
919+
"email": "kyle@kwc.io",
920+
},
921+
IgnoreUserInfo: true,
922+
AllowSignups: true,
923+
UseAccessToken: true,
924+
StatusCode: http.StatusOK,
925+
AssertUser: func(t testing.TB, u codersdk.User) {
926+
assert.Equal(t, "kyle@kwc.io", u.Email)
927+
},
928+
},
929+
{
930+
Name: "AccessTokenMergeNotJWT",
931+
IDTokenClaims: jwt.MapClaims{
932+
"sub": uuid.NewString(),
933+
},
934+
IgnoreUserInfo: true,
935+
AllowSignups: true,
936+
UseAccessToken: true,
937+
StatusCode: http.StatusBadRequest,
938+
},
911939
{
912940
Name: "EmailOnly",
913941
IDTokenClaims: jwt.MapClaims{
@@ -1290,20 +1318,31 @@ func TestUserOIDC(t *testing.T) {
12901318
tc := tc
12911319
t.Run(tc.Name, func(t *testing.T) {
12921320
t.Parallel()
1293-
fake := oidctest.NewFakeIDP(t,
1321+
opts := []oidctest.FakeIDPOpt{
12941322
oidctest.WithRefresh(func(_ string) error {
12951323
return xerrors.New("refreshing token should never occur")
12961324
}),
12971325
oidctest.WithServing(),
12981326
oidctest.WithStaticUserInfo(tc.UserInfoClaims),
1299-
)
1327+
}
1328+
1329+
if tc.AccessTokenClaims != nil && len(tc.AccessTokenClaims) > 0 {
1330+
opts = append(opts, oidctest.WithAccessTokenJWTHook(func(email string, exp time.Time) jwt.MapClaims {
1331+
return tc.AccessTokenClaims
1332+
}))
1333+
}
1334+
1335+
fake := oidctest.NewFakeIDP(t, opts...)
13001336
cfg := fake.OIDCConfig(t, nil, func(cfg *coderd.OIDCConfig) {
13011337
cfg.AllowSignups = tc.AllowSignups
13021338
cfg.EmailDomain = tc.EmailDomain
13031339
cfg.IgnoreEmailVerified = tc.IgnoreEmailVerified
13041340
if tc.IgnoreUserInfo {
13051341
cfg.SecondaryClaims = coderd.MergedClaimsSourceUserInfo
13061342
}
1343+
if tc.UseAccessToken {
1344+
cfg.SecondaryClaims = coderd.MergedClaimsSourceAccessToken
1345+
}
13071346
cfg.NameField = "name"
13081347
})
13091348

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy