|
1 | 1 | package coderd_test
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | +"context" |
4 | 5 | "net/http"
|
5 | 6 | "regexp"
|
6 | 7 | "testing"
|
7 | 8 |
|
8 | 9 | "github.com/golang-jwt/jwt/v4"
|
9 | 10 | "github.com/stretchr/testify/require"
|
| 11 | +"golang.org/x/xerrors" |
10 | 12 |
|
11 | 13 | "github.com/coder/coder/v2/coderd"
|
12 | 14 | "github.com/coder/coder/v2/coderd/coderdtest"
|
@@ -357,6 +359,40 @@ func TestUserOIDC(t *testing.T) {
|
357 | 359 | runner.ForceRefresh(t,client,claims)
|
358 | 360 | }
|
359 | 361 | })
|
| 362 | + |
| 363 | +t.Run("FailedRefresh",func(t*testing.T) { |
| 364 | +t.Parallel() |
| 365 | + |
| 366 | +runner:=setupOIDCTest(t,oidcTestConfig{ |
| 367 | +FakeOpts: []oidctest.FakeIDPOpt{ |
| 368 | +oidctest.WithRefreshHook(func(_string)error { |
| 369 | +// Always "expired" refresh token. |
| 370 | +returnxerrors.New("refresh token is expired") |
| 371 | +}), |
| 372 | +}, |
| 373 | +Config:func(cfg*coderd.OIDCConfig) { |
| 374 | +cfg.AllowSignups=true |
| 375 | +}, |
| 376 | +}) |
| 377 | + |
| 378 | +claims:= jwt.MapClaims{ |
| 379 | +"email":"alice@coder.com", |
| 380 | +} |
| 381 | +// Login a new client that signs up |
| 382 | +client,resp:=runner.Login(t,claims) |
| 383 | +require.Equal(t,http.StatusOK,resp.StatusCode) |
| 384 | + |
| 385 | +// Expire the token, cause a refresh |
| 386 | +runner.ExpireOauthToken(t,client) |
| 387 | + |
| 388 | +// This should fail because the oauth token refresh should fail. |
| 389 | +_,err:=client.User(context.Background(),codersdk.Me) |
| 390 | +require.Error(t,err) |
| 391 | +varapiError*codersdk.Error |
| 392 | +require.ErrorAs(t,err,&apiError) |
| 393 | +require.Equal(t,http.StatusUnauthorized,apiError.StatusCode()) |
| 394 | +require.ErrorContains(t,apiError,"refresh") |
| 395 | +}) |
360 | 396 | })
|
361 | 397 | }
|
362 | 398 |
|
@@ -576,14 +612,16 @@ type oidcTestRunner struct {
|
576 | 612 | // ForceRefresh will use an authenticated codersdk.Client, and force their
|
577 | 613 | // OIDC token to be expired and require a refresh. The refresh will use the claims provided.
|
578 | 614 | // It just calls the /users/me endpoint to trigger the refresh.
|
579 |
| -ForceRefreshfunc(t*testing.T,client*codersdk.Client,idToken jwt.MapClaims) |
| 615 | +ForceRefreshfunc(t*testing.T,client*codersdk.Client,idToken jwt.MapClaims) |
| 616 | +ExpireOauthTokenfunc(t*testing.T,client*codersdk.Client) |
580 | 617 | }
|
581 | 618 |
|
582 | 619 | typeoidcTestConfigstruct {
|
583 | 620 | Userinfo jwt.MapClaims
|
584 | 621 |
|
585 | 622 | // Config allows modifying the Coderd OIDC configuration.
|
586 |
| -Configfunc(cfg*coderd.OIDCConfig) |
| 623 | +Configfunc(cfg*coderd.OIDCConfig) |
| 624 | +FakeOpts []oidctest.FakeIDPOpt |
587 | 625 | }
|
588 | 626 |
|
589 | 627 | func (r*oidcTestRunner)AssertRoles(t*testing.T,userIdentstring,roles []string) {
|
@@ -633,10 +671,12 @@ func setupOIDCTest(t *testing.T, settings oidcTestConfig) *oidcTestRunner {
|
633 | 671 | t.Helper()
|
634 | 672 |
|
635 | 673 | fake:=oidctest.NewFakeIDP(t,
|
636 |
| -oidctest.WithStaticUserInfo(settings.Userinfo), |
637 |
| -oidctest.WithLogging(t,nil), |
638 |
| -// Run fake IDP on a real webserver |
639 |
| -oidctest.WithServing(), |
| 674 | +append([]oidctest.FakeIDPOpt{ |
| 675 | +oidctest.WithStaticUserInfo(settings.Userinfo), |
| 676 | +oidctest.WithLogging(t,nil), |
| 677 | +// Run fake IDP on a real webserver |
| 678 | +oidctest.WithServing(), |
| 679 | +},settings.FakeOpts...)..., |
640 | 680 | )
|
641 | 681 |
|
642 | 682 | ctx:=testutil.Context(t,testutil.WaitMedium)
|
@@ -665,5 +705,8 @@ func setupOIDCTest(t *testing.T, settings oidcTestConfig) *oidcTestRunner {
|
665 | 705 | ForceRefresh:func(t*testing.T,client*codersdk.Client,idToken jwt.MapClaims) {
|
666 | 706 | helper.ForceRefresh(t,api.Database,client,idToken)
|
667 | 707 | },
|
| 708 | +ExpireOauthToken:func(t*testing.T,client*codersdk.Client) { |
| 709 | +helper.ExpireOauthToken(t,api.Database,client) |
| 710 | +}, |
668 | 711 | }
|
669 | 712 | }
|