Skip to content

Commit a1b716b

Browse files
committed
comments
1 parent a8a9633 commit a1b716b

File tree

5 files changed

+48
-21
lines changed

5 files changed

+48
-21
lines changed

coderd/coderdtest/oidctest/helper.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func (h *LoginHelper) Login(t *testing.T, idTokenClaims jwt.MapClaims) (*codersd
4646
return h.fake.Login(t, unauthenticatedClient, idTokenClaims)
4747
}
4848

49+
// ExpireOauthToken expires the oauth token for the given user.
4950
func (h *LoginHelper) ExpireOauthToken(t *testing.T, db database.Store, user *codersdk.Client) (refreshToken string) {
5051
t.Helper()
5152

@@ -79,7 +80,11 @@ func (h *LoginHelper) ExpireOauthToken(t *testing.T, db database.Store, user *co
7980
return link.OAuthRefreshToken
8081
}
8182

82-
// ForceRefresh forces the client to refresh its oauth token.
83+
// ForceRefresh forces the client to refresh its oauth token. It does this by
84+
// expiring the oauth token, then doing an authenticated call. This will force
85+
// the API Key middleware to refresh the oauth token.
86+
//
87+
// A unit test assertion makes sure the refresh token is used.
8388
func (h *LoginHelper) ForceRefresh(t *testing.T, db database.Store, user *codersdk.Client, idToken jwt.MapClaims) {
8489
t.Helper()
8590

coderd/coderdtest/oidctest/idp.go

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ import (
1818
"testing"
1919
"time"
2020

21-
"github.com/coder/coder/v2/codersdk"
22-
2321
"github.com/coreos/go-oidc/v3/oidc"
2422
"github.com/go-chi/chi/v5"
2523
"github.com/go-jose/go-jose/v3"
@@ -33,8 +31,11 @@ import (
3331
"cdr.dev/slog"
3432
"cdr.dev/slog/sloggers/slogtest"
3533
"github.com/coder/coder/v2/coderd"
34+
"github.com/coder/coder/v2/codersdk"
3635
)
3736

37+
// FakeIDP is a functional OIDC provider.
38+
// It only supports 1 OIDC client.
3839
type FakeIDP struct {
3940
issuer string
4041
key *rsa.PrivateKey
@@ -47,6 +48,8 @@ type FakeIDP struct {
4748
clientSecret string
4849
logger slog.Logger
4950

51+
// These maps are used to control the state of the IDP.
52+
// That is the various access tokens, refresh tokens, states, etc.
5053
codeToStateMap *SyncMap[string, string]
5154
// Token -> Email
5255
accessTokens *SyncMap[string, string]
@@ -68,18 +71,23 @@ type FakeIDP struct {
6871

6972
type FakeIDPOpt func(idp *FakeIDP)
7073

74+
// WithRefreshHook is called when a refresh token is used. The email is
75+
// the email of the user that is being refreshed assuming the claims are correct.
7176
func WithRefreshHook(hook func(email string) error) func(*FakeIDP) {
7277
return func(f *FakeIDP) {
7378
f.hookOnRefresh = hook
7479
}
7580
}
7681

82+
// WithLogging is optional, but will log some HTTP calls made to the IDP.
7783
func WithLogging(t testing.TB, options *slogtest.Options) func(*FakeIDP) {
7884
return func(f *FakeIDP) {
7985
f.logger = slogtest.Make(t, options)
8086
}
8187
}
8288

89+
// WithStaticUserInfo is optional, but will return the same user info for
90+
// every user on the /userinfo endpoint.
8391
func WithStaticUserInfo(info jwt.MapClaims) func(*FakeIDP) {
8492
return func(f *FakeIDP) {
8593
f.hookUserInfo = func(_ string) jwt.MapClaims {
@@ -94,6 +102,7 @@ func WithDynamicUserInfo(userInfoFunc func(email string) jwt.MapClaims) func(*Fa
94102
}
95103
}
96104

105+
// WithServing makes the IDP run an actual http server.
97106
func WithServing() func(*FakeIDP) {
98107
return func(f *FakeIDP) {
99108
f.serve = true
@@ -218,6 +227,8 @@ func (f *FakeIDP) AttemptLogin(t testing.TB, client *codersdk.Client, idTokenCla
218227

219228
// LoginClient reuses the context of the passed in client. This means the same
220229
// cookies will be used. This should be an unauthenticated client in most cases.
230+
//
231+
// This is a niche case, but it is needed for testing ConvertLoginType.
221232
func (f *FakeIDP) LoginClient(t testing.TB, client *codersdk.Client, idTokenClaims jwt.MapClaims, opts ...func(r *http.Request)) (*codersdk.Client, *http.Response) {
222233
t.Helper()
223234

@@ -268,7 +279,10 @@ func (f *FakeIDP) LoginClient(t testing.TB, client *codersdk.Client, idTokenClai
268279
}
269280

270281
// OIDCCallback will emulate the IDP redirecting back to the Coder callback.
271-
// This is helpful if no Coderd exists.
282+
// This is helpful if no Coderd exists because the IDP needs to redirect to
283+
// something.
284+
// Essentially this is used to fake the Coderd side of the exchange.
285+
// The flow starts at the user hitting the OIDC login page.
272286
func (f *FakeIDP) OIDCCallback(t testing.TB, state string, idTokenClaims jwt.MapClaims) (*http.Response, error) {
273287
t.Helper()
274288
f.stateToIDTokenClaims.Store(state, idTokenClaims)
@@ -320,6 +334,7 @@ func (f *FakeIDP) newRefreshTokens(email string) string {
320334
return refreshToken
321335
}
322336

337+
// authenticateBearerTokenRequest enforces the access token is valid.
323338
func (f *FakeIDP) authenticateBearerTokenRequest(t testing.TB, req *http.Request) (string, error) {
324339
t.Helper()
325340

@@ -332,6 +347,7 @@ func (f *FakeIDP) authenticateBearerTokenRequest(t testing.TB, req *http.Request
332347
return token, nil
333348
}
334349

350+
// authenticateOIDClientRequest enforces the client_id and client_secret are valid.
335351
func (f *FakeIDP) authenticateOIDClientRequest(t testing.TB, req *http.Request) (url.Values, error) {
336352
t.Helper()
337353

@@ -353,6 +369,7 @@ func (f *FakeIDP) authenticateOIDClientRequest(t testing.TB, req *http.Request)
353369
return values, nil
354370
}
355371

372+
// encodeClaims is a helper func to convert claims to a valid JWT.
356373
func (f *FakeIDP) encodeClaims(t testing.TB, claims jwt.MapClaims) string {
357374
t.Helper()
358375

@@ -374,6 +391,7 @@ func (f *FakeIDP) encodeClaims(t testing.TB, claims jwt.MapClaims) string {
374391
return signed
375392
}
376393

394+
// httpHandler is the IDP http server.
377395
func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
378396
t.Helper()
379397

@@ -572,12 +590,12 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
572590
return mux
573591
}
574592

575-
// HTTPClient runs the IDP in memory and returns an http.Client that can be used
576-
// to make requests to the IDP. All requests are handled in memory, and no network
577-
// requests are made.
593+
// HTTPClient does nothing if IsServing is used.
578594
//
579-
// If a request is not to the IDP, then the passed in client will be used.
580-
// If no client is passed in, then any regular network requests will fail.
595+
// If IsServing is not used, then it will return a client that will make requests
596+
// to the IDP all in memory. If a request is not to the IDP, then the passed in
597+
// client will be used. If no client is passed in, then any regular network
598+
// requests will fail.
581599
func (f *FakeIDP) HTTPClient(rest *http.Client) *http.Client {
582600
if f.serve {
583601
if rest == nil || rest.Transport == nil {
@@ -619,31 +637,40 @@ func (f *FakeIDP) RefreshUsed(refreshToken string) bool {
619637
return used
620638
}
621639

640+
// UpdateRefreshClaims allows the caller to change what claims are returned
641+
// for a given refresh token. By default, all refreshes use the same claims as
642+
// the original IDToken issuance.
622643
func (f *FakeIDP) UpdateRefreshClaims(refreshToken string, claims jwt.MapClaims) {
623644
f.refreshIDTokenClaims.Store(refreshToken, claims)
624645
}
625646

647+
// SetRedirect is required for the IDP to know where to redirect and call
648+
// Coderd.
626649
func (f *FakeIDP) SetRedirect(t testing.TB, url string) {
627650
t.Helper()
628651

629652
f.cfg.RedirectURL = url
630653
}
631654

655+
// SetCoderdCallback is optional and only works if not using the IsServing.
656+
// It will setup a fake "Coderd" for the IDP to call when the IDP redirects
657+
// back after authenticating.
632658
func (f *FakeIDP) SetCoderdCallback(callback func(req *http.Request) (*http.Response, error)) {
659+
if f.serve {
660+
panic("cannot set callback handler when using 'WithServing'. Must implement an actual 'Coderd'")
661+
}
633662
f.fakeCoderd = callback
634663
}
635664

636665
func (f *FakeIDP) SetCoderdCallbackHandler(handler http.HandlerFunc) {
637-
if f.serve {
638-
panic("cannot set callback handler when using 'WithServing'. Must implement an actual 'Coderd'")
639-
}
640-
f.fakeCoderd = func(req *http.Request) (*http.Response, error) {
666+
f.SetCoderdCallback(func(req *http.Request) (*http.Response, error) {
641667
resp := httptest.NewRecorder()
642668
handler.ServeHTTP(resp, req)
643669
return resp.Result(), nil
644-
}
670+
})
645671
}
646672

673+
// OIDCConfig returns the OIDC config to use for Coderd.
647674
func (f *FakeIDP) OIDCConfig(t testing.TB, scopes []string, opts ...func(cfg *coderd.OIDCConfig)) *coderd.OIDCConfig {
648675
t.Helper()
649676
if len(scopes) == 0 {

coderd/coderdtest/oidctest/idp_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"time"
99

1010
"github.com/golang-jwt/jwt/v4"
11-
1211
"github.com/stretchr/testify/assert"
1312

1413
"github.com/coder/coder/v2/coderd/coderdtest/oidctest"

coderd/coderdtest/oidctest/map.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package oidctest
22

33
import "sync"
44

5+
// SyncMap is a type safe sync.Map
56
type SyncMap[K, V any] struct {
67
m sync.Map
78
}

enterprise/coderd/userauth_test.go

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -572,15 +572,10 @@ type oidcTestRunner struct {
572572
API *coderden.API
573573

574574
// Login will call the OIDC flow with an unauthenticated client.
575-
// The customer actions will all be taken care of, and the idToken claims
576-
// will be returned.
575+
// The IDP will return the idToken claims.
577576
Login func(t *testing.T, idToken jwt.MapClaims) (*codersdk.Client, *http.Response)
578577
// ForceRefresh will use an authenticated codersdk.Client, and force their
579578
// OIDC token to be expired and require a refresh. The refresh will use the claims provided.
580-
//
581-
// The client MUST be used to actually trigger the refresh. This just
582-
// expires the oauth token so the next authenticated API call will
583-
// trigger a refresh. The returned function is an example of said call.
584579
// It just calls the /users/me endpoint to trigger the refresh.
585580
ForceRefresh func(t *testing.T, client *codersdk.Client, idToken jwt.MapClaims)
586581
}

0 commit comments

Comments
 (0)