Validate JWT token
Validate Age Estimation Token
The Age Estimation Token is a JWT token that contains the estimated age of the user.
The token is either signed, or signed and encrypted by the PRIVO ID Service. If the JWT is both signed and encrypted, the JSON document will be signed then encrypted, with the result being a Nested JWT, as defined in [RFC7519].
The subject claim (sub
) contains a JSON encoded array with a few successful attempts of the estimated user age.
Example of the Age Estimation Token:
Encoded token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImlkMSJ9.eyJzdWIiOiJbMzksNDAsMzksMzgsMzVdIiwiYXpwIjoicHJpdm9sb2NrIiwiYXVkIjoiQWdlRXN0aW1hdGlvbiIsImV4cCI6MTcxMDg3MDAwMSwiaWF0IjoxNzEwODY5MTAxLCJpc3MiOiJodHRwczovL2lkLXN2Yy1pbnQucHJpdm8uY29tIiwianRpIjoiZmQwOTMyYjktYzI4NS00N2NkLTliYmEtMjkwYmI1M2M5MDI0In0.T6Nat3VVkmP9X_TQ9HX08YQTRVsf_phA_KserAscuzYg25SahEANogwSriIStJ8KCiLkqsErXa77jABB65ydyINtIOJOTBNj-ks_JZsdGl9s75hrzbkZ4MfFy7_U_gL7H0NmMzrJWpUU4cNXI9oKdanZsVwIstHPE-GHc7TnwnxHffmPL7vccL_GcnmSYGWI2PRPK5Lgn7ficQZ3avJKZYEGY-vnrEYlwglEtBOcrfxow2l-8T0Oieuylrmf5LVFjSj7v8HDfEx0iU3B1i9ffZsVtCLpVwFyRgHoSiwbs0wXdtcMD_uOAVXBhMfclNBJV4uDR-_hF4kI7pnsRu0IlQ
Decoded header:
{
"typ": "JWT",
"alg": "RS256",
"kid": "id1"
}
Decoded payload:
{
"sub": "[28,29,29,31,30]", // Estimated age, each number represents a different attempt
"azp": "service_identifier", // Service Identifier provided by PRIVO during integration
"aud": "AgeEstimation", // PRIVO product ID
"exp": 1710837509, // Expiration time
"iat": 1710836609, // Issue time
"iss": "https://id-svc-int.privo.com", // Issuer
"jti": "361140de-b74f-45eb-bc06-c51a209862b2" // JWT ID
}
To verify that the Age Estimation Token is valid, ensure that the following criteria are satisfied:
- The Age Estimation Token is properly signed by the PRIVO ID Service. Use the ID Service's public keys available in JWK format in INT env at
https://id-svc-int.privo.com/.well_known/jwks
to verify the token's signature. Please note that public keys depend on Enviroment (INT/PROD) that you are usign. PROD version -https://id-svc.privo.com/.well_known/jwks
. - The value of the authorized party (
azp
) claim must be equal to your Service Identifier (provided by PRIVO). - The value of the audience claim (
aud
) must be equal to one of the PRIVO product IDs (AgeGate, AgeVerification, AgeEstimation, etc) that you integrate. - The value of the issuer (
iss
) claim must be equal tohttps://id-svc.privo.com
on production enviroment orhttps://id-svc-int.privo.com
on integration enviroment. - The value of the issue at (
iat
) claim is the number of seconds from 1970-01-01T00:00:00Z as measured in UTC, and must be less than the current time. - The value of the expiration time (
exp
) claim is the number of seconds from 1970-01-01T00:00:00Z as measured in UTC, and must be greater than the current time.
We strongly recommend using a well-known JWT library to perform the above verification steps. If you don't use any JWT in your codebase, you can find available libraries for your language at https://jwt.io/libraries.
Age Verification Token validation - Java example (JAVA-JWT)
Please see documentation and more examples at:
pom.xml
...
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>com.auth0</groupId>
<artifactId>jwks-rsa</artifactId>
<version>0.22.1</version>
</dependency>
...
Java code
import java.net.URL;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.concurrent.TimeUnit;
import com.auth0.jwk.JwkProvider;
import com.auth0.jwk.JwkProviderBuilder;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.RSAKeyProvider;
...
public static final String SERVICE_IDENTIFIER = "...";
public static final String PRIVO_PRODUCT_ID = "AgeEstimation";
// INT environment
public static final String PRIVO_ID_ISSUER = "https://id-svc-int.privo.com";
public static final String PRIVO_ID_JWKS_URL = "https://id-svc-int.privo.com/.well_known/jwks";
// PROD environment
// public static final String PRIVO_ID_ISSUER = "https://id-svc.privo.com";
// public static final String PRIVO_ID_JWKS_URL = "https://id-svc.privo.com/.well_known/jwks";
...
// build key provider from JWKS URL
JwkProvider provider = new JwkProviderBuilder(new URL(PRIVO_ID_JWKS_URL))
// allow 5 cached keys for 1 hour
.cached(5, 1, TimeUnit.HOURS).build();
RSAKeyProvider keyProvider = new RSAKeyProvider() {
@Override
public RSAPublicKey getPublicKeyById(String kid) {
try {
return (RSAPublicKey) provider.get(kid).getPublicKey();
} catch (Exception e) {
// FIXME: handle exception
e.printStackTrace();
return null;
}
}
@Override
public RSAPrivateKey getPrivateKey() {
return null;
}
@Override
public String getPrivateKeyId() {
return null;
}
};
Algorithm algorithm = Algorithm.RSA256(keyProvider);
// build the verifier, it can be reused
JWTVerifier verifier = JWT.require(algorithm)
// The value of the issuer (`iss`) claim must be equal to `https://id-svc.privo.com` on production
// enviroment or `https://id-svc-int.privo.com` on integration enviroment.
.withIssuer(PRIVO_ID_ISSUER)
// The value of the authorized party (`azp`) claim must be equal to your Service Identifier
// (provided by PRIVO).
.withClaim("azp", SERVICE_IDENTIFIER)
// The value of the audience claim (`aud`) must be equal to one of the PRIVO product IDs (AgeGate,
// AgeVerification, AgeEstimation, etc) that you integrate.
.withAudience(PRIVO_PRODUCT_ID)
// build the verifier
.build();
...
// verify the token and get the estimated ages
try {
DecodedJWT decodedJWT = verifier.verify(ageEstimationToken);
String ages = decodedJWT.getSubject();
System.out.println(ages); // [39,40,39,38,35]
} catch (JWTVerificationException ex) {
//FIXME: handle exception
ex.printStackTrace();
}