feat: failed update refresh should redirect to login (#9442)

* chore: update refresh oauth token message
* chore: unauthorized -> forbidden for non authentication failures
* redirect to login on all 401 responses
* add unit test to verify 401 on expired refresh
This commit is contained in:
Steven Masley
2023-08-30 16:14:24 -05:00
committed by GitHub
parent b9fbc541c6
commit e827278db7
9 changed files with 58 additions and 19 deletions

View File

@ -1,12 +1,14 @@
package coderd_test
import (
"context"
"net/http"
"regexp"
"testing"
"github.com/golang-jwt/jwt/v4"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/coder/coder/v2/coderd"
"github.com/coder/coder/v2/coderd/coderdtest"
@ -357,6 +359,40 @@ func TestUserOIDC(t *testing.T) {
runner.ForceRefresh(t, client, claims)
}
})
t.Run("FailedRefresh", func(t *testing.T) {
t.Parallel()
runner := setupOIDCTest(t, oidcTestConfig{
FakeOpts: []oidctest.FakeIDPOpt{
oidctest.WithRefreshHook(func(_ string) error {
// Always "expired" refresh token.
return xerrors.New("refresh token is expired")
}),
},
Config: func(cfg *coderd.OIDCConfig) {
cfg.AllowSignups = true
},
})
claims := jwt.MapClaims{
"email": "alice@coder.com",
}
// Login a new client that signs up
client, resp := runner.Login(t, claims)
require.Equal(t, http.StatusOK, resp.StatusCode)
// Expire the token, cause a refresh
runner.ExpireOauthToken(t, client)
// This should fail because the oauth token refresh should fail.
_, err := client.User(context.Background(), codersdk.Me)
require.Error(t, err)
var apiError *codersdk.Error
require.ErrorAs(t, err, &apiError)
require.Equal(t, http.StatusUnauthorized, apiError.StatusCode())
require.ErrorContains(t, apiError, "refresh")
})
})
}
@ -576,14 +612,16 @@ type oidcTestRunner struct {
// ForceRefresh will use an authenticated codersdk.Client, and force their
// OIDC token to be expired and require a refresh. The refresh will use the claims provided.
// It just calls the /users/me endpoint to trigger the refresh.
ForceRefresh func(t *testing.T, client *codersdk.Client, idToken jwt.MapClaims)
ForceRefresh func(t *testing.T, client *codersdk.Client, idToken jwt.MapClaims)
ExpireOauthToken func(t *testing.T, client *codersdk.Client)
}
type oidcTestConfig struct {
Userinfo jwt.MapClaims
// Config allows modifying the Coderd OIDC configuration.
Config func(cfg *coderd.OIDCConfig)
Config func(cfg *coderd.OIDCConfig)
FakeOpts []oidctest.FakeIDPOpt
}
func (r *oidcTestRunner) AssertRoles(t *testing.T, userIdent string, roles []string) {
@ -633,10 +671,12 @@ func setupOIDCTest(t *testing.T, settings oidcTestConfig) *oidcTestRunner {
t.Helper()
fake := oidctest.NewFakeIDP(t,
oidctest.WithStaticUserInfo(settings.Userinfo),
oidctest.WithLogging(t, nil),
// Run fake IDP on a real webserver
oidctest.WithServing(),
append([]oidctest.FakeIDPOpt{
oidctest.WithStaticUserInfo(settings.Userinfo),
oidctest.WithLogging(t, nil),
// Run fake IDP on a real webserver
oidctest.WithServing(),
}, settings.FakeOpts...)...,
)
ctx := testutil.Context(t, testutil.WaitMedium)
@ -665,5 +705,8 @@ func setupOIDCTest(t *testing.T, settings oidcTestConfig) *oidcTestRunner {
ForceRefresh: func(t *testing.T, client *codersdk.Client, idToken jwt.MapClaims) {
helper.ForceRefresh(t, api.Database, client, idToken)
},
ExpireOauthToken: func(t *testing.T, client *codersdk.Client) {
helper.ExpireOauthToken(t, api.Database, client)
},
}
}