Connect your Java application to Identity as a Service (IDaaS) to receive and process account synchronization events. IDaaS pushes events to your application's HTTP endpoint as signed JSON Web Tokens (JWTs). Before processing any event, verify the signature to confirm the request is authentic — skipping this step lets attackers send forged events that trigger unauthorized account changes.
Complete one or two of the following steps to implement account synchronization:
Signature verification (required) — validates that the event was sent by IDaaS and has not been tampered with
Decryption (optional) — decrypts the event payload when Service Data Encryption is enabled
For details on how IDaaS pushes events and the overall call flow, see Overview.
Prerequisites
Before you begin, ensure that you have:
A Java application with a publicly reachable HTTP endpoint
Access to the synchronization configuration of your application in IDaaS
Maven configured in your project
Verify the signature
Every event that IDaaS sends is signed with an RSA private key. Your application verifies the signature using the corresponding public key, which is published at the public key endpoint in the application's synchronization configuration.
How signature verification works:
Retrieve the JSON Web Key Set (JWKS) from the public key endpoint.
Match the key ID (
kid) in the JWT header to a key in the JWKS.Verify the JWT signature using the matched RSA public key.
Validate JWT claims: issuer (
urn:alibaba:idaas:app:event), audience (your application ID), expiration time, and issue time.
The JWT consumer is configured with two time-window parameters:
setMaxFutureValidityInMinutes(1)— rejects tokens dated more than 1 minute in the future, guarding against clock manipulationsetAllowedClockSkewInSeconds(120)— allows up to 120 seconds of clock drift between IDaaS and your server
Step 1: Get the public key endpoint URL
In your IDaaS application's synchronization configuration, locate the public key endpoint URL.

For instructions on accessing the synchronization configuration, see Account synchronization - Synchronize IDaaS accounts on an application.
The endpoint returns a JWKS document in the following format:
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "KEY3PdQDx97********h83p8husNSC9AKMH",
"n": "rLUnH5PNeGUZE-********GGIxyM5O7TDdaG4********D9mV1CjE8hVHBxXM96IcCCH_1xmUZEZRp_MBP6m2XeNWUXanCpeyuIAD2kxmaQAqituZdIlT4l3-q9gtccdY-khaE-OfH9qYZhlxFcYj0gVtOvKZFIkuGhME4IQJd_RAWS3OPXxtbGhO2fZYCiuuc8NWub5mcVQnqsy5aJPLwHbVwVUwYNOmaq97_m2TtPcIVWtw7AOzX8O78UrYnYt_QPrv7uVdJMbHleSOx2A1IXqrAkJWecwFfvTsBTCUOPPDeVRQEHzzwmf3zpz5KMgHZU1I5pyqi0KJ6BuMHWw"
}
]
}Note: For non-Java languages, click the public key endpoint URL in the synchronization configuration to download the public key, then store it in a local .pem file.Step 2: Add the Maven dependency
Add the following dependency to your pom.xml:
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.9</version>
</dependency>Step 3: Add the JwtUtil utility class
Copy the following utility class into your project. It fetches the JWKS from the public key endpoint, caches it in memory, and builds a configured JWT consumer.
import org.apache.commons.codec.binary.StringUtils;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.JsonWebKeySet;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;
import org.jose4j.lang.JoseException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class JwtUtil {
private final static ConcurrentMap<String, JsonWebKeySet> IDAAS_SIGN_JWK_SET_MAP = new ConcurrentHashMap<>();
public static JwtConsumer createJwtConsumerFromUrl(String jwkUrl, String appId) {
try {
final JsonWebKeySet jsonWebKeySet = getJsonWebKeySetByUrl(jwkUrl);
return createJwtConsumer(jsonWebKeySet, appId);
} catch (Exception e) {
throw new RuntimeException("Fetch JWKs from url failed: " + e.getMessage() + ", " + jwkUrl, e);
}
}
public static JwtConsumer createJwtConsumer(JsonWebKeySet jsonWebKeySet, String appId) {
final JwtConsumerBuilder jwtConsumerBuilder = new JwtConsumerBuilder();
jwtConsumerBuilder.setExpectedIssuer("urn:alibaba:idaas:app:event");
jwtConsumerBuilder.setRequireExpirationTime();
jwtConsumerBuilder.setRequireJwtId();
jwtConsumerBuilder.setRequireIssuedAt();
jwtConsumerBuilder.setRequireExpirationTime();
// Reject tokens dated more than 1 minute in the future (prevents clock manipulation attacks)
jwtConsumerBuilder.setMaxFutureValidityInMinutes(1);
// Allow up to 120 seconds of clock drift between IDaaS and your server
jwtConsumerBuilder.setAllowedClockSkewInSeconds(120);
jwtConsumerBuilder.setExpectedAudience(appId);
jwtConsumerBuilder.setVerificationKeyResolver((jws, nestingContext) -> {
final String signKeyId = jws.getKeyIdHeaderValue();
for (JsonWebKey jsonWebKey : jsonWebKeySet.getJsonWebKeys()) {
if (StringUtils.equals(jsonWebKey.getKeyId(), signKeyId)) {
return jsonWebKey.getKey();
}
}
throw new RuntimeException("Cannot find verification key: " + signKeyId);
});
return jwtConsumerBuilder.build();
}
synchronized private static JsonWebKeySet getJsonWebKeySetByUrl(String jwkUrlString) throws IOException, JoseException {
JsonWebKeySet jsonWebKeySet = IDAAS_SIGN_JWK_SET_MAP.get(jwkUrlString);
if (jsonWebKeySet == null) {
jsonWebKeySet = innerGetJsonWebKeySetByUrl(jwkUrlString);
IDAAS_SIGN_JWK_SET_MAP.put(jwkUrlString, jsonWebKeySet);
}
return jsonWebKeySet;
}
private static JsonWebKeySet innerGetJsonWebKeySetByUrl(String jwkUrlString) throws IOException, JoseException {
final URL jwkUrl = new URL(jwkUrlString);
final URLConnection urlConnection = jwkUrl.openConnection();
urlConnection.setConnectTimeout(50000);
urlConnection.setReadTimeout(50000);
final String jwkSetJson = new String(readAll(urlConnection.getInputStream()), StandardCharsets.UTF_8);
return new JsonWebKeySet(jwkSetJson);
}
public static byte[] readAll(InputStream inputStream) throws IOException {
final byte[] buffer = new byte[1024 * 8];
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int len; ((len = inputStream.read(buffer)) != -1); ) {
baos.write(buffer, 0, len);
}
return baos.toByteArray();
}
}Step 4: Verify the event
Call JwtUtil in your event handler to verify the JWT and extract the payload:
// Public key — get the JWKS from the public key endpoint in the application's synchronization configuration
String publicKey = "{\n"
+ " \"keys\": [\n"
+ " {\n"
+ " \"kty\": \"RSA\",\n"
+ " \"e\": \"AQAB\",\n"
+ " \"use\": \"sig\",\n"
+ " \"kid\": \"KEYHH4yFa1c*******qNo1nJ7nM2FR3595P1\",\n"
+ " \"n\": \"oy_xxxxxxxxxxxxxxxxxxxxxxx95d1padSEABqIbcTKcnlTaET3WHaR"
+ "-3MvsooeZWluv94GQEp-U2jzM1adgTqBl_7KPjUk0dwrZbob_8pOLX5UQMF7Oo_nH5-H5EyL9-yGGhFA4oeuA"
+ "-b73qXShxP7eHs5xTT1kiYEu2NE3rBZdtrRwUiC_h1DvZMtyWFOPwm3dpLiwCcdlgcKvVuSEXyCBj6Gjevn3_G1guVQ2kHlNOVyNn6Ky1iGQJzXctJCEJ5fnBRs4XZZbPNSciYMD2-__cRdbYPtGyyuoEAfouw\"\n"
+ " }\n"
+ " ]\n"
+ "}";;
// Application ID — find the ID of your application in the application list
String appId = "app_mjavzivahje6zxxxx";
// Build a JWT consumer using the JWKS and application ID
JwtConsumer jwtConsumer = JwtUtil.createJwtConsumer(new JsonWebKeySet(publicKey), appId);
// Verify the signature and extract the payload
// Pass the raw value of the event parameter received by your endpoint
JwtClaims jwtClaims = jwtConsumer.processToClaims("The value of the event parameter");
// Access the payload data
Map<String, Object> map = jwtClaims.getClaimsMap();
// Process your business logic using the event dataDecrypt the event payload (optional)
If Service Data Encryption is enabled for your application, IDaaS encrypts the event data using AES (Advanced Encryption Standard) before sending it. The encrypted data is transmitted in the cipher_data field of the event payload. Decrypt cipher_data to get the original event data.
Step 1: Get the encryption key
In your IDaaS application settings, either enter an encryption key or click Generate Key to create one.

