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 for an identification with identity card or passport is the following:
iss(value =Opentech)iat(issueTime)exp(expiration)identificationIdJwt: an unique identifier of this AutomatID identificationcustomerIdentifier: the customer ID on the admin portalapplicationId: the application ID on the admin portalname: first name of the user (extracted from the MRZ, uppercase, normalized according to ICAO 9303 specifications)surname: last name of the user (extracted from the MRZ, uppercase, normalized according to ICAO 9303 specifications)dateOfBirth: formatYYMMDDgender:MALE|FEMALE|UNKNOWN|UNSPECIFIEDuserCountry: user nationality (ISO-3 country code)documentType:I(identity card) |P(passport) |U(unknown)documentNumber: the number of the documentdateOfExpiry: formatYYMMDDissuerCountry: document issuing country (ISO-3 country code)chipAuthSucceded: boolean, whether Chip Authentication (CA) was successful during the NFC data readingphotoMatchRequested: boolean, whether the liveness check was requestedphotoMatchingResult: float in [0, 1]: similarity score between the photo extracted from the NFC records and the photo obtained during the liveness check (0: no match, 1: exact match)documentPhoto: base64 of the user photo extracted from the NFC records, in JPG formattest: this is alwaysfalse
The structure of the JWT for an identification with card scan is the following:
iss(value =Opentech)iat(issueTime)exp(expiration)identification_id: an unique identifier of this AutomatID identificationcustomer_identifier: the customer ID on the admin portalinternal_application_id: the application ID on the admin portalcard_matched:trueif the read card was a valid card and it matched the provided input card data
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);
}