Skip to main content

Managing successful identification

After the user has successfully performed an identification, the onSuccess method of the passed callback is invoked.

You will receive an object containing the kind of ID document that has been validated and a JWT signed by Opentech attesting the successful identification. The JWT may also be encrypted if you chose so during the project setup.

JWT validation on your backend

The JWT should be sent to your app's backend, where it should be decrypted (if required) and where its signature and validity should be verified.

The structure of the JWT is the following:

  • iss (value = Opentech)
  • iat (issueTime)
  • exp (expiration)
  • customer_identifier
  • application_id
  • card_matched
  • test: this is always false

In order to verify the JWT, you can refer to the following examples.

The code for JWT Validation received in the scan flow depends on the fact that you choose or not to encrypt the response or not.

In the following examples, jwtSerialized is assumed to contain the JWT that you obtained in the onSuccess() callback and that you sent on your server.

Obtain the AutomatID™ signing key

First of all, you need to download and extract the correct key.

SignedJWT signedJWT = SignedJWT.parse(jwtSerialized);
String keyID = signedJWT.getHeader().getKeyID();
RestTemplate restTemplate = new RestTemplate();
String url = "https://api.automat-id.com/.well-known/jwks.json";
ResponseEntity<JWKSRequest> forEntity = restTemplate.getForEntity(url, JWKSRequest.class);
List<JWKSModel> keys = forEntity.getBody().getKeys();
JWKSModel keySelected = keys.stream()
.filter(key -> org.apache.commons.lang3.StringUtils.equals(key.getKid(), keyID))
.findFirst()
.orElseThrow(RuntimeException::new);

JWKSRequest is defined as follows:

public class JWKSRequest {
private List<JWKSModel> keys;
// ... getters, setters and toString omitted for brevity
}

JWKSModel is defined as follows:

public class JWKSModel {
private String kty;
private String alg;
private String e;
private String use;
private String kid;
private String n;
// ... getters, setters and toString omitted for brevity
}

Then, obtain the public key from e and n:

RSAPublicKey verifySignaturePublicKey = getPublicKey(keySelected.getN(), keySelected.getE());

The method getPublicKey() can be implemented as follows:

private RSAPublicKey getPublicKey(String pubMod, String pubExp) {
try {
BigInteger m = new BigInteger(pubMod);
BigInteger e = new BigInteger(pubExp);
RSAPublicKeySpec rsaPublicKeySpec = new RSAPublicKeySpec(m, e);
final KeyFactory fact = KeyFactory.getInstance("RSA");
return (RSAPublicKey) fact.generatePublic(rsaPublicKeySpec);
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
}
}

Case 1: ID card/Passport - unencrypted response

SignedJWT signedJWT = SignedJWT.parse(jwtSerialized);
JWSVerifier jwsVerifier = new RSASSAVerifier(verifySignaturePublicKey);
signedJWT.verify(jwsVerifier);

Case 2: Payment card - unencrypted response

public boolean verifyJWTSigned(String jwtSerialized, RSAPublicKey verifySignaturePublicKey) throws Exception {
SignedJWT signedJWT = SignedJWT.parse(jwtSerialized);
JWSVerifier jwsVerifier = new RSASSAVerifier(verifySignaturePublicKey);
signedJWT.verify(jwsVerifier);
Map<String, Object> claims = signedJWT.getJWTClaimsSet().getClaims();
if (!StringUtils.equals((String) claims.get("application_id"), "YOUR_INTERNAL_APPLICATION_ID")) {
return false;
}
if (!StringUtils.equals((String) claims.get("customer_identifier"), "YOUR_CUSTOMER_IDENTIFIER")) {
return false;
}
if (new DateTime(claims.get("exp")).isBeforeNow()) {
return false;
}
if (!Boolean.FALSE.equals(claims.get("test")) {
return false;
}
Boolean cardMatched = (Boolean) claims.get("card_matched");
return Boolean.TRUE.equals(cardMatched);
}

Case 3: ID card/Passport - encrypted response

public void verifyJWTSignedAndEncrypted(String jwtSerialized, PrivateKey decryptionPrivateKey, RSAPublicKey verifySignPublicKey) throws Exception {
JWEObject jweObject = JWEObject.parse(jwtSerialized);
jweObject.decrypt(new RSADecrypter(decryptionPrivateKey));
JWSVerifier jwsVerifier = new RSASSAVerifier(verifySignPublicKey);
SignedJWT signedJWT = jweObject.getPayload().toSignedJWT();
signedJWT.verify(jwsVerifier);
}

Case 4: Payment card - encrypted response

public boolean verifyJWTSignedAndEncrypted(String jwtSerialized, PrivateKey decryptionPrivateKey, RSAPublicKey verifySignPublicKey) throws Exception {
JWEObject jweObject = JWEObject.parse(jwtSerialized);
jweObject.decrypt(new RSADecrypter(decryptionPrivateKey));
JWSVerifier jwsVerifier = new RSASSAVerifier(verifySignPublicKey);
SignedJWT signedJWT = jweObject.getPayload().toSignedJWT();
signedJWT.verify(jwsVerifier);
Map<String, Object> claims = signedJWT.getJWTClaimsSet().getClaims();
if (!StringUtils.equals((String) claims.get("application_id"), "YOUR_INTERNAL_APPLICATION_ID")) {
return false;
}
if (!StringUtils.equals((String) claims.get("customer_identifier"), "YOUR_CUSTOMER_IDENTIFIER")) {
return false;
}
if (new DateTime(claims.get("exp")).isBeforeNow()) {
return false;
}
if (!StringUtils.equals((String) claims.get("identification_id"),YOUR_IDENTIFICATION_ID) {
return false;
}
if (!Boolean.FALSE.equals(claims.get("test")) {
return false;
}
Boolean cardMatched = (Boolean) claims.get("card_matched");
return Boolean.TRUE.equals(cardMatched);
}