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 alwaysfalse
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);
}