Copy the key — you will use it in the decryption code.
Step 2: Add the Maven dependencies
Add both dependencies to your pom.xml:
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.9</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
</dependency>Step 3: Decrypt cipher_data
The following method decrypts the cipher_data value using JSON Web Encryption (JWE) with AES-GCM:
public String decrypte(String cipherData, String key) throws JoseException {
String alg = "AES";
// Build the AES key from the hex-encoded encryption key
SecretKeySpec secretKeySpec = new SecretKeySpec(Hex.decode(key), alg);
JsonWebKey jsonWebKey = JsonWebKey.Factory.newJwk(secretKeySpec);
JsonWebEncryption receiverJwe = new JsonWebEncryption();
// Restrict the allowed key agreement algorithm to "dir" (direct encryption)
AlgorithmConstraints algConstraints = new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.PERMIT, new String[]{"dir"});
receiverJwe.setAlgorithmConstraints(algConstraints);
// Restrict the allowed content encryption algorithms to AES-GCM variants
AlgorithmConstraints encConstraints = new AlgorithmConstraints(
AlgorithmConstraints.ConstraintType.PERMIT, new String[]{"A256GCM", "A192GCM", "A128GCM"});
receiverJwe.setContentEncryptionAlgorithmConstraints(encConstraints);
// Set the decryption key and the ciphertext
receiverJwe.setKey(jsonWebKey.getKey());
receiverJwe.setCompactSerialization(cipherData);
// Return the decrypted event data as a UTF-8 string
return new String(receiverJwe.getPlaintextBytes(), StandardCharsets.UTF_8);
}What's next
Review the event payload schema to understand the account synchronization event fields
Overview — understand how IDaaS pushes events and the overall provisioning architecture