Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitbbc1a9a

Browse files
authored
fix: useUserInfo endpoint with OIDC (#5735)
This resolves a user issue surfaced in Discord:https://discord.com/channels/747933592273027093/1064566338875576361/1064566338875576361Both methods of obtaining claims need to be used accordingto the OIDC specification.
1 parent592ce3b commitbbc1a9a

File tree

4 files changed

+89
-24
lines changed

4 files changed

+89
-24
lines changed

‎cli/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ func Server(vip *viper.Viper, newAPI func(context.Context, *coderd.Options) (*co
543543
Endpoint:oidcProvider.Endpoint(),
544544
Scopes:cfg.OIDC.Scopes.Value,
545545
},
546+
Provider:oidcProvider,
546547
Verifier:oidcProvider.Verifier(&oidc.Config{
547548
ClientID:cfg.OIDC.ClientID.Value,
548549
}),

‎coderd/coderdtest/coderdtest.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,14 +887,31 @@ func (o *OIDCConfig) EncodeClaims(t *testing.T, claims jwt.MapClaims) string {
887887
returnbase64.StdEncoding.EncodeToString([]byte(signed))
888888
}
889889

890-
func (o*OIDCConfig)OIDCConfig()*coderd.OIDCConfig {
890+
func (o*OIDCConfig)OIDCConfig(t*testing.T,userInfoClaims jwt.MapClaims)*coderd.OIDCConfig {
891+
// By default, the provider can be empty.
892+
// This means it won't support any endpoints!
893+
provider:=&oidc.Provider{}
894+
ifuserInfoClaims!=nil {
895+
resp,err:=json.Marshal(userInfoClaims)
896+
require.NoError(t,err)
897+
srv:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r*http.Request) {
898+
w.WriteHeader(http.StatusOK)
899+
_,_=w.Write(resp)
900+
}))
901+
t.Cleanup(srv.Close)
902+
cfg:=&oidc.ProviderConfig{
903+
UserInfoURL:srv.URL,
904+
}
905+
provider=cfg.NewProvider(context.Background())
906+
}
891907
return&coderd.OIDCConfig{
892908
OAuth2Config:o,
893909
Verifier:oidc.NewVerifier(o.issuer,&oidc.StaticKeySet{
894910
PublicKeys: []crypto.PublicKey{o.key.Public()},
895911
},&oidc.Config{
896912
SkipClientIDCheck:true,
897913
}),
914+
Provider:provider,
898915
UsernameField:"preferred_username",
899916
}
900917
}

‎coderd/userauth.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
204204
typeOIDCConfigstruct {
205205
httpmw.OAuth2Config
206206

207+
Provider*oidc.Provider
207208
Verifier*oidc.IDTokenVerifier
208209
// EmailDomains are the domains to enforce when a user authenticates.
209210
EmailDomain []string
@@ -258,6 +259,35 @@ func (api *API) userOIDC(rw http.ResponseWriter, r *http.Request) {
258259
})
259260
return
260261
}
262+
263+
// Not all claims are necessarily embedded in the `id_token`.
264+
// In GitLab, the username is left empty and must be fetched in UserInfo.
265+
//
266+
// The OIDC specification says claims can be in either place, so we fetch
267+
// user info and merge the two claim sets to be sure we have all of
268+
// the correct data.
269+
userInfo,err:=api.OIDCConfig.Provider.UserInfo(ctx,oauth2.StaticTokenSource(state.Token))
270+
iferr==nil {
271+
userInfoClaims:=map[string]interface{}{}
272+
err=userInfo.Claims(&userInfoClaims)
273+
iferr!=nil {
274+
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
275+
Message:"Failed to unmarshal user info claims.",
276+
Detail:err.Error(),
277+
})
278+
return
279+
}
280+
fork,v:=rangeuserInfoClaims {
281+
claims[k]=v
282+
}
283+
}elseif!strings.Contains(err.Error(),"user info endpoint is not supported by this provider") {
284+
httpapi.Write(ctx,rw,http.StatusInternalServerError, codersdk.Response{
285+
Message:"Failed to obtain user information claims.",
286+
Detail:"The OIDC provider returned no claims as part of the `id_token`. The attempt to fetch claims via the UserInfo endpoint failed: "+err.Error(),
287+
})
288+
return
289+
}
290+
261291
usernameRaw,ok:=claims[api.OIDCConfig.UsernameField]
262292
varusernamestring
263293
ifok {

‎coderd/userauth_test.go

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,8 @@ func TestUserOIDC(t *testing.T) {
480480

481481
for_,tc:=range []struct {
482482
Namestring
483-
Claims jwt.MapClaims
483+
IDTokenClaims jwt.MapClaims
484+
UserInfoClaims jwt.MapClaims
484485
AllowSignupsbool
485486
EmailDomain []string
486487
Usernamestring
@@ -489,31 +490,31 @@ func TestUserOIDC(t *testing.T) {
489490
IgnoreEmailVerifiedbool
490491
}{{
491492
Name:"EmailOnly",
492-
Claims: jwt.MapClaims{
493+
IDTokenClaims: jwt.MapClaims{
493494
"email":"kyle@kwc.io",
494495
},
495496
AllowSignups:true,
496497
StatusCode:http.StatusTemporaryRedirect,
497498
Username:"kyle",
498499
}, {
499500
Name:"EmailNotVerified",
500-
Claims: jwt.MapClaims{
501+
IDTokenClaims: jwt.MapClaims{
501502
"email":"kyle@kwc.io",
502503
"email_verified":false,
503504
},
504505
AllowSignups:true,
505506
StatusCode:http.StatusForbidden,
506507
}, {
507508
Name:"EmailNotAString",
508-
Claims: jwt.MapClaims{
509+
IDTokenClaims: jwt.MapClaims{
509510
"email":3.14159,
510511
"email_verified":false,
511512
},
512513
AllowSignups:true,
513514
StatusCode:http.StatusBadRequest,
514515
}, {
515516
Name:"EmailNotVerifiedIgnored",
516-
Claims: jwt.MapClaims{
517+
IDTokenClaims: jwt.MapClaims{
517518
"email":"kyle@kwc.io",
518519
"email_verified":false,
519520
},
@@ -523,7 +524,7 @@ func TestUserOIDC(t *testing.T) {
523524
IgnoreEmailVerified:true,
524525
}, {
525526
Name:"NotInRequiredEmailDomain",
526-
Claims: jwt.MapClaims{
527+
IDTokenClaims: jwt.MapClaims{
527528
"email":"kyle@kwc.io",
528529
"email_verified":true,
529530
},
@@ -534,7 +535,7 @@ func TestUserOIDC(t *testing.T) {
534535
StatusCode:http.StatusForbidden,
535536
}, {
536537
Name:"EmailDomainCaseInsensitive",
537-
Claims: jwt.MapClaims{
538+
IDTokenClaims: jwt.MapClaims{
538539
"email":"kyle@KWC.io",
539540
"email_verified":true,
540541
},
@@ -544,20 +545,20 @@ func TestUserOIDC(t *testing.T) {
544545
},
545546
StatusCode:http.StatusTemporaryRedirect,
546547
}, {
547-
Name:"EmptyClaims",
548-
Claims: jwt.MapClaims{},
549-
AllowSignups:true,
550-
StatusCode:http.StatusBadRequest,
548+
Name:"EmptyClaims",
549+
IDTokenClaims: jwt.MapClaims{},
550+
AllowSignups:true,
551+
StatusCode:http.StatusBadRequest,
551552
}, {
552553
Name:"NoSignups",
553-
Claims: jwt.MapClaims{
554+
IDTokenClaims: jwt.MapClaims{
554555
"email":"kyle@kwc.io",
555556
"email_verified":true,
556557
},
557558
StatusCode:http.StatusForbidden,
558559
}, {
559560
Name:"UsernameFromEmail",
560-
Claims: jwt.MapClaims{
561+
IDTokenClaims: jwt.MapClaims{
561562
"email":"kyle@kwc.io",
562563
"email_verified":true,
563564
},
@@ -566,7 +567,7 @@ func TestUserOIDC(t *testing.T) {
566567
StatusCode:http.StatusTemporaryRedirect,
567568
}, {
568569
Name:"UsernameFromClaims",
569-
Claims: jwt.MapClaims{
570+
IDTokenClaims: jwt.MapClaims{
570571
"email":"kyle@kwc.io",
571572
"email_verified":true,
572573
"preferred_username":"hotdog",
@@ -578,7 +579,7 @@ func TestUserOIDC(t *testing.T) {
578579
// Services like Okta return the email as the username:
579580
// https://developer.okta.com/docs/reference/api/oidc/#base-claims-always-present
580581
Name:"UsernameAsEmail",
581-
Claims: jwt.MapClaims{
582+
IDTokenClaims: jwt.MapClaims{
582583
"email":"kyle@kwc.io",
583584
"email_verified":true,
584585
"preferred_username":"kyle@kwc.io",
@@ -589,21 +590,35 @@ func TestUserOIDC(t *testing.T) {
589590
}, {
590591
// See: https://github.com/coder/coder/issues/4472
591592
Name:"UsernameIsEmail",
592-
Claims: jwt.MapClaims{
593+
IDTokenClaims: jwt.MapClaims{
593594
"preferred_username":"kyle@kwc.io",
594595
},
595596
Username:"kyle",
596597
AllowSignups:true,
597598
StatusCode:http.StatusTemporaryRedirect,
598599
}, {
599600
Name:"WithPicture",
600-
Claims: jwt.MapClaims{
601+
IDTokenClaims: jwt.MapClaims{
602+
"email":"kyle@kwc.io",
603+
"email_verified":true,
604+
"preferred_username":"kyle",
605+
"picture":"/example.png",
606+
},
607+
Username:"kyle",
608+
AllowSignups:true,
609+
AvatarURL:"/example.png",
610+
StatusCode:http.StatusTemporaryRedirect,
611+
}, {
612+
Name:"WithUserInfoClaims",
613+
IDTokenClaims: jwt.MapClaims{
601614
"email":"kyle@kwc.io",
602615
"email_verified":true,
603-
"username":"kyle",
604-
"picture":"/example.png",
605616
},
606-
Username:"kyle",
617+
UserInfoClaims: jwt.MapClaims{
618+
"preferred_username":"potato",
619+
"picture":"/example.png",
620+
},
621+
Username:"potato",
607622
AllowSignups:true,
608623
AvatarURL:"/example.png",
609624
StatusCode:http.StatusTemporaryRedirect,
@@ -613,15 +628,15 @@ func TestUserOIDC(t *testing.T) {
613628
t.Parallel()
614629
conf:=coderdtest.NewOIDCConfig(t,"")
615630

616-
config:=conf.OIDCConfig()
631+
config:=conf.OIDCConfig(t,tc.UserInfoClaims)
617632
config.AllowSignups=tc.AllowSignups
618633
config.EmailDomain=tc.EmailDomain
619634
config.IgnoreEmailVerified=tc.IgnoreEmailVerified
620635

621636
client:=coderdtest.New(t,&coderdtest.Options{
622637
OIDCConfig:config,
623638
})
624-
resp:=oidcCallback(t,client,conf.EncodeClaims(t,tc.Claims))
639+
resp:=oidcCallback(t,client,conf.EncodeClaims(t,tc.IDTokenClaims))
625640
assert.Equal(t,tc.StatusCode,resp.StatusCode)
626641

627642
ctx,_:=testutil.Context(t)
@@ -647,7 +662,7 @@ func TestUserOIDC(t *testing.T) {
647662

648663
conf:=coderdtest.NewOIDCConfig(t,"")
649664

650-
config:=conf.OIDCConfig()
665+
config:=conf.OIDCConfig(t,nil)
651666
config.AllowSignups=true
652667

653668
client:=coderdtest.New(t,&coderdtest.Options{
@@ -705,6 +720,7 @@ func TestUserOIDC(t *testing.T) {
705720
verifier:=oidc.NewVerifier("",&oidc.StaticKeySet{
706721
PublicKeys: []crypto.PublicKey{},
707722
},&oidc.Config{})
723+
provider:=&oidc.Provider{}
708724

709725
client:=coderdtest.New(t,&coderdtest.Options{
710726
OIDCConfig:&coderd.OIDCConfig{
@@ -715,6 +731,7 @@ func TestUserOIDC(t *testing.T) {
715731
"id_token":"invalid",
716732
}),
717733
},
734+
Provider:provider,
718735
Verifier:verifier,
719736
},
720737
})

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp