diff --git a/admin/api.go b/admin/api.go
index 2b51672d6e95d5753079266e9304aa110c490a95..84e6c5aea70d2f94aecba6fba1295522e5a95c2b 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 55590995b540fc574e94ad7938ddf565c758915c..4105dba0be8c17918f04a2d0da2199e38ceaa6da 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 3fefff41ed85b885db381fa56fa45dc4e7b196f1..75ad7f30423bd02e2ef059114a68c6a57c14f9d5 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 4fc7ac305fb0bc015ea23535cad2d9c5e7211018..c1fa791683c464c504dbbd79eb857910e6073585 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,
 		},
 	}
 
diff --git a/schema/adminschema/README.md b/schema/adminschema/README.md
index 133df9baebc0b7aa315dde69e9055ce3f1e33aba..5025ecf09fb96a76ba8a95e5642590b5c4c656ff 100644
--- a/schema/adminschema/README.md
+++ b/schema/adminschema/README.md
@@ -28,14 +28,14 @@ __Version:__ v1
 {
     clientName: string // OPTIONAL for normal cliens. Name of the Client to be presented to the End-User. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ). REQUIRED for public clients,
     clientURI: string // OPTIONAL. URL of the home page of the Client. The value of this field MUST point to a valid Web page. If present, the server SHOULD display this URL to the End-User in a followable fashion. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
-    id: string // The client ID. Ignored in client create requests.,
+    id: string // The client ID. If specified in a client create request, it will be used as the ID. Otherwise, the server will choose the ID.,
     isAdmin: boolean,
     logoURI: string // OPTIONAL. URL that references a logo for the Client application. If present, the server SHOULD display this image to the End-User during approval. The value of this field MUST point to a valid image file. If desired, representation of this Claim in different languages and scripts is represented as described in Section 2.1 ( Metadata Languages and Scripts ) .,
-    public: boolean // OPTIONAL. Determines if the client is public. Public clients have certain restrictions: They cannot use their credentials to obtain a client JWT. Their redirects URLs cannot be specified: they are always http://localhost:$PORT or urn:ietf:wg:oauth:2.0:oob,
+    public: boolean // OPTIONAL. Determines if the client is public. Public clients have certain restrictions: They cannot use their credentials to obtain a client JWT. Their redirects URLs cannot be specified: they are always http://localhost:$PORT or urn:ietf:wg:oauth:2.0:oob.,
     redirectURIs: [
         string
     ],
-    secret: string // The client secret. Ignored in client create requests.,
+    secret: string // The client secret. If specified in a client create request, it will be used as the secret. Otherwise, the server will choose the secret. Must be a base64 URLEncoded string.,
     trustedPeers: [
         string
     ]
diff --git a/schema/adminschema/v1-gen.go b/schema/adminschema/v1-gen.go
index c441eae5771e3ca3d24b880b496f33c6516a99b3..4ebabd3345e010e415e22368408258e53b9c516b 100644
--- a/schema/adminschema/v1-gen.go
+++ b/schema/adminschema/v1-gen.go
@@ -125,7 +125,8 @@ type Client struct {
 	// Languages and Scripts ) .
 	ClientURI string `json:"clientURI,omitempty"`
 
-	// Id: The client ID. Ignored in client create requests.
+	// Id: The client ID. If specified in a client create request, it will
+	// be used as the ID. Otherwise, the server will choose the ID.
 	Id string `json:"id,omitempty"`
 
 	IsAdmin bool `json:"isAdmin,omitempty"`
@@ -141,7 +142,7 @@ type Client struct {
 	// Public: OPTIONAL. Determines if the client is public. Public clients
 	// have certain restrictions: They cannot use their credentials to
 	// obtain a client JWT. Their redirects URLs cannot be specified: they
-	// are always http://localhost:$PORT or urn:ietf:wg:oauth:2.0:oob
+	// are always http://localhost:$PORT or urn:ietf:wg:oauth:2.0:oob.
 	Public bool `json:"public,omitempty"`
 
 	// RedirectURIs: REQUIRED for normal clients. Array of Redirection URI
@@ -154,7 +155,9 @@ type Client struct {
 	// clients.
 	RedirectURIs []string `json:"redirectURIs,omitempty"`
 
-	// Secret: The client secret. Ignored in client create requests.
+	// Secret: The client secret. If specified in a client create request,
+	// it will be used as the secret. Otherwise, the server will choose the
+	// secret. Must be a base64 URLEncoded string.
 	Secret string `json:"secret,omitempty"`
 
 	// TrustedPeers: Array of ClientIDs of clients that are allowed to mint
diff --git a/schema/adminschema/v1-json.go b/schema/adminschema/v1-json.go
index acb2345d7d11e21a56488ef647d79b7965cd0010..c9a878c5d6500c5cf5929a2b6220e404fbd188c2 100644
--- a/schema/adminschema/v1-json.go
+++ b/schema/adminschema/v1-json.go
@@ -58,11 +58,11 @@ const DiscoveryJSON = `{
       "properties": {
         "id": {
           "type": "string",
-          "description": "The client ID. Ignored in client create requests."
+          "description": "The client ID. If specified in a client create request, it will be used as the ID. Otherwise, the server will choose the ID."
         },
         "secret": {
           "type": "string",
-          "description": "The client secret. Ignored in client create requests."
+          "description": "The client secret. If specified in a client create request, it will be used as the secret. Otherwise, the server will choose the secret. Must be a base64 URLEncoded string."
         },
         "isAdmin": {
           "type": "boolean"
@@ -95,7 +95,7 @@ const DiscoveryJSON = `{
         },
         "public": {
           "type": "boolean",
-          "description": "OPTIONAL. Determines if the client is public. Public clients have certain restrictions: They cannot use their credentials to obtain a client JWT. Their redirects URLs cannot be specified: they are always http://localhost:$PORT or urn:ietf:wg:oauth:2.0:oob"
+          "description": "OPTIONAL. Determines if the client is public. Public clients have certain restrictions: They cannot use their credentials to obtain a client JWT. Their redirects URLs cannot be specified: they are always http://localhost:$PORT or urn:ietf:wg:oauth:2.0:oob."
         }
       }
     },
diff --git a/schema/adminschema/v1.json b/schema/adminschema/v1.json
index 7ca75c1d4b0edbc24363b200c537620b5e3442c0..83fe76a172a7f3812ede7362e2ba0646fa552a8f 100644
--- a/schema/adminschema/v1.json
+++ b/schema/adminschema/v1.json
@@ -51,11 +51,11 @@
       "properties": {
         "id": {
           "type": "string",
-          "description": "The client ID. Ignored in client create requests."
+          "description": "The client ID. If specified in a client create request, it will be used as the ID. Otherwise, the server will choose the ID."
         },
         "secret": {
           "type": "string",
-          "description": "The client secret. Ignored in client create requests."
+          "description": "The client secret. If specified in a client create request, it will be used as the secret. Otherwise, the server will choose the secret. Must be a base64 URLEncoded string."
         },
         "isAdmin": {
           "type": "boolean"