diff --git a/coderd/userauth_test.go b/coderd/userauth_test.go index 4432710c28..f1adbfe869 100644 --- a/coderd/userauth_test.go +++ b/coderd/userauth_test.go @@ -19,6 +19,7 @@ import ( "github.com/stretchr/testify/require" "golang.org/x/xerrors" + "cdr.dev/slog" "cdr.dev/slog/sloggers/slogtest" "github.com/coder/coder/v2/coderd" "github.com/coder/coder/v2/coderd/audit" @@ -30,6 +31,7 @@ import ( "github.com/coder/coder/v2/coderd/database/dbtestutil" "github.com/coder/coder/v2/coderd/promoauth" "github.com/coder/coder/v2/codersdk" + "github.com/coder/coder/v2/cryptorand" "github.com/coder/coder/v2/testutil" ) @@ -742,202 +744,243 @@ func TestUserOIDC(t *testing.T) { StatusCode int IgnoreEmailVerified bool IgnoreUserInfo bool - }{{ - Name: "EmailOnly", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", + }{ + { + Name: "EmailOnly", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + }, + AllowSignups: true, + StatusCode: http.StatusOK, + Username: "kyle", }, - AllowSignups: true, - StatusCode: http.StatusOK, - Username: "kyle", - }, { - Name: "EmailNotVerified", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": false, + { + Name: "EmailNotVerified", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": false, + }, + AllowSignups: true, + StatusCode: http.StatusForbidden, }, - AllowSignups: true, - StatusCode: http.StatusForbidden, - }, { - Name: "EmailNotAString", - IDTokenClaims: jwt.MapClaims{ - "email": 3.14159, - "email_verified": false, + { + Name: "EmailNotAString", + IDTokenClaims: jwt.MapClaims{ + "email": 3.14159, + "email_verified": false, + }, + AllowSignups: true, + StatusCode: http.StatusBadRequest, }, - AllowSignups: true, - StatusCode: http.StatusBadRequest, - }, { - Name: "EmailNotVerifiedIgnored", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": false, + { + Name: "EmailNotVerifiedIgnored", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": false, + }, + AllowSignups: true, + StatusCode: http.StatusOK, + Username: "kyle", + IgnoreEmailVerified: true, }, - AllowSignups: true, - StatusCode: http.StatusOK, - Username: "kyle", - IgnoreEmailVerified: true, - }, { - Name: "NotInRequiredEmailDomain", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": true, + { + Name: "NotInRequiredEmailDomain", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": true, + }, + AllowSignups: true, + EmailDomain: []string{ + "coder.com", + }, + StatusCode: http.StatusForbidden, }, - AllowSignups: true, - EmailDomain: []string{ - "coder.com", + { + Name: "EmailDomainCaseInsensitive", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@KWC.io", + "email_verified": true, + }, + AllowSignups: true, + EmailDomain: []string{ + "kwc.io", + }, + StatusCode: http.StatusOK, }, - StatusCode: http.StatusForbidden, - }, { - Name: "EmailDomainCaseInsensitive", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@KWC.io", - "email_verified": true, + { + Name: "EmailDomainSubset", + IDTokenClaims: jwt.MapClaims{ + "email": "colin@gmail.com", + "email_verified": true, + }, + AllowSignups: true, + EmailDomain: []string{ + "mail.com", + }, + StatusCode: http.StatusForbidden, }, - AllowSignups: true, - EmailDomain: []string{ - "kwc.io", + { + Name: "EmptyClaims", + IDTokenClaims: jwt.MapClaims{}, + AllowSignups: true, + StatusCode: http.StatusBadRequest, }, - StatusCode: http.StatusOK, - }, { - Name: "EmailDomainSubset", - IDTokenClaims: jwt.MapClaims{ - "email": "colin@gmail.com", - "email_verified": true, + { + Name: "NoSignups", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": true, + }, + StatusCode: http.StatusForbidden, }, - AllowSignups: true, - EmailDomain: []string{ - "mail.com", + { + Name: "UsernameFromEmail", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": true, + }, + Username: "kyle", + AllowSignups: true, + StatusCode: http.StatusOK, }, - StatusCode: http.StatusForbidden, - }, { - Name: "EmptyClaims", - IDTokenClaims: jwt.MapClaims{}, - AllowSignups: true, - StatusCode: http.StatusBadRequest, - }, { - Name: "NoSignups", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": true, + { + Name: "UsernameFromClaims", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": true, + "preferred_username": "hotdog", + }, + Username: "hotdog", + AllowSignups: true, + StatusCode: http.StatusOK, }, - StatusCode: http.StatusForbidden, - }, { - Name: "UsernameFromEmail", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": true, + { + // Services like Okta return the email as the username: + // https://developer.okta.com/docs/reference/api/oidc/#base-claims-always-present + Name: "UsernameAsEmail", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": true, + "preferred_username": "kyle@kwc.io", + }, + Username: "kyle", + AllowSignups: true, + StatusCode: http.StatusOK, }, - Username: "kyle", - AllowSignups: true, - StatusCode: http.StatusOK, - }, { - Name: "UsernameFromClaims", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": true, - "preferred_username": "hotdog", + { + // See: https://github.com/coder/coder/issues/4472 + Name: "UsernameIsEmail", + IDTokenClaims: jwt.MapClaims{ + "preferred_username": "kyle@kwc.io", + }, + Username: "kyle", + AllowSignups: true, + StatusCode: http.StatusOK, }, - Username: "hotdog", - AllowSignups: true, - StatusCode: http.StatusOK, - }, { - // Services like Okta return the email as the username: - // https://developer.okta.com/docs/reference/api/oidc/#base-claims-always-present - Name: "UsernameAsEmail", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": true, - "preferred_username": "kyle@kwc.io", + { + Name: "WithPicture", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": true, + "preferred_username": "kyle", + "picture": "/example.png", + }, + Username: "kyle", + AllowSignups: true, + AvatarURL: "/example.png", + StatusCode: http.StatusOK, }, - Username: "kyle", - AllowSignups: true, - StatusCode: http.StatusOK, - }, { - // See: https://github.com/coder/coder/issues/4472 - Name: "UsernameIsEmail", - IDTokenClaims: jwt.MapClaims{ - "preferred_username": "kyle@kwc.io", + { + Name: "WithUserInfoClaims", + IDTokenClaims: jwt.MapClaims{ + "email": "kyle@kwc.io", + "email_verified": true, + }, + UserInfoClaims: jwt.MapClaims{ + "preferred_username": "potato", + "picture": "/example.png", + }, + Username: "potato", + AllowSignups: true, + AvatarURL: "/example.png", + StatusCode: http.StatusOK, }, - Username: "kyle", - AllowSignups: true, - StatusCode: http.StatusOK, - }, { - Name: "WithPicture", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": true, - "preferred_username": "kyle", - "picture": "/example.png", + { + Name: "GroupsDoesNothing", + IDTokenClaims: jwt.MapClaims{ + "email": "coolin@coder.com", + "groups": []string{"pingpong"}, + }, + AllowSignups: true, + StatusCode: http.StatusOK, }, - Username: "kyle", - AllowSignups: true, - AvatarURL: "/example.png", - StatusCode: http.StatusOK, - }, { - Name: "WithUserInfoClaims", - IDTokenClaims: jwt.MapClaims{ - "email": "kyle@kwc.io", - "email_verified": true, + { + Name: "UserInfoOverridesIDTokenClaims", + IDTokenClaims: jwt.MapClaims{ + "email": "internaluser@internal.domain", + "email_verified": false, + }, + UserInfoClaims: jwt.MapClaims{ + "email": "externaluser@external.domain", + "email_verified": true, + "preferred_username": "user", + }, + Username: "user", + AllowSignups: true, + IgnoreEmailVerified: false, + StatusCode: http.StatusOK, }, - UserInfoClaims: jwt.MapClaims{ - "preferred_username": "potato", - "picture": "/example.png", + { + Name: "InvalidUserInfo", + IDTokenClaims: jwt.MapClaims{ + "email": "internaluser@internal.domain", + "email_verified": false, + }, + UserInfoClaims: jwt.MapClaims{ + "email": 1, + }, + AllowSignups: true, + IgnoreEmailVerified: false, + StatusCode: http.StatusInternalServerError, }, - Username: "potato", - AllowSignups: true, - AvatarURL: "/example.png", - StatusCode: http.StatusOK, - }, { - Name: "GroupsDoesNothing", - IDTokenClaims: jwt.MapClaims{ - "email": "coolin@coder.com", - "groups": []string{"pingpong"}, + { + Name: "IgnoreUserInfo", + IDTokenClaims: jwt.MapClaims{ + "email": "user@internal.domain", + "email_verified": true, + "preferred_username": "user", + }, + UserInfoClaims: jwt.MapClaims{ + "email": "user.mcname@external.domain", + "preferred_username": "Mr. User McName", + }, + Username: "user", + IgnoreUserInfo: true, + AllowSignups: true, + StatusCode: http.StatusOK, }, - AllowSignups: true, - StatusCode: http.StatusOK, - }, { - Name: "UserInfoOverridesIDTokenClaims", - IDTokenClaims: jwt.MapClaims{ - "email": "internaluser@internal.domain", - "email_verified": false, + { + Name: "HugeIDToken", + IDTokenClaims: inflateClaims(t, jwt.MapClaims{ + "email": "user@domain.tld", + "email_verified": true, + }, 65536), + Username: "user", + AllowSignups: true, + StatusCode: http.StatusOK, }, - UserInfoClaims: jwt.MapClaims{ - "email": "externaluser@external.domain", - "email_verified": true, - "preferred_username": "user", + { + Name: "HugeClaims", + IDTokenClaims: jwt.MapClaims{ + "email": "user@domain.tld", + "email_verified": true, + }, + UserInfoClaims: inflateClaims(t, jwt.MapClaims{}, 65536), + Username: "user", + AllowSignups: true, + StatusCode: http.StatusOK, }, - Username: "user", - AllowSignups: true, - IgnoreEmailVerified: false, - StatusCode: http.StatusOK, - }, { - Name: "InvalidUserInfo", - IDTokenClaims: jwt.MapClaims{ - "email": "internaluser@internal.domain", - "email_verified": false, - }, - UserInfoClaims: jwt.MapClaims{ - "email": 1, - }, - AllowSignups: true, - IgnoreEmailVerified: false, - StatusCode: http.StatusInternalServerError, - }, { - Name: "IgnoreUserInfo", - IDTokenClaims: jwt.MapClaims{ - "email": "user@internal.domain", - "email_verified": true, - "preferred_username": "user", - }, - UserInfoClaims: jwt.MapClaims{ - "email": "user.mcname@external.domain", - "preferred_username": "Mr. User McName", - }, - Username: "user", - IgnoreUserInfo: true, - AllowSignups: true, - StatusCode: http.StatusOK, - }} { + } { tc := tc t.Run(tc.Name, func(t *testing.T) { t.Parallel() @@ -956,7 +999,7 @@ func TestUserOIDC(t *testing.T) { }) auditor := audit.NewMock() - logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}) + logger := slogtest.Make(t, &slogtest.Options{IgnoreErrors: true}).Leveled(slog.LevelDebug) owner := coderdtest.New(t, &coderdtest.Options{ Auditor: auditor, OIDCConfig: cfg, @@ -1287,3 +1330,13 @@ func authCookieValue(cookies []*http.Cookie) string { } return "" } + +// inflateClaims 'inflates' a jwt.MapClaims from a seed by +// adding a ridiculously large key-value pair of length size. +func inflateClaims(t testing.TB, seed jwt.MapClaims, size int) jwt.MapClaims { + t.Helper() + junk, err := cryptorand.String(size) + require.NoError(t, err) + seed["random_data"] = junk + return seed +}