From 9b8ab3bdc6bc0573015d49b134eb91bf2bd10c90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A1n=20L=C3=B3pez=20G=C3=B3mez?= Date: Mon, 1 Aug 2016 10:17:06 +0200 Subject: [PATCH] ClientCredentials flow in UserAPI Fixes #528 --- cmd/dex-worker/main.go | 24 +++-- integration/user_api_test.go | 187 +++++++++++++++++++++++++++++++++-- server/config.go | 30 +++--- server/server.go | 11 ++- server/user.go | 36 +++++-- user/api/api.go | 10 +- user/api/api_test.go | 132 +++++++++++++++++++------ 7 files changed, 347 insertions(+), 83 deletions(-) diff --git a/cmd/dex-worker/main.go b/cmd/dex-worker/main.go index aef6f6c0..f80884cc 100644 --- a/cmd/dex-worker/main.go +++ b/cmd/dex-worker/main.go @@ -51,6 +51,9 @@ func main() { enableClientRegistration := fs.Bool("enable-client-registration", false, "Allow dynamic registration of clients") + // Client credentials administration + apiUseClientCredentials := fs.Bool("api-use-client-credentials", false, "Forces API to authenticate using client credentials instead of ID token. Clients must be 'admin clients' to use the API.") + noDB := fs.Bool("no-db", false, "manage entities in-process w/o any encryption, used only for single-node testing") // UI-related: @@ -137,16 +140,17 @@ func main() { } scfg := server.ServerConfig{ - IssuerURL: *issuer, - TemplateDir: *templates, - EmailTemplateDirs: emailTemplateDirs, - EmailFromAddress: *emailFrom, - EmailerConfigFile: *emailConfig, - IssuerName: *issuerName, - IssuerLogoURL: *issuerLogoURL, - EnableRegistration: *enableRegistration, - EnableClientRegistration: *enableClientRegistration, - RegisterOnFirstLogin: *registerOnFirstLogin, + IssuerURL: *issuer, + TemplateDir: *templates, + EmailTemplateDirs: emailTemplateDirs, + EmailFromAddress: *emailFrom, + EmailerConfigFile: *emailConfig, + IssuerName: *issuerName, + IssuerLogoURL: *issuerLogoURL, + EnableRegistration: *enableRegistration, + EnableClientRegistration: *enableClientRegistration, + EnableClientCredentialAccess: *apiUseClientCredentials, + RegisterOnFirstLogin: *registerOnFirstLogin, } if *noDB { diff --git a/integration/user_api_test.go b/integration/user_api_test.go index e08dc103..85108131 100644 --- a/integration/user_api_test.go +++ b/integration/user_api_test.go @@ -84,6 +84,12 @@ var ( userGoodToken = makeUserToken(testIssuerURL, "ID-1", testClientID, time.Hour*1, testPrivKey) + clientToken = makeClientToken(testIssuerURL, + testClientID, time.Hour*1, testPrivKey) + + badClientToken = makeClientToken(testIssuerURL, + userBadClientID, time.Hour*1, testPrivKey) + userBadTokenNotAdmin = makeUserToken(testIssuerURL, "ID-2", testClientID, time.Hour*1, testPrivKey) @@ -97,7 +103,7 @@ var ( "ID-4", testClientID, time.Hour*1, testPrivKey) ) -func makeUserAPITestFixtures() *userAPITestFixtures { +func makeUserAPITestFixtures(clientCredsFlag bool) *userAPITestFixtures { f := &userAPITestFixtures{} dbMap, _, _, um := makeUserObjects(userUsers, userPasswords) @@ -157,8 +163,8 @@ func makeUserAPITestFixtures() *userAPITestFixtures { f.emailer = &testEmailer{} um.Clock = clock - api := api.NewUsersAPI(um, clientManager, refreshRepo, f.emailer, "local") - usrSrv := server.NewUserMgmtServer(api, jwtvFactory, um, clientManager) + api := api.NewUsersAPI(um, clientManager, refreshRepo, f.emailer, "local", clientCredsFlag) + usrSrv := server.NewUserMgmtServer(api, jwtvFactory, um, clientManager, clientCredsFlag) f.hSrv = httptest.NewServer(usrSrv.HTTPHandler()) f.trans = &tokenHandlerTransport{ @@ -180,48 +186,89 @@ func TestGetUser(t *testing.T) { token string errCode int + + clientCredsFlag bool }{ { id: "ID-1", token: userGoodToken, errCode: 0, - }, { + + clientCredsFlag: false, + }, + { + id: "ID-1", + + token: clientToken, + errCode: 0, + + clientCredsFlag: true, + }, + { + id: "ID-1", + + token: badClientToken, + errCode: http.StatusForbidden, + + clientCredsFlag: true, + }, + { + id: "ID-1", + + token: clientToken, + errCode: http.StatusUnauthorized, + + clientCredsFlag: false, + }, + { id: "NOONE", token: userGoodToken, errCode: http.StatusNotFound, + + clientCredsFlag: false, }, { id: "ID-1", token: userBadTokenNotAdmin, errCode: http.StatusUnauthorized, + + clientCredsFlag: false, }, { id: "ID-1", token: userBadTokenExpired, errCode: http.StatusUnauthorized, + + clientCredsFlag: false, }, { id: "ID-1", token: userBadTokenDisabled, errCode: http.StatusUnauthorized, + + clientCredsFlag: false, }, { id: "ID-1", token: "", errCode: http.StatusUnauthorized, + + clientCredsFlag: false, }, { id: "ID-1", token: "gibberish", errCode: http.StatusUnauthorized, + + clientCredsFlag: false, }, } for i, tt := range tests { func() { - f := makeUserAPITestFixtures() + f := makeUserAPITestFixtures(tt.clientCredsFlag) f.trans.Token = tt.token defer f.close() @@ -318,7 +365,7 @@ func TestListUsers(t *testing.T) { for i, tt := range tests { func() { - f := makeUserAPITestFixtures() + f := makeUserAPITestFixtures(false) defer f.close() f.trans.Token = tt.token @@ -382,6 +429,8 @@ func TestCreateUser(t *testing.T) { wantResponse schema.UserCreateResponse wantCode int + + clientCredsFlag bool }{ { @@ -409,6 +458,53 @@ func TestCreateUser(t *testing.T) { }, }, }, + { + + req: schema.UserCreateRequest{ + User: &schema.User{ + Email: "newuser@example.com", + DisplayName: "New User", + EmailVerified: true, + Admin: false, + CreatedAt: clock.Now().Format(time.RFC3339), + }, + RedirectURL: testRedirectURL.String(), + }, + + token: clientToken, + + wantResponse: schema.UserCreateResponse{ + EmailSent: true, + User: &schema.User{ + Email: "newuser@example.com", + DisplayName: "New User", + EmailVerified: true, + Admin: false, + CreatedAt: clock.Now().Format(time.RFC3339), + }, + }, + + clientCredsFlag: true, + }, + { + + req: schema.UserCreateRequest{ + User: &schema.User{ + Email: "newuser@example.com", + DisplayName: "New User", + EmailVerified: true, + Admin: false, + CreatedAt: clock.Now().Format(time.RFC3339), + }, + RedirectURL: testRedirectURL.String(), + }, + + token: badClientToken, + + wantCode: http.StatusForbidden, + + clientCredsFlag: true, + }, { // Duplicate email @@ -488,6 +584,28 @@ func TestCreateUser(t *testing.T) { wantCode: http.StatusUnauthorized, }, + { + + req: schema.UserCreateRequest{ + User: &schema.User{ + Email: "newuser@example.com", + DisplayName: "New User", + EmailVerified: true, + Admin: false, + CreatedAt: clock.Now().Format(time.RFC3339), + }, + + RedirectURL: testRedirectURL.String(), + }, + + // make sure that the endpoint is protected, but don't exhaustively + // try every variation like in TestGetUser + token: clientToken, + + wantCode: http.StatusUnauthorized, + + clientCredsFlag: false, + }, { req: schema.UserCreateRequest{ User: &schema.User{ @@ -507,7 +625,7 @@ func TestCreateUser(t *testing.T) { } for i, tt := range tests { func() { - f := makeUserAPITestFixtures() + f := makeUserAPITestFixtures(tt.clientCredsFlag) defer f.close() f.trans.Token = tt.token f.emailer.cantEmail = tt.cantEmail @@ -588,7 +706,7 @@ func TestDisableUser(t *testing.T) { } for i, tt := range tests { - f := makeUserAPITestFixtures() + f := makeUserAPITestFixtures(false) usr, err := f.client.Users.Get(tt.id).Do() if err != nil { @@ -625,7 +743,7 @@ func TestRefreshTokenEndpoints(t *testing.T) { } for i, tt := range tests { - f := makeUserAPITestFixtures() + f := makeUserAPITestFixtures(false) list, err := f.client.RefreshClient.List(tt.userID).Do() if err != nil { t.Errorf("case %d: list clients: %v", i, err) @@ -666,6 +784,8 @@ func TestResendEmailInvitation(t *testing.T) { wantResponse schema.ResendEmailInvitationResponse wantCode int + + clientCredsFlag bool }{ { @@ -687,6 +807,36 @@ func TestResendEmailInvitation(t *testing.T) { RedirectURL: testRedirectURL.String(), }, + userID: "ID-3", + email: "Email-3@example.com", + token: clientToken, + + wantResponse: schema.ResendEmailInvitationResponse{ + EmailSent: true, + }, + + clientCredsFlag: true, + }, + { + + req: schema.ResendEmailInvitationRequest{ + RedirectURL: testRedirectURL.String(), + }, + + userID: "ID-3", + email: "Email-3@example.com", + token: badClientToken, + + wantCode: http.StatusForbidden, + + clientCredsFlag: true, + }, + { + + req: schema.ResendEmailInvitationRequest{ + RedirectURL: testRedirectURL.String(), + }, + userID: "ID-3", email: "Email-3@example.com", cantEmail: true, @@ -747,6 +897,19 @@ func TestResendEmailInvitation(t *testing.T) { RedirectURL: testRedirectURL.String(), }, + userID: "ID-3", + email: "Email-3@example.com", + token: clientToken, + + wantCode: http.StatusUnauthorized, + + clientCredsFlag: false, + }, + { + req: schema.ResendEmailInvitationRequest{ + RedirectURL: testRedirectURL.String(), + }, + userID: "ID-3", email: "Email-3@example.com", token: userBadTokenExpired, @@ -778,7 +941,7 @@ func TestResendEmailInvitation(t *testing.T) { } for i, tt := range tests { func() { - f := makeUserAPITestFixtures() + f := makeUserAPITestFixtures(tt.clientCredsFlag) defer f.close() f.trans.Token = tt.token f.emailer.cantEmail = tt.cantEmail @@ -869,6 +1032,10 @@ func (t *testEmailer) SendInviteEmail(email string, redirectURL url.URL, clientI return retURL, nil } +func makeClientToken(issuerURL url.URL, clientID string, expires time.Duration, privKey *key.PrivateKey) string { + return makeUserToken(issuerURL, clientID, clientID, expires, privKey) +} + func makeUserToken(issuerURL url.URL, userID, clientID string, expires time.Duration, privKey *key.PrivateKey) string { signer := key.NewPrivateKeySet([]*key.PrivateKey{testPrivKey}, diff --git a/server/config.go b/server/config.go index 87372468..9e835b36 100644 --- a/server/config.go +++ b/server/config.go @@ -29,17 +29,18 @@ import ( ) type ServerConfig struct { - IssuerURL string - IssuerName string - IssuerLogoURL string - TemplateDir string - EmailTemplateDirs []string - EmailFromAddress string - EmailerConfigFile string - StateConfig StateConfigurer - EnableRegistration bool - EnableClientRegistration bool - RegisterOnFirstLogin bool + IssuerURL string + IssuerName string + IssuerLogoURL string + TemplateDir string + EmailTemplateDirs []string + EmailFromAddress string + EmailerConfigFile string + StateConfig StateConfigurer + EnableRegistration bool + EnableClientRegistration bool + EnableClientCredentialAccess bool + RegisterOnFirstLogin bool } type StateConfigurer interface { @@ -78,9 +79,10 @@ func (cfg *ServerConfig) Server() (*Server, error) { HealthChecks: []health.Checkable{km}, Connectors: []connector.Connector{}, - EnableRegistration: cfg.EnableRegistration, - EnableClientRegistration: cfg.EnableClientRegistration, - RegisterOnFirstLogin: cfg.RegisterOnFirstLogin, + EnableRegistration: cfg.EnableRegistration, + EnableClientRegistration: cfg.EnableClientRegistration, + EnableClientCredentialAccess: cfg.EnableClientCredentialAccess, + RegisterOnFirstLogin: cfg.RegisterOnFirstLogin, } err = cfg.StateConfig.Configure(&srv) diff --git a/server/server.go b/server/server.go index 6ec37535..de461f89 100644 --- a/server/server.go +++ b/server/server.go @@ -93,9 +93,10 @@ type Server struct { UserEmailer *useremail.UserEmailer - EnableRegistration bool - EnableClientRegistration bool - RegisterOnFirstLogin bool + EnableRegistration bool + EnableClientRegistration bool + EnableClientCredentialAccess bool + RegisterOnFirstLogin bool dbMap *gorp.DbMap localConnectorID string @@ -300,8 +301,8 @@ func (s *Server) HTTPHandler() http.Handler { apiBasePath := path.Join(httpPathAPI, APIVersion) registerDiscoveryResource(apiBasePath, mux) - usersAPI := usersapi.NewUsersAPI(s.UserManager, s.ClientManager, s.RefreshTokenRepo, s.UserEmailer, s.localConnectorID) - handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientManager).HTTPHandler() + usersAPI := usersapi.NewUsersAPI(s.UserManager, s.ClientManager, s.RefreshTokenRepo, s.UserEmailer, s.localConnectorID, s.EnableClientCredentialAccess) + handler := NewUserMgmtServer(usersAPI, s.JWTVerifierFactory(), s.UserManager, s.ClientManager, s.EnableClientCredentialAccess).HTTPHandler() handleStripPrefix(apiBasePath+"/", handler) diff --git a/server/user.go b/server/user.go index d4901a48..f88ef460 100644 --- a/server/user.go +++ b/server/user.go @@ -35,18 +35,20 @@ var ( ) type UserMgmtServer struct { - api *api.UsersAPI - jwtvFactory JWTVerifierFactory - um *usermanager.UserManager - cm *clientmanager.ClientManager + api *api.UsersAPI + jwtvFactory JWTVerifierFactory + um *usermanager.UserManager + cm *clientmanager.ClientManager + allowClientCredsAuth bool } -func NewUserMgmtServer(userMgmtAPI *api.UsersAPI, jwtvFactory JWTVerifierFactory, um *usermanager.UserManager, cm *clientmanager.ClientManager) *UserMgmtServer { +func NewUserMgmtServer(userMgmtAPI *api.UsersAPI, jwtvFactory JWTVerifierFactory, um *usermanager.UserManager, cm *clientmanager.ClientManager, allowClientCredsAuth bool) *UserMgmtServer { return &UserMgmtServer{ - api: userMgmtAPI, - jwtvFactory: jwtvFactory, - um: um, - cm: cm, + api: userMgmtAPI, + jwtvFactory: jwtvFactory, + um: um, + cm: cm, + allowClientCredsAuth: allowClientCredsAuth, } } @@ -92,7 +94,7 @@ func (s *UserMgmtServer) authAPIHandle(handle authedHandle, requiresAdmin bool) s.writeError(w, err) return } - if creds.User.Disabled || (requiresAdmin && !creds.User.Admin) { + if !s.allowClientCredsAuth && (creds.User.Disabled || (requiresAdmin && !creds.User.Admin)) { s.writeError(w, api.ErrorUnauthorized) return } @@ -299,6 +301,20 @@ func (s *UserMgmtServer) getCreds(r *http.Request, requiresAdmin bool) (api.Cred return api.Creds{}, api.ErrorUnauthorized } + if s.allowClientCredsAuth && (len(clientIDs) == 1) && (sub == clientIDs[0]) { + isAdmin, err := s.cm.IsDexAdmin(clientIDs[0]) + if err != nil { + log.Errorf("userMgmtServer: GetCreds err: %q", err) + return api.Creds{}, err + } + if requiresAdmin && !isAdmin { + return api.Creds{}, api.ErrorForbidden + } + return api.Creds{ + ClientIDs: clientIDs, + }, nil + } + usr, err := s.um.Get(sub) if err != nil { if err == user.ErrorNotFound { diff --git a/user/api/api.go b/user/api/api.go index 64aeab0e..2d09ba0b 100644 --- a/user/api/api.go +++ b/user/api/api.go @@ -91,6 +91,7 @@ type UsersAPI struct { clientManager *clientmanager.ClientManager refreshRepo refresh.RefreshTokenRepo emailer Emailer + allowClientCreds bool } type Emailer interface { @@ -104,19 +105,19 @@ type Creds struct { } // TODO(ericchiang): Don't pass a dbMap. See #385. -func NewUsersAPI(userManager *usermanager.UserManager, clientManager *clientmanager.ClientManager, refreshRepo refresh.RefreshTokenRepo, emailer Emailer, localConnectorID string) *UsersAPI { +func NewUsersAPI(userManager *usermanager.UserManager, clientManager *clientmanager.ClientManager, refreshRepo refresh.RefreshTokenRepo, emailer Emailer, localConnectorID string, allowClientCreds bool) *UsersAPI { return &UsersAPI{ userManager: userManager, refreshRepo: refreshRepo, clientManager: clientManager, localConnectorID: localConnectorID, emailer: emailer, + allowClientCreds: allowClientCreds, } } func (u *UsersAPI) GetUser(creds Creds, id string) (schema.User, error) { log.Infof("userAPI: GetUser") - if !u.Authorize(creds) { return schema.User{}, ErrorUnauthorized } @@ -312,6 +313,11 @@ func (u *UsersAPI) RevokeRefreshTokensForClient(creds Creds, userID, clientID st } func (u *UsersAPI) Authorize(creds Creds) bool { + if u.allowClientCreds { + if creds.User.ID == "" { + return true + } + } return creds.User.Admin && !creds.User.Disabled } diff --git a/user/api/api_test.go b/user/api/api_test.go index 81624e58..5166ed99 100644 --- a/user/api/api_test.go +++ b/user/api/api_test.go @@ -63,6 +63,14 @@ var ( ClientIDs: []string{goodClientID}, } + clientCreds = Creds{ + User: user.User{ + ID: "", + Admin: false, + }, + ClientIDs: []string{goodClientID}, + } + badCreds = Creds{ User: user.User{ ID: "ID-2", @@ -103,7 +111,7 @@ var ( } ) -func makeTestFixtures() (*UsersAPI, *testEmailer) { +func makeTestFixtures(clientCredsFlag bool) (*UsersAPI, *testEmailer) { dbMap := db.NewMemDB() ur := func() user.UserRepo { repo, err := db.NewUserRepoFromUsers(dbMap, []user.UserWithRemoteIdentities{ @@ -223,39 +231,49 @@ func makeTestFixtures() (*UsersAPI, *testEmailer) { } emailer := &testEmailer{} - api := NewUsersAPI(mgr, clientManager, refreshRepo, emailer, "local") + api := NewUsersAPI(mgr, clientManager, refreshRepo, emailer, "local", clientCredsFlag) return api, emailer } func TestGetUser(t *testing.T) { tests := []struct { - creds Creds - id string - wantErr error + creds Creds + id string + wantErr error + clientCredsFlag bool }{ { - creds: goodCreds, - id: "ID-1", + creds: goodCreds, + id: "ID-1", + clientCredsFlag: false, }, { - creds: badCreds, - id: "ID-1", - wantErr: ErrorUnauthorized, + creds: badCreds, + id: "ID-1", + wantErr: ErrorUnauthorized, + clientCredsFlag: false, }, { - creds: goodCreds, - id: "NO_ID", - wantErr: ErrorResourceNotFound, + creds: goodCreds, + id: "NO_ID", + wantErr: ErrorResourceNotFound, + clientCredsFlag: false, }, { - creds: credsWithMultipleAudiences, - id: "ID-1", + creds: credsWithMultipleAudiences, + id: "ID-1", + clientCredsFlag: false, + }, + { + creds: clientCreds, + id: "ID-1", + clientCredsFlag: true, }, } for i, tt := range tests { - api, _ := makeTestFixtures() + api, _ := makeTestFixtures(tt.clientCredsFlag) usr, err := api.GetUser(tt.creds, tt.id) if tt.wantErr != nil { if err != tt.wantErr { @@ -309,7 +327,7 @@ func TestListUsers(t *testing.T) { } for i, tt := range tests { - api, _ := makeTestFixtures() + api, _ := makeTestFixtures(false) gotIDs := [][]string{} var next string @@ -346,6 +364,8 @@ func TestCreateUser(t *testing.T) { redirURL url.URL cantEmail bool + clientCredsFlag bool + wantResponse schema.UserCreateResponse wantClientID string wantErr error @@ -370,7 +390,31 @@ func TestCreateUser(t *testing.T) { CreatedAt: clock.Now().Format(time.RFC3339), }, }, - wantClientID: goodClientID, + wantClientID: goodClientID, + clientCredsFlag: false, + }, + { + creds: clientCreds, + usr: schema.User{ + Email: "newuser01@example.com", + DisplayName: "New User", + EmailVerified: true, + Admin: false, + }, + redirURL: validRedirURL, + + wantResponse: schema.UserCreateResponse{ + EmailSent: true, + User: &schema.User{ + Email: "newuser01@example.com", + DisplayName: "New User", + EmailVerified: true, + Admin: false, + CreatedAt: clock.Now().Format(time.RFC3339), + }, + }, + wantClientID: goodClientID, + clientCredsFlag: true, }, { creds: credsWithMultipleAudiences, @@ -392,7 +436,8 @@ func TestCreateUser(t *testing.T) { CreatedAt: clock.Now().Format(time.RFC3339), }, }, - wantClientID: goodClientID, + wantClientID: goodClientID, + clientCredsFlag: false, }, { creds: goodCreds, @@ -415,7 +460,8 @@ func TestCreateUser(t *testing.T) { }, ResetPasswordLink: resetPasswordURL.String(), }, - wantClientID: goodClientID, + wantClientID: goodClientID, + clientCredsFlag: false, }, { creds: goodCreds, @@ -439,12 +485,13 @@ func TestCreateUser(t *testing.T) { }, redirURL: validRedirURL, - wantErr: ErrorUnauthorized, + wantErr: ErrorUnauthorized, + clientCredsFlag: false, }, } for i, tt := range tests { - api, emailer := makeTestFixtures() + api, emailer := makeTestFixtures(tt.clientCredsFlag) emailer.cantEmail = tt.cantEmail response, err := api.CreateUser(tt.creds, tt.usr, tt.redirURL) @@ -528,7 +575,7 @@ func TestDisableUsers(t *testing.T) { } for i, tt := range tests { - api, _ := makeTestFixtures() + api, _ := makeTestFixtures(false) _, err := api.DisableUser(goodCreds, tt.id, tt.disable) if err != nil { t.Fatalf("case %d: unexpected error: %v", i, err) @@ -552,6 +599,8 @@ func TestResendEmailInvitation(t *testing.T) { redirURL url.URL cantEmail bool + clientCredsFlag bool + wantResponse schema.ResendEmailInvitationResponse wantErr error wantClientID string @@ -565,7 +614,20 @@ func TestResendEmailInvitation(t *testing.T) { wantResponse: schema.ResendEmailInvitationResponse{ EmailSent: true, }, - wantClientID: goodClientID, + wantClientID: goodClientID, + clientCredsFlag: false, + }, + { + creds: clientCreds, + userID: "ID-1", + email: "id1@example.com", + redirURL: validRedirURL, + + wantResponse: schema.ResendEmailInvitationResponse{ + EmailSent: true, + }, + wantClientID: goodClientID, + clientCredsFlag: true, }, { creds: goodCreds, @@ -578,7 +640,8 @@ func TestResendEmailInvitation(t *testing.T) { EmailSent: false, ResetPasswordLink: resetPasswordURL.String(), }, - wantClientID: goodClientID, + wantClientID: goodClientID, + clientCredsFlag: false, }, { creds: credsWithMultipleAudiences, @@ -591,7 +654,8 @@ func TestResendEmailInvitation(t *testing.T) { EmailSent: false, ResetPasswordLink: resetPasswordURL.String(), }, - wantClientID: goodClientID, + wantClientID: goodClientID, + clientCredsFlag: false, }, { creds: badCreds, @@ -599,7 +663,8 @@ func TestResendEmailInvitation(t *testing.T) { email: "id1@example.com", redirURL: validRedirURL, - wantErr: ErrorUnauthorized, + wantErr: ErrorUnauthorized, + clientCredsFlag: false, }, { creds: goodCreds, @@ -607,7 +672,8 @@ func TestResendEmailInvitation(t *testing.T) { email: "id1@example.com", redirURL: url.URL{Host: "scammers.com"}, - wantErr: ErrorInvalidRedirectURL, + wantErr: ErrorInvalidRedirectURL, + clientCredsFlag: false, }, { creds: goodCreds, @@ -615,7 +681,8 @@ func TestResendEmailInvitation(t *testing.T) { email: "id2@example.com", redirURL: validRedirURL, - wantErr: ErrorVerifiedEmail, + wantErr: ErrorVerifiedEmail, + clientCredsFlag: false, }, { creds: goodCreds, @@ -623,12 +690,13 @@ func TestResendEmailInvitation(t *testing.T) { email: "non-existent@example.com", redirURL: validRedirURL, - wantErr: ErrorResourceNotFound, + wantErr: ErrorResourceNotFound, + clientCredsFlag: false, }, } for i, tt := range tests { - api, emailer := makeTestFixtures() + api, emailer := makeTestFixtures(tt.clientCredsFlag) emailer.cantEmail = tt.cantEmail response, err := api.ResendEmailInvitation(tt.creds, tt.userID, tt.redirURL) @@ -670,7 +738,7 @@ func TestRevokeRefreshToken(t *testing.T) { {"ID-2", goodClientID, []string{goodClientID}, []string{}}, } - api, _ := makeTestFixtures() + api, _ := makeTestFixtures(false) listClientsWithRefreshTokens := func(creds Creds, userID string) ([]string, error) { clients, err := api.ListClientsWithRefreshTokens(creds, userID)