diff --git a/admin/api.go b/admin/api.go index 2b51672d..84e6c5ae 100644 --- a/admin/api.go +++ b/admin/api.go @@ -78,6 +78,8 @@ var ( client.ErrorPublicClientMissingName: errorMaker("bad_request", "Public clients require a ClientName", http.StatusBadRequest), + client.ErrorInvalidClientSecret: errorMaker("bad_request", "Secret must be a base64 encoded string", http.StatusBadRequest), + user.ErrorNotFound: errorMaker("resource_not_found", "Resource could not be found.", http.StatusNotFound), user.ErrorDuplicateEmail: errorMaker("bad_request", "Email already in use.", http.StatusBadRequest), user.ErrorInvalidEmail: errorMaker("bad_request", "invalid email.", http.StatusBadRequest), diff --git a/client/client.go b/client/client.go index 55590995..4105dba0 100644 --- a/client/client.go +++ b/client/client.go @@ -16,7 +16,10 @@ import ( ) var ( - ErrorInvalidClientID = errors.New("not a valid client ID") + ErrorInvalidClientID = errors.New("not a valid client ID") + + ErrorInvalidClientSecret = errors.New("not a valid client Secret") + ErrorInvalidRedirectURL = errors.New("not a valid redirect url for the given client") ErrorCantChooseRedirectURL = errors.New("must provide a redirect url; client has many") ErrorNoValidRedirectURLs = errors.New("no valid redirect URLs for this client.") @@ -46,7 +49,7 @@ const ( func HashSecret(creds oidc.ClientCredentials) ([]byte, error) { secretBytes, err := base64.URLEncoding.DecodeString(creds.Secret) if err != nil { - return nil, err + return nil, ErrorInvalidClientSecret } hashed, err := bcrypt.GenerateFromPassword([]byte( secretBytes), diff --git a/client/manager/manager.go b/client/manager/manager.go index 3fefff41..75ad7f30 100644 --- a/client/manager/manager.go +++ b/client/manager/manager.go @@ -196,18 +196,30 @@ func (m *ClientManager) addClientCredentials(cli *client.Client) error { seed = cli.Metadata.RedirectURIs[0].Host } - // Generate Client ID - clientID, err := m.clientIDGenerator(seed) - if err != nil { - return err + var err error + var clientID string + if cli.Credentials.ID != "" { + clientID = cli.Credentials.ID + } else { + // Generate Client ID + clientID, err = m.clientIDGenerator(seed) + if err != nil { + return err + } } - // Generate Secret - secret, err := m.secretGenerator() - if err != nil { - return err + var clientSecret string + if cli.Credentials.Secret != "" { + clientSecret = cli.Credentials.Secret + } else { + // Generate Secret + secret, err := m.secretGenerator() + if err != nil { + return err + } + clientSecret = base64.URLEncoding.EncodeToString(secret) } - clientSecret := base64.URLEncoding.EncodeToString(secret) + cli.Credentials = oidc.ClientCredentials{ ID: clientID, Secret: clientSecret, diff --git a/integration/admin_api_test.go b/integration/admin_api_test.go index 4fc7ac30..c1fa7916 100644 --- a/integration/admin_api_test.go +++ b/integration/admin_api_test.go @@ -383,12 +383,17 @@ func TestCreateClient(t *testing.T) { } addIDAndSecret := func(cli adminschema.Client) *adminschema.Client { - if cli.Public { - cli.Id = "client_" + cli.ClientName - } else { - cli.Id = "client_auth.example.com" + if cli.Id == "" { + if cli.Public { + cli.Id = "client_" + cli.ClientName + } else { + cli.Id = "client_auth.example.com" + } + } + + if cli.Secret == "" { + cli.Secret = base64.URLEncoding.EncodeToString([]byte("client_0")) } - cli.Secret = base64.URLEncoding.EncodeToString([]byte("client_0")) return &cli } @@ -436,6 +441,24 @@ func TestCreateClient(t *testing.T) { adminClientWithPeers := adminClientGood adminClientWithPeers.TrustedPeers = []string{"test_client_0"} + adminClientOwnID := adminClientGood + adminClientOwnID.Id = "my_own_id" + + clientGoodOwnID := clientGood + clientGoodOwnID.Credentials.ID = "my_own_id" + + adminClientOwnSecret := adminClientGood + adminClientOwnSecret.Secret = base64.URLEncoding.EncodeToString([]byte("my_own_secret")) + clientGoodOwnSecret := clientGood + + adminClientOwnIDAndSecret := adminClientGood + adminClientOwnIDAndSecret.Id = "my_own_id" + adminClientOwnIDAndSecret.Secret = base64.URLEncoding.EncodeToString([]byte("my_own_secret")) + clientGoodOwnIDAndSecret := clientGoodOwnID + + adminClientBadSecret := adminClientGood + adminClientBadSecret.Secret = "not_base64_encoded" + tests := []struct { req adminschema.ClientCreateRequest want adminschema.ClientCreateResponse @@ -446,24 +469,21 @@ func TestCreateClient(t *testing.T) { { req: adminschema.ClientCreateRequest{}, wantError: http.StatusBadRequest, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminschema.Client{ IsAdmin: true, }, }, wantError: http.StatusBadRequest, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminschema.Client{ RedirectURIs: []string{"909090"}, }, }, wantError: http.StatusBadRequest, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminClientGood, }, @@ -471,8 +491,7 @@ func TestCreateClient(t *testing.T) { Client: addIDAndSecret(adminClientGood), }, wantClient: clientGood, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminAdminClient, }, @@ -480,8 +499,7 @@ func TestCreateClient(t *testing.T) { Client: addIDAndSecret(adminAdminClient), }, wantClient: clientGoodAdmin, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminMultiRedirect, }, @@ -489,8 +507,7 @@ func TestCreateClient(t *testing.T) { Client: addIDAndSecret(adminMultiRedirect), }, wantClient: clientMultiRedirect, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminClientWithPeers, }, @@ -499,8 +516,7 @@ func TestCreateClient(t *testing.T) { }, wantClient: clientGood, wantTrustedPeers: []string{"test_client_0"}, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminPublicClientGood, }, @@ -508,18 +524,45 @@ func TestCreateClient(t *testing.T) { Client: addIDAndSecret(adminPublicClientGood), }, wantClient: clientPublicGood, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminPublicClientMissingName, }, wantError: http.StatusBadRequest, - }, - { + }, { req: adminschema.ClientCreateRequest{ Client: &adminPublicClientHasARedirect, }, wantError: http.StatusBadRequest, + }, { + req: adminschema.ClientCreateRequest{ + Client: &adminClientOwnID, + }, + want: adminschema.ClientCreateResponse{ + Client: addIDAndSecret(adminClientOwnID), + }, + wantClient: clientGoodOwnID, + }, { + req: adminschema.ClientCreateRequest{ + Client: &adminClientOwnSecret, + }, + want: adminschema.ClientCreateResponse{ + Client: addIDAndSecret(adminClientOwnSecret), + }, + wantClient: clientGoodOwnSecret, + }, { + req: adminschema.ClientCreateRequest{ + Client: &adminClientOwnIDAndSecret, + }, + want: adminschema.ClientCreateResponse{ + Client: addIDAndSecret(adminClientOwnIDAndSecret), + }, + wantClient: clientGoodOwnIDAndSecret, + }, { + req: adminschema.ClientCreateRequest{ + Client: &adminClientBadSecret, + }, + wantError: http.StatusBadRequest, }, }