chore(coderd): add tests for big oidc tokens (#12424)

- Adds two test cases for a 64k+ ID token and a 64k+ userinfo payload.
- Reformats the entire test cases array as instructed by CI
This commit is contained in:
Cian Johnston 2024-03-05 14:46:00 +00:00 committed by GitHub
parent b1f9a6dc31
commit 4343998c37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 229 additions and 176 deletions

View File

@ -19,6 +19,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"cdr.dev/slog"
"cdr.dev/slog/sloggers/slogtest" "cdr.dev/slog/sloggers/slogtest"
"github.com/coder/coder/v2/coderd" "github.com/coder/coder/v2/coderd"
"github.com/coder/coder/v2/coderd/audit" "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/database/dbtestutil"
"github.com/coder/coder/v2/coderd/promoauth" "github.com/coder/coder/v2/coderd/promoauth"
"github.com/coder/coder/v2/codersdk" "github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/cryptorand"
"github.com/coder/coder/v2/testutil" "github.com/coder/coder/v2/testutil"
) )
@ -742,202 +744,243 @@ func TestUserOIDC(t *testing.T) {
StatusCode int StatusCode int
IgnoreEmailVerified bool IgnoreEmailVerified bool
IgnoreUserInfo bool IgnoreUserInfo bool
}{{ }{
Name: "EmailOnly", {
IDTokenClaims: jwt.MapClaims{ Name: "EmailOnly",
"email": "kyle@kwc.io", IDTokenClaims: jwt.MapClaims{
"email": "kyle@kwc.io",
},
AllowSignups: true,
StatusCode: http.StatusOK,
Username: "kyle",
}, },
AllowSignups: true, {
StatusCode: http.StatusOK, Name: "EmailNotVerified",
Username: "kyle", IDTokenClaims: jwt.MapClaims{
}, { "email": "kyle@kwc.io",
Name: "EmailNotVerified", "email_verified": false,
IDTokenClaims: jwt.MapClaims{ },
"email": "kyle@kwc.io", AllowSignups: true,
"email_verified": false, StatusCode: http.StatusForbidden,
}, },
AllowSignups: true, {
StatusCode: http.StatusForbidden, Name: "EmailNotAString",
}, { IDTokenClaims: jwt.MapClaims{
Name: "EmailNotAString", "email": 3.14159,
IDTokenClaims: jwt.MapClaims{ "email_verified": false,
"email": 3.14159, },
"email_verified": false, AllowSignups: true,
StatusCode: http.StatusBadRequest,
}, },
AllowSignups: true, {
StatusCode: http.StatusBadRequest, Name: "EmailNotVerifiedIgnored",
}, { IDTokenClaims: jwt.MapClaims{
Name: "EmailNotVerifiedIgnored", "email": "kyle@kwc.io",
IDTokenClaims: jwt.MapClaims{ "email_verified": false,
"email": "kyle@kwc.io", },
"email_verified": false, AllowSignups: true,
StatusCode: http.StatusOK,
Username: "kyle",
IgnoreEmailVerified: true,
}, },
AllowSignups: true, {
StatusCode: http.StatusOK, Name: "NotInRequiredEmailDomain",
Username: "kyle", IDTokenClaims: jwt.MapClaims{
IgnoreEmailVerified: true, "email": "kyle@kwc.io",
}, { "email_verified": true,
Name: "NotInRequiredEmailDomain", },
IDTokenClaims: jwt.MapClaims{ AllowSignups: true,
"email": "kyle@kwc.io", EmailDomain: []string{
"email_verified": true, "coder.com",
},
StatusCode: http.StatusForbidden,
}, },
AllowSignups: true, {
EmailDomain: []string{ Name: "EmailDomainCaseInsensitive",
"coder.com", IDTokenClaims: jwt.MapClaims{
"email": "kyle@KWC.io",
"email_verified": true,
},
AllowSignups: true,
EmailDomain: []string{
"kwc.io",
},
StatusCode: http.StatusOK,
}, },
StatusCode: http.StatusForbidden, {
}, { Name: "EmailDomainSubset",
Name: "EmailDomainCaseInsensitive", IDTokenClaims: jwt.MapClaims{
IDTokenClaims: jwt.MapClaims{ "email": "colin@gmail.com",
"email": "kyle@KWC.io", "email_verified": true,
"email_verified": true, },
AllowSignups: true,
EmailDomain: []string{
"mail.com",
},
StatusCode: http.StatusForbidden,
}, },
AllowSignups: true, {
EmailDomain: []string{ Name: "EmptyClaims",
"kwc.io", IDTokenClaims: jwt.MapClaims{},
AllowSignups: true,
StatusCode: http.StatusBadRequest,
}, },
StatusCode: http.StatusOK, {
}, { Name: "NoSignups",
Name: "EmailDomainSubset", IDTokenClaims: jwt.MapClaims{
IDTokenClaims: jwt.MapClaims{ "email": "kyle@kwc.io",
"email": "colin@gmail.com", "email_verified": true,
"email_verified": true, },
StatusCode: http.StatusForbidden,
}, },
AllowSignups: true, {
EmailDomain: []string{ Name: "UsernameFromEmail",
"mail.com", IDTokenClaims: jwt.MapClaims{
"email": "kyle@kwc.io",
"email_verified": true,
},
Username: "kyle",
AllowSignups: true,
StatusCode: http.StatusOK,
}, },
StatusCode: http.StatusForbidden, {
}, { Name: "UsernameFromClaims",
Name: "EmptyClaims", IDTokenClaims: jwt.MapClaims{
IDTokenClaims: jwt.MapClaims{}, "email": "kyle@kwc.io",
AllowSignups: true, "email_verified": true,
StatusCode: http.StatusBadRequest, "preferred_username": "hotdog",
}, { },
Name: "NoSignups", Username: "hotdog",
IDTokenClaims: jwt.MapClaims{ AllowSignups: true,
"email": "kyle@kwc.io", StatusCode: http.StatusOK,
"email_verified": true,
}, },
StatusCode: http.StatusForbidden, {
}, { // Services like Okta return the email as the username:
Name: "UsernameFromEmail", // https://developer.okta.com/docs/reference/api/oidc/#base-claims-always-present
IDTokenClaims: jwt.MapClaims{ Name: "UsernameAsEmail",
"email": "kyle@kwc.io", IDTokenClaims: jwt.MapClaims{
"email_verified": true, "email": "kyle@kwc.io",
"email_verified": true,
"preferred_username": "kyle@kwc.io",
},
Username: "kyle",
AllowSignups: true,
StatusCode: http.StatusOK,
}, },
Username: "kyle", {
AllowSignups: true, // See: https://github.com/coder/coder/issues/4472
StatusCode: http.StatusOK, Name: "UsernameIsEmail",
}, { IDTokenClaims: jwt.MapClaims{
Name: "UsernameFromClaims", "preferred_username": "kyle@kwc.io",
IDTokenClaims: jwt.MapClaims{ },
"email": "kyle@kwc.io", Username: "kyle",
"email_verified": true, AllowSignups: true,
"preferred_username": "hotdog", StatusCode: http.StatusOK,
}, },
Username: "hotdog", {
AllowSignups: true, Name: "WithPicture",
StatusCode: http.StatusOK, IDTokenClaims: jwt.MapClaims{
}, { "email": "kyle@kwc.io",
// Services like Okta return the email as the username: "email_verified": true,
// https://developer.okta.com/docs/reference/api/oidc/#base-claims-always-present "preferred_username": "kyle",
Name: "UsernameAsEmail", "picture": "/example.png",
IDTokenClaims: jwt.MapClaims{ },
"email": "kyle@kwc.io", Username: "kyle",
"email_verified": true, AllowSignups: true,
"preferred_username": "kyle@kwc.io", AvatarURL: "/example.png",
StatusCode: http.StatusOK,
}, },
Username: "kyle", {
AllowSignups: true, Name: "WithUserInfoClaims",
StatusCode: http.StatusOK, IDTokenClaims: jwt.MapClaims{
}, { "email": "kyle@kwc.io",
// See: https://github.com/coder/coder/issues/4472 "email_verified": true,
Name: "UsernameIsEmail", },
IDTokenClaims: jwt.MapClaims{ UserInfoClaims: jwt.MapClaims{
"preferred_username": "kyle@kwc.io", "preferred_username": "potato",
"picture": "/example.png",
},
Username: "potato",
AllowSignups: true,
AvatarURL: "/example.png",
StatusCode: http.StatusOK,
}, },
Username: "kyle", {
AllowSignups: true, Name: "GroupsDoesNothing",
StatusCode: http.StatusOK, IDTokenClaims: jwt.MapClaims{
}, { "email": "coolin@coder.com",
Name: "WithPicture", "groups": []string{"pingpong"},
IDTokenClaims: jwt.MapClaims{ },
"email": "kyle@kwc.io", AllowSignups: true,
"email_verified": true, StatusCode: http.StatusOK,
"preferred_username": "kyle",
"picture": "/example.png",
}, },
Username: "kyle", {
AllowSignups: true, Name: "UserInfoOverridesIDTokenClaims",
AvatarURL: "/example.png", IDTokenClaims: jwt.MapClaims{
StatusCode: http.StatusOK, "email": "internaluser@internal.domain",
}, { "email_verified": false,
Name: "WithUserInfoClaims", },
IDTokenClaims: jwt.MapClaims{ UserInfoClaims: jwt.MapClaims{
"email": "kyle@kwc.io", "email": "externaluser@external.domain",
"email_verified": true, "email_verified": true,
"preferred_username": "user",
},
Username: "user",
AllowSignups: true,
IgnoreEmailVerified: false,
StatusCode: http.StatusOK,
}, },
UserInfoClaims: jwt.MapClaims{ {
"preferred_username": "potato", Name: "InvalidUserInfo",
"picture": "/example.png", 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, Name: "IgnoreUserInfo",
AvatarURL: "/example.png", IDTokenClaims: jwt.MapClaims{
StatusCode: http.StatusOK, "email": "user@internal.domain",
}, { "email_verified": true,
Name: "GroupsDoesNothing", "preferred_username": "user",
IDTokenClaims: jwt.MapClaims{ },
"email": "coolin@coder.com", UserInfoClaims: jwt.MapClaims{
"groups": []string{"pingpong"}, "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: "HugeIDToken",
}, { IDTokenClaims: inflateClaims(t, jwt.MapClaims{
Name: "UserInfoOverridesIDTokenClaims", "email": "user@domain.tld",
IDTokenClaims: jwt.MapClaims{ "email_verified": true,
"email": "internaluser@internal.domain", }, 65536),
"email_verified": false, Username: "user",
AllowSignups: true,
StatusCode: http.StatusOK,
}, },
UserInfoClaims: jwt.MapClaims{ {
"email": "externaluser@external.domain", Name: "HugeClaims",
"email_verified": true, IDTokenClaims: jwt.MapClaims{
"preferred_username": "user", "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 tc := tc
t.Run(tc.Name, func(t *testing.T) { t.Run(tc.Name, func(t *testing.T) {
t.Parallel() t.Parallel()
@ -956,7 +999,7 @@ func TestUserOIDC(t *testing.T) {
}) })
auditor := audit.NewMock() 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{ owner := coderdtest.New(t, &coderdtest.Options{
Auditor: auditor, Auditor: auditor,
OIDCConfig: cfg, OIDCConfig: cfg,
@ -1287,3 +1330,13 @@ func authCookieValue(cookies []*http.Cookie) string {
} }
return "" 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
}