From aba2257f6efc130f7b59a4090e85bf0b238731a2 Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Mon, 24 Apr 2023 10:07:43 -0500 Subject: [PATCH] [SDK-3864] - Add support for client credential management (#525) --- .../com/auth0/client/mgmt/ClientsEntity.java | 83 ++++++++ .../com/auth0/json/mgmt/client/Client.java | 10 + .../client/ClientAuthenticationMethods.java | 28 +++ .../auth0/json/mgmt/client/Credential.java | 191 ++++++++++++++++++ .../auth0/json/mgmt/client/PrivateKeyJwt.java | 38 ++++ .../java/com/auth0/client/MockServer.java | 2 + .../auth0/client/mgmt/ClientsEntityTest.java | 117 +++++++++++ .../auth0/json/mgmt/client/ClientTest.java | 99 ++++++++- .../json/mgmt/client/CredentialTest.java | 63 ++++++ src/test/resources/mgmt/client.json | 9 +- .../resources/mgmt/client_credential.json | 11 + .../mgmt/client_credential_list.json | 13 ++ 12 files changed, 662 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/auth0/json/mgmt/client/ClientAuthenticationMethods.java create mode 100644 src/main/java/com/auth0/json/mgmt/client/Credential.java create mode 100644 src/main/java/com/auth0/json/mgmt/client/PrivateKeyJwt.java create mode 100644 src/test/java/com/auth0/json/mgmt/client/CredentialTest.java create mode 100644 src/test/resources/mgmt/client_credential.json create mode 100644 src/test/resources/mgmt/client_credential_list.json diff --git a/src/main/java/com/auth0/client/mgmt/ClientsEntity.java b/src/main/java/com/auth0/client/mgmt/ClientsEntity.java index 4fa4be3e..06bbf123 100644 --- a/src/main/java/com/auth0/client/mgmt/ClientsEntity.java +++ b/src/main/java/com/auth0/client/mgmt/ClientsEntity.java @@ -4,6 +4,7 @@ import com.auth0.client.mgmt.filter.FieldsFilter; import com.auth0.json.mgmt.client.Client; import com.auth0.json.mgmt.client.ClientsPage; +import com.auth0.json.mgmt.client.Credential; import com.auth0.net.EmptyBodyRequest; import com.auth0.net.BaseRequest; import com.auth0.net.Request; @@ -182,4 +183,86 @@ public Request rotateSecret(String clientId) { return new EmptyBodyRequest<>(this.client, tokenProvider, url, HttpMethod.POST, new TypeReference() { }); } + + /** + * Creates an Application's client credential. A token with scope {@code create:client_credentials} is required. + * + * @param clientId the application's client id. + * @param credential the credential to create. + * @return a Request to execute. + */ + public Request createCredential(String clientId, Credential credential) { + Asserts.assertNotNull(clientId, "client id"); + + String url = baseUrl + .newBuilder() + .addPathSegments("api/v2/clients") + .addPathSegment(clientId) + .addPathSegment("credentials") + .build() + .toString(); + BaseRequest request = new BaseRequest<>(this.client, tokenProvider, url, HttpMethod.POST, new TypeReference() { + }); + request.setBody(credential); + return request; + } + + /** + * Get the client credentials associated with this application. A token with scope {@code read:client_credentials} is required. + * @param clientId the ID of the application + * @return a request to execute. + */ + public Request> listCredentials(String clientId) { + Asserts.assertNotNull(clientId, "client id"); + String url = baseUrl + .newBuilder() + .addPathSegments("api/v2/clients") + .addPathSegment(clientId) + .addPathSegment("credentials").build().toString(); + return new BaseRequest<>(client, tokenProvider, url, HttpMethod.GET, new TypeReference>() { + }); + } + + /** + * Get a client credentials object. A token with scope {@code read:client_credentials} is required. + * @param clientId the ID of the application. + * @param credentialId the ID of the credential to retrieve. + * @return a request to execute. + */ + public Request getCredential(String clientId, String credentialId) { + Asserts.assertNotNull(clientId, "client id"); + Asserts.assertNotNull(credentialId, "credential id"); + + String url = baseUrl + .newBuilder() + .addPathSegments("api/v2/clients") + .addPathSegment(clientId) + .addPathSegment("credentials") + .addPathSegment(credentialId) + .build().toString(); + + return new BaseRequest<>(client, tokenProvider, url, HttpMethod.GET, new TypeReference() { + }); + } + + /** + * Deletes a client credential. A token with scope {@code } is required. + * @param clientId the ID of the application. + * @param credentialId the ID of the credential to delete + * @return a request to execute. + */ + public Request deleteCredential(String clientId, String credentialId) { + Asserts.assertNotNull(clientId, "client id"); + Asserts.assertNotNull(credentialId, "credential id"); + + String url = baseUrl + .newBuilder() + .addPathSegments("api/v2/clients") + .addPathSegment(clientId) + .addPathSegment("credentials") + .addPathSegment(credentialId) + .build() + .toString(); + return new VoidRequest(client, tokenProvider, url, HttpMethod.DELETE); + } } diff --git a/src/main/java/com/auth0/json/mgmt/client/Client.java b/src/main/java/com/auth0/json/mgmt/client/Client.java index c153a435..cc6c9880 100644 --- a/src/main/java/com/auth0/json/mgmt/client/Client.java +++ b/src/main/java/com/auth0/json/mgmt/client/Client.java @@ -90,6 +90,8 @@ public class Client { private Boolean crossOriginAuth; @JsonProperty("cross_origin_loc") private String crossOriginLoc; + @JsonProperty("client_authentication_methods") + private ClientAuthenticationMethods clientAuthenticationMethods; /** * Getter for the name of the tenant this client belongs to. @@ -793,5 +795,13 @@ public void setCrossOriginLoc(String crossOriginLoc) { public String getCrossOriginLoc() { return crossOriginLoc; } + + public void setClientAuthenticationMethods(ClientAuthenticationMethods clientAuthenticationMethods) { + this.clientAuthenticationMethods = clientAuthenticationMethods; + } + + public ClientAuthenticationMethods getClientAuthenticationMethods() { + return clientAuthenticationMethods; + } } diff --git a/src/main/java/com/auth0/json/mgmt/client/ClientAuthenticationMethods.java b/src/main/java/com/auth0/json/mgmt/client/ClientAuthenticationMethods.java new file mode 100644 index 00000000..c42f93d6 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/client/ClientAuthenticationMethods.java @@ -0,0 +1,28 @@ +package com.auth0.json.mgmt.client; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Class that represents an Auth0 Application authentication methods. Related to the {@link com.auth0.client.mgmt.ClientsEntity} entity. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ClientAuthenticationMethods { + + @JsonProperty("private_key_jwt") + private PrivateKeyJwt privateKeyJwt; + + public ClientAuthenticationMethods() { + + } + + public ClientAuthenticationMethods(PrivateKeyJwt privateKeyJwt) { + this.privateKeyJwt = privateKeyJwt; + } + + public PrivateKeyJwt getPrivateKeyJwt() { + return privateKeyJwt; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/client/Credential.java b/src/main/java/com/auth0/json/mgmt/client/Credential.java new file mode 100644 index 00000000..e28b39ea --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/client/Credential.java @@ -0,0 +1,191 @@ +package com.auth0.json.mgmt.client; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Date; + +/** + * Class that represents an Auth0 application credential object. Related to the {@link com.auth0.client.mgmt.ClientsEntity} entity. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Credential { + + @JsonProperty("credential_type") + private String credentialType; + @JsonProperty("name") + private String name; + @JsonProperty("pem") + private String pem; + + @JsonProperty("id") + private String id; + @JsonProperty("kid") + private String kid; + @JsonProperty("thumbprint") + private String thumbprint; + @JsonProperty("alg") + private String alg; + @JsonProperty("parse_expiry_from_cert") + private Boolean parseExpiryFromCert; + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonProperty("created_at") + private Date createdAt; + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonProperty("updated_at") + private Date updatedAt; + @JsonFormat(shape = JsonFormat.Shape.STRING) + @JsonProperty("expires_at") + private Date expiresAt; + + /** + * Create a new credential + * @param credentialType the credential type + * @param pem the PEM + */ + public Credential(String credentialType, String pem) { + this.credentialType = credentialType; + this.pem = pem; + } + + /** + * Create a new credential + * @param id the ID of the credential + */ + public Credential(String id) { + this.id = id; + } + + /** + * Create a new credential + */ + public Credential() {} + + /** + * @return the credential type + */ + public String getCredentialType() { + return credentialType; + } + + /** + * Sets the credential type + * @param credentialType the credential type + */ + public void setCredentialType(String credentialType) { + this.credentialType = credentialType; + } + + /** + * @return the credential name + */ + public String getName() { + return name; + } + + /** + * Sets the credential name + * @param name the name of the credential + */ + public void setName(String name) { + this.name = name; + } + + /** + * @return the credential's PEM + */ + public String getPem() { + return pem; + } + + /** + * Sets the credential's PEM + * @param pem the PEM of the credential + */ + public void setPem(String pem) { + this.pem = pem; + } + + /** + * @return the ID of the credential + */ + public String getId() { + return id; + } + + /** + * @return the KID of the credential + */ + public String getKid() { + return kid; + } + + /** + * @return the thumbprint of the credential + */ + public String getThumbprint() { + return thumbprint; + } + + /** + * @return the date the credential was created at + */ + public Date getCreatedAt() { + return createdAt; + } + + /** + * @return the algorithm of this credential + */ + public String getAlg() { + return alg; + } + + /** + * Set the algorithm + * @param alg the algorithm + */ + public void setAlg(String alg) { + this.alg = alg; + } + + /** + * @return the time this credential was last updated + */ + public Date getUpdatedAt() { + return updatedAt; + } + + /** + * @return the expiration time of this credential + */ + public Date getExpiresAt() { + return expiresAt; + } + + /** + * Set the expires_at value for this credential + * @param expiresAt the time this credential should expire + */ + public void setExpiresAt(Date expiresAt) { + this.expiresAt = expiresAt; + } + + /** + * @return whether the expiry will be parsed from the x509 certificate + */ + public Boolean getParseExpiryFromCert() { + return parseExpiryFromCert; + } + + /** + * Whether to parse expiry from x509 certificate + * @param parseExpiryFromCert true to parse expiry; false otherwise. + */ + public void setParseExpiryFromCert(Boolean parseExpiryFromCert) { + this.parseExpiryFromCert = parseExpiryFromCert; + } +} diff --git a/src/main/java/com/auth0/json/mgmt/client/PrivateKeyJwt.java b/src/main/java/com/auth0/json/mgmt/client/PrivateKeyJwt.java new file mode 100644 index 00000000..1cb674ce --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/client/PrivateKeyJwt.java @@ -0,0 +1,38 @@ +package com.auth0.json.mgmt.client; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * Class that represents an Auth0 Application private key JWT authentication method. Related to the {@link com.auth0.client.mgmt.ClientsEntity} entity. + */ +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class PrivateKeyJwt { + + @JsonProperty("credentials") + private List credentials; + + /** + * Create a new instance + */ + public PrivateKeyJwt() {} + + /** + * Create a new instance + * @param credentials the credentials to use + */ + public PrivateKeyJwt(List credentials) { + this.credentials = credentials; + } + + /** + * @return the credentials + */ + public List getCredentials() { + return credentials; + } +} diff --git a/src/test/java/com/auth0/client/MockServer.java b/src/test/java/com/auth0/client/MockServer.java index d4d40a69..881602ba 100644 --- a/src/test/java/com/auth0/client/MockServer.java +++ b/src/test/java/com/auth0/client/MockServer.java @@ -39,6 +39,8 @@ public class MockServer { public static final String MGMT_CLIENTS_LIST = "src/test/resources/mgmt/clients_list.json"; public static final String MGMT_CLIENTS_PAGED_LIST = "src/test/resources/mgmt/clients_paged_list.json"; public static final String MGMT_CLIENT = "src/test/resources/mgmt/client.json"; + public static final String MGMT_CLIENT_CREDENTIAL = "src/test/resources/mgmt/client_credential.json"; + public static final String MGMT_CLIENT_CREDENTIAL_LIST = "src/test/resources/mgmt/client_credential_list.json"; public static final String MGMT_CONNECTIONS_LIST = "src/test/resources/mgmt/connections_list.json"; public static final String MGMT_CONNECTIONS_PAGED_LIST = "src/test/resources/mgmt/connections_paged_list.json"; public static final String MGMT_CONNECTION = "src/test/resources/mgmt/connection.json"; diff --git a/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java b/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java index 752da1eb..fd6ba719 100644 --- a/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java +++ b/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java @@ -4,6 +4,7 @@ import com.auth0.client.mgmt.filter.FieldsFilter; import com.auth0.json.mgmt.client.Client; import com.auth0.json.mgmt.client.ClientsPage; +import com.auth0.json.mgmt.client.Credential; import com.auth0.net.Request; import com.auth0.net.client.HttpMethod; import okhttp3.mockwebserver.RecordedRequest; @@ -289,4 +290,120 @@ public void shouldRotateClientSecret() throws Exception { assertThat(response, is(notNullValue())); } + @Test + public void shouldListClientCredentials() throws Exception { + Request> request = api.clients().listCredentials("clientId"); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_CLIENT_CREDENTIAL_LIST, 200); + List response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/clients/clientId/credentials")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + + assertThat(response, is(notNullValue())); + } + + @Test + public void shouldThrowOnListCredentialsWithNullClientId() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'client id' cannot be null!"); + api.clients().listCredentials(null); + } + + @Test + public void shouldGetClientCredential() throws Exception { + Request request = api.clients().getCredential("clientId", "credId"); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_CLIENT_CREDENTIAL, 200); + Credential response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/clients/clientId/credentials/credId")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + + assertThat(response, is(notNullValue())); + } + + @Test + public void shouldThrowOnGetCredentialsWithNullClientId() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'client id' cannot be null!"); + api.clients().getCredential(null, "credId"); + } + + @Test + public void shouldThrowOnGetCredentialsWithNullCredentialId() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'credential id' cannot be null!"); + api.clients().getCredential("clientId", null); + } + + @Test + public void shouldCreateClientCredential() throws Exception { + Credential credential = new Credential("public_key", "pem"); + Request request = api.clients().createCredential("clientId", credential); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_CLIENT_CREDENTIAL, 201); + Credential response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.POST, "/api/v2/clients/clientId/credentials")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + + Map body = bodyFromRequest(recordedRequest); + assertThat(body.size(), is(2)); + assertThat(body, hasEntry("credential_type", "public_key")); + assertThat(body, hasEntry("pem", "pem")); + + assertThat(response, is(notNullValue())); + } + + @Test + public void shouldThrowOnCreateCredentialsWithNullClientId() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'client id' cannot be null!"); + api.clients().createCredential(null, new Credential()); + } + + @Test + public void shouldThrowOnCreateCredentialsWithNullCredentialId() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'credential id' cannot be null!"); + api.clients().deleteCredential("clientId", null); + } + + @Test + public void shouldDeleteCredential() throws Exception { + Request request = api.clients().deleteCredential("clientId", "credId"); + assertThat(request, is(notNullValue())); + + server.emptyResponse(204); + request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.DELETE, "/api/v2/clients/clientId/credentials/credId")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + } + + @Test + public void shouldThrowOnDeleteCredentialsWithNullClientId() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'client id' cannot be null!"); + api.clients().deleteCredential(null, "credId"); + } + + @Test + public void shouldThrowOnDeleteCredentialsWithNullCredentialId() { + exception.expect(IllegalArgumentException.class); + exception.expectMessage("'credential id' cannot be null!"); + api.clients().deleteCredential("clientId", null); + } } diff --git a/src/test/java/com/auth0/json/mgmt/client/ClientTest.java b/src/test/java/com/auth0/json/mgmt/client/ClientTest.java index b46155a1..1482a722 100644 --- a/src/test/java/com/auth0/json/mgmt/client/ClientTest.java +++ b/src/test/java/com/auth0/json/mgmt/client/ClientTest.java @@ -15,7 +15,91 @@ public class ClientTest extends JsonTest { private static final String readOnlyJson = "{\"client_id\":\"clientId\",\"is_heroku_app\":true,\"signing_keys\":[{\"cert\":\"ce\",\"pkcs7\":\"pk\",\"subject\":\"su\"}]}"; - private static final String json = "{\"name\":\"name\",\"description\":\"description\",\"client_secret\":\"secret\",\"app_type\":\"type\",\"logo_uri\":\"uri\",\"oidc_conformant\":true,\"is_first_party\":true,\"initiate_login_uri\":\"https://myhome.com/login\",\"callbacks\":[\"value\"],\"allowed_origins\":[\"value\"],\"web_origins\":[\"value\"],\"grant_types\":[\"value\"],\"client_aliases\":[\"value\"],\"allowed_clients\":[\"value\"],\"allowed_logout_urls\":[\"value\"],\"organization_usage\":\"allow\",\"organization_require_behavior\":\"no_prompt\",\"jwt_configuration\":{\"lifetime_in_seconds\":100,\"scopes\":\"openid\",\"alg\":\"alg\"},\"encryption_key\":{\"pub\":\"pub\",\"cert\":\"cert\"},\"sso\":true,\"sso_disabled\":true,\"custom_login_page_on\":true,\"custom_login_page\":\"custom\",\"custom_login_page_preview\":\"preview\",\"form_template\":\"template\",\"addons\":{\"rms\":{},\"mscrm\":{},\"slack\":{},\"layer\":{}},\"token_endpoint_auth_method\":\"method\",\"client_metadata\":{\"key\":\"value\"},\"mobile\":{\"android\":{\"app_package_name\":\"pkg\",\"sha256_cert_fingerprints\":[\"256\"]},\"ios\":{\"team_id\":\"team\",\"app_bundle_identifier\":\"id\"}},\"refresh_token\":{\"rotation_type\":\"non-rotating\"}}"; + private static final String json = "{\n" + + " \"name\": \"name\",\n" + + " \"description\": \"description\",\n" + + " \"client_secret\": \"secret\",\n" + + " \"app_type\": \"type\",\n" + + " \"logo_uri\": \"uri\",\n" + + " \"oidc_conformant\": true,\n" + + " \"is_first_party\": true,\n" + + " \"initiate_login_uri\": \"https://myhome.com/login\",\n" + + " \"callbacks\": [\n" + + " \"value\"\n" + + " ],\n" + + " \"allowed_origins\": [\n" + + " \"value\"\n" + + " ],\n" + + " \"web_origins\": [\n" + + " \"value\"\n" + + " ],\n" + + " \"grant_types\": [\n" + + " \"value\"\n" + + " ],\n" + + " \"client_aliases\": [\n" + + " \"value\"\n" + + " ],\n" + + " \"allowed_clients\": [\n" + + " \"value\"\n" + + " ],\n" + + " \"allowed_logout_urls\": [\n" + + " \"value\"\n" + + " ],\n" + + " \"organization_usage\": \"allow\",\n" + + " \"organization_require_behavior\": \"no_prompt\",\n" + + " \"jwt_configuration\": {\n" + + " \"lifetime_in_seconds\": 100,\n" + + " \"scopes\": \"openid\",\n" + + " \"alg\": \"alg\"\n" + + " },\n" + + " \"encryption_key\": {\n" + + " \"pub\": \"pub\",\n" + + " \"cert\": \"cert\"\n" + + " },\n" + + " \"sso\": true,\n" + + " \"sso_disabled\": true,\n" + + " \"custom_login_page_on\": true,\n" + + " \"custom_login_page\": \"custom\",\n" + + " \"custom_login_page_preview\": \"preview\",\n" + + " \"form_template\": \"template\",\n" + + " \"addons\": {\n" + + " \"rms\": {},\n" + + " \"mscrm\": {},\n" + + " \"slack\": {},\n" + + " \"layer\": {}\n" + + " },\n" + + " \"token_endpoint_auth_method\": \"method\",\n" + + " \"client_metadata\": {\n" + + " \"key\": \"value\"\n" + + " },\n" + + " \"mobile\": {\n" + + " \"android\": {\n" + + " \"app_package_name\": \"pkg\",\n" + + " \"sha256_cert_fingerprints\": [\n" + + " \"256\"\n" + + " ]\n" + + " },\n" + + " \"ios\": {\n" + + " \"team_id\": \"team\",\n" + + " \"app_bundle_identifier\": \"id\"\n" + + " }\n" + + " },\n" + + " \"refresh_token\": {\n" + + " \"rotation_type\": \"non-rotating\"\n" + + " },\n" + + " \"client_authentication_methods\": {\n" + + " \"private_key_jwt\": {\n" + + " \"credentials\": [\n" + + " {\n" + + " \"id\": \"cred_abc\"\n" + + " },\n" + + " {\n" + + " \"id\": \"cred_123\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " }\n" + + "}"; @Test public void shouldSerialize() throws Exception { @@ -58,6 +142,10 @@ public void shouldSerialize() throws Exception { client.setRefreshToken(refreshToken); client.setOrganizationUsage("require"); client.setOrganizationRequireBehavior("pre_login_prompt"); + Credential credential = new Credential("public_key", "PEM"); + PrivateKeyJwt privateKeyJwt = new PrivateKeyJwt(Collections.singletonList(credential)); + ClientAuthenticationMethods cam = new ClientAuthenticationMethods(privateKeyJwt); + client.setClientAuthenticationMethods(cam); String serialized = toJSON(client); assertThat(serialized, is(notNullValue())); @@ -91,6 +179,8 @@ public void shouldSerialize() throws Exception { assertThat(serialized, JsonMatcher.hasEntry("refresh_token", notNullValue())); assertThat(serialized, JsonMatcher.hasEntry("organization_usage", "require")); assertThat(serialized, JsonMatcher.hasEntry("organization_require_behavior", "pre_login_prompt")); + assertThat(serialized, JsonMatcher.hasEntry("client_authentication_methods", notNullValue())); + assertThat(serialized, JsonMatcher.hasEntry("client_authentication_methods", containsString("{\"private_key_jwt\":{\"credentials\":[{\"credential_type\":\"public_key\",\"pem\":\"PEM\"}]}}"))); } @Test @@ -134,6 +224,13 @@ public void shouldDeserialize() throws Exception { assertThat(client.getRefreshToken(), is(notNullValue())); assertThat(client.getOrganizationUsage(), is("allow")); assertThat(client.getOrganizationRequireBehavior(), is("no_prompt")); + + assertThat(client.getClientAuthenticationMethods(), is(notNullValue())); + assertThat(client.getClientAuthenticationMethods().getPrivateKeyJwt(), is(notNullValue())); + assertThat(client.getClientAuthenticationMethods().getPrivateKeyJwt().getCredentials(), is(notNullValue())); + assertThat(client.getClientAuthenticationMethods().getPrivateKeyJwt().getCredentials().size(), is(2)); + assertThat(client.getClientAuthenticationMethods().getPrivateKeyJwt().getCredentials().get(0).getId(), is("cred_abc")); + assertThat(client.getClientAuthenticationMethods().getPrivateKeyJwt().getCredentials().get(1).getId(), is("cred_123")); } @Test diff --git a/src/test/java/com/auth0/json/mgmt/client/CredentialTest.java b/src/test/java/com/auth0/json/mgmt/client/CredentialTest.java new file mode 100644 index 00000000..64daf639 --- /dev/null +++ b/src/test/java/com/auth0/json/mgmt/client/CredentialTest.java @@ -0,0 +1,63 @@ +package com.auth0.json.mgmt.client; + +import com.auth0.json.JsonMatcher; +import com.auth0.json.JsonTest; +import org.junit.Test; + +import java.time.Instant; +import java.util.Date; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +public class CredentialTest extends JsonTest { + + private final static String JSON = "{\n" + + " \"id\": \"cred_asqwXXXIIadqq2T3zAXPwv\",\n" + + " \"name\": \"new cred\",\n" + + " \"credential_type\": \"public_key\",\n" + + " \"kid\": \"eKtBStP7BN2NiaoVrNGzb0QaRl4HlzyH1Lp00JB6Xj0\",\n" + + " \"alg\": \"RS256\",\n" + + " \"thumbprint\": \"thumb\",\n" + + " \"created_at\": \"2023-04-13T16:18:01.481Z\",\n" + + " \"updated_at\": \"2023-04-13T16:18:01.481Z\",\n" + + " \"expires_at\": \"2023-04-13T16:19:00.349Z\"\n" + + "}\n"; + + @Test + public void shouldDeserialize() throws Exception { + Credential credential = fromJSON(JSON, Credential.class); + + assertThat(credential, is(notNullValue())); + assertThat(credential.getAlg(), is("RS256")); + assertThat(credential.getCredentialType(), is("public_key")); + assertThat(credential.getExpiresAt(), is(Date.from(Instant.parse("2023-04-13T16:19:00.349Z")))); + assertThat(credential.getUpdatedAt(), is(Date.from(Instant.parse("2023-04-13T16:18:01.481Z")))); + assertThat(credential.getCreatedAt(), is(Date.from(Instant.parse("2023-04-13T16:18:01.481Z")))); + assertThat(credential.getKid(), is("eKtBStP7BN2NiaoVrNGzb0QaRl4HlzyH1Lp00JB6Xj0")); + assertThat(credential.getId(), is("cred_asqwXXXIIadqq2T3zAXPwv")); + assertThat(credential.getThumbprint(), is("thumb")); + } + + @Test + public void shouldSerialize() throws Exception { + Credential credential = new Credential("id"); + credential.setCredentialType("public_key"); + credential.setPem("pem"); + credential.setAlg("alg"); + credential.setName("name"); + credential.setParseExpiryFromCert(true); + credential.setExpiresAt(new Date()); + + String json = toJSON(credential); + assertThat(json, is(notNullValue())); + + assertThat(json, JsonMatcher.hasEntry("name", "name")); + assertThat(json, JsonMatcher.hasEntry("alg", "alg")); + assertThat(json, JsonMatcher.hasEntry("parse_expiry_from_cert", true)); + assertThat(json, JsonMatcher.hasEntry("pem", "pem")); + assertThat(json, JsonMatcher.hasEntry("credential_type", "public_key")); + assertThat(json, JsonMatcher.hasEntry("expires_at", is(notNullValue()))); + } +} diff --git a/src/test/resources/mgmt/client.json b/src/test/resources/mgmt/client.json index 54307b15..70fd3095 100644 --- a/src/test/resources/mgmt/client.json +++ b/src/test/resources/mgmt/client.json @@ -59,5 +59,12 @@ "team_id": "9JA89QQLNQ", "app_bundle_identifier": "com.my.bundle.id" } + }, + "client_authentication_methods": { + "private_key_jwt": { + "credentials": [ { + "id": "cred_abc" + }] + } } -} \ No newline at end of file +} diff --git a/src/test/resources/mgmt/client_credential.json b/src/test/resources/mgmt/client_credential.json new file mode 100644 index 00000000..fa0f0fc2 --- /dev/null +++ b/src/test/resources/mgmt/client_credential.json @@ -0,0 +1,11 @@ +{ + "id": "cred_asqwXXXIIadqq2T3zAXPwv", + "name": "new cred", + "credential_type": "public_key", + "kid": "eKtBStP7BN2NiaoVrNGzb0QaRl4HlzyH1Lp00JB6Xj0", + "alg": "RS256", + "thumbprint": "thumb", + "created_at": "2023-04-13T16:18:01.481Z", + "updated_at": "2023-04-13T16:18:01.481Z", + "expires_at": "2023-04-13T16:19:00.349Z" +} diff --git a/src/test/resources/mgmt/client_credential_list.json b/src/test/resources/mgmt/client_credential_list.json new file mode 100644 index 00000000..59fa510d --- /dev/null +++ b/src/test/resources/mgmt/client_credential_list.json @@ -0,0 +1,13 @@ +[ + { + "id": "cred_asqwdTaebdqq2T3zAXPwv", + "name": "new cred", + "credential_type": "public_key", + "kid": "eKtBStP7BN2NV1MVrNGzb0QaRp0uHlzyH1Lje9JB6Xj0", + "alg": "RS256", + "thumbprint": "thumb", + "created_at": "2023-04-13T16:18:01.481Z", + "updated_at": "2023-04-13T16:18:01.481Z", + "expires_at": "2023-04-13T16:19:00.349Z" + } +]