@@ -3,13 +3,12 @@ package coderd_test
33import (
44 "context"
55 "crypto"
6- "crypto/rand"
7- "crypto/rsa"
6+ "fmt"
87 "io"
98 "net/http"
109 "net/url"
10+ "strings"
1111 "testing"
12- "time"
1312
1413 "github.com/coreos/go-oidc/v3/oidc"
1514 "github.com/golang-jwt/jwt"
@@ -450,17 +449,19 @@ func TestUserOIDC(t *testing.T) {
450449 tc := tc
451450 t .Run (tc .Name , func (t * testing.T ) {
452451 t .Parallel ()
453- config := createOIDCConfig (t , tc .Claims )
452+ conf := coderdtest .NewOIDCConfig (t , "" )
453+
454+ config := conf .OIDCConfig ()
454455 config .AllowSignups = tc .AllowSignups
455456 config .EmailDomain = tc .EmailDomain
457+
456458 client := coderdtest .New (t , & coderdtest.Options {
457459 OIDCConfig : config ,
458460 })
459- resp := oidcCallback (t , client )
461+ resp := oidcCallback (t , client , conf . EncodeClaims ( t , tc . Claims ) )
460462 assert .Equal (t , tc .StatusCode , resp .StatusCode )
461463
462- ctx , cancel := context .WithTimeout (context .Background (), testutil .WaitLong )
463- defer cancel ()
464+ ctx , _ := testutil .Context (t )
464465
465466 if tc .Username != "" {
466467 client .SessionToken = authCookieValue (resp .Cookies ())
@@ -478,10 +479,50 @@ func TestUserOIDC(t *testing.T) {
478479 })
479480 }
480481
482+ t .Run ("AlternateUsername" , func (t * testing.T ) {
483+ t .Parallel ()
484+
485+ conf := coderdtest .NewOIDCConfig (t , "" )
486+
487+ config := conf .OIDCConfig ()
488+ config .AllowSignups = true
489+
490+ client := coderdtest .New (t , & coderdtest.Options {
491+ OIDCConfig : config ,
492+ })
493+
494+ code := conf .EncodeClaims (t , jwt.MapClaims {
495+ "email" : "jon@coder.com" ,
496+ })
497+ resp := oidcCallback (t , client , code )
498+ assert .Equal (t , http .StatusTemporaryRedirect , resp .StatusCode )
499+
500+ ctx , _ := testutil .Context (t )
501+
502+ client .SessionToken = authCookieValue (resp .Cookies ())
503+ user , err := client .User (ctx , "me" )
504+ require .NoError (t , err )
505+ require .Equal (t , "jon" , user .Username )
506+
507+ // Pass a different subject field so that we prompt creating a
508+ // new user.
509+ code = conf .EncodeClaims (t , jwt.MapClaims {
510+ "email" : "jon@example2.com" ,
511+ "sub" : "diff" ,
512+ })
513+ resp = oidcCallback (t , client , code )
514+ assert .Equal (t , http .StatusTemporaryRedirect , resp .StatusCode )
515+
516+ client .SessionToken = authCookieValue (resp .Cookies ())
517+ user , err = client .User (ctx , "me" )
518+ require .NoError (t , err )
519+ require .True (t , strings .HasPrefix (user .Username , "jon-" ), "username %q should have prefix %q" , user .Username , "jon-" )
520+ })
521+
481522 t .Run ("Disabled" , func (t * testing.T ) {
482523 t .Parallel ()
483524 client := coderdtest .New (t , nil )
484- resp := oidcCallback (t , client )
525+ resp := oidcCallback (t , client , "asdf" )
485526 require .Equal (t , http .StatusPreconditionRequired , resp .StatusCode )
486527 })
487528
@@ -492,7 +533,7 @@ func TestUserOIDC(t *testing.T) {
492533 OAuth2Config : & oauth2Config {},
493534 },
494535 })
495- resp := oidcCallback (t , client )
536+ resp := oidcCallback (t , client , "asdf" )
496537 require .Equal (t , http .StatusBadRequest , resp .StatusCode )
497538 })
498539
@@ -514,48 +555,16 @@ func TestUserOIDC(t *testing.T) {
514555 Verifier : verifier ,
515556 },
516557 })
517- resp := oidcCallback (t , client )
558+ resp := oidcCallback (t , client , "asdf" )
518559 require .Equal (t , http .StatusBadRequest , resp .StatusCode )
519560 })
520561}
521562
522- // createOIDCConfig generates a new OIDCConfig that returns a static token
523- // with the claims provided.
524- func createOIDCConfig (t * testing.T , claims jwt.MapClaims ) * coderd.OIDCConfig {
525- t .Helper ()
526- key , err := rsa .GenerateKey (rand .Reader , 2048 )
527- require .NoError (t , err )
528-
529- // https://datatracker.ietf.org/doc/html/rfc7519#section-4.1
530- claims ["exp" ] = time .Now ().Add (time .Hour ).UnixMilli ()
531- claims ["iss" ] = "https://coder.com"
532- claims ["sub" ] = "hello"
533-
534- signed , err := jwt .NewWithClaims (jwt .SigningMethodRS256 , claims ).SignedString (key )
535- require .NoError (t , err )
536-
537- verifier := oidc .NewVerifier ("https://coder.com" , & oidc.StaticKeySet {
538- PublicKeys : []crypto.PublicKey {key .Public ()},
539- }, & oidc.Config {
540- SkipClientIDCheck : true ,
541- })
542-
543- return & coderd.OIDCConfig {
544- OAuth2Config : & oauth2Config {
545- token : (& oauth2.Token {
546- AccessToken : "token" ,
547- }).WithExtra (map [string ]interface {}{
548- "id_token" : signed ,
549- }),
550- },
551- Verifier : verifier ,
552- }
553- }
554-
555563func oauth2Callback (t * testing.T , client * codersdk.Client ) * http.Response {
556564 client .HTTPClient .CheckRedirect = func (req * http.Request , via []* http.Request ) error {
557565 return http .ErrUseLastResponse
558566 }
567+
559568 state := "somestate"
560569 oauthURL , err := client .URL .Parse ("/api/v2/users/oauth2/github/callback?code=asd&state=" + state )
561570 require .NoError (t , err )
@@ -573,19 +582,18 @@ func oauth2Callback(t *testing.T, client *codersdk.Client) *http.Response {
573582 return res
574583}
575584
576- func oidcCallback (t * testing.T , client * codersdk.Client ) * http.Response {
585+ func oidcCallback (t * testing.T , client * codersdk.Client , code string ) * http.Response {
577586 t .Helper ()
578587 client .HTTPClient .CheckRedirect = func (req * http.Request , via []* http.Request ) error {
579588 return http .ErrUseLastResponse
580589 }
581- state := "somestate"
582- oauthURL , err := client .URL .Parse ("/api/v2/users/oidc/callback?code=asd&state=" + state )
590+ oauthURL , err := client .URL .Parse (fmt .Sprintf ("/api/v2/users/oidc/callback?code=%s&state=somestate" , code ))
583591 require .NoError (t , err )
584592 req , err := http .NewRequestWithContext (context .Background (), "GET" , oauthURL .String (), nil )
585593 require .NoError (t , err )
586594 req .AddCookie (& http.Cookie {
587595 Name : codersdk .OAuth2StateKey ,
588- Value : state ,
596+ Value : "somestate" ,
589597 })
590598 res , err := client .HTTPClient .Do (req )
591599 require .NoError (t , err )
0 commit comments