Discovery
To get URLs for token and JWKS (required for validating signed ID Token), a request should be sent to the discovery URL [https://www.ident-preprod1.nets.eu/oidc/.well-known/openid-configuration]. It is returning a JSON document where the two values of interest are named
token_endpoint and
jwks_uri.
curl -s https://www.ident-preprod1.nets.eu/oidc/.well-known/openid-configuration
// A truststore file is used to establish an HTTPS connection.
// See the implementation of ClaimsService.getHTTPRequest in the demo app source code to
// see how the values from the app config are used in the HTTPS request.
String truststorepath, truststorepwd;
HTTPResponse discoveryHTTPResp = getHTTPRequest("https://www.ident-preprod1.nets.eu/oidc/.well-known/openid-configuration", truststorepath, truststorepwd).send();
JSONObject discoveryJsonObject = discoveryHTTPResp.getContentAsJSONObject();
String tokenEndPoint = JSONObjectUtils.getString(discoveryJsonObject, "token_endpoint");
String jwks_uri = JSONObjectUtils.getString(discoveryJsonObject, "jwks_uri");
String discoveryEndpoint = "https://www.ident-preprod1.nets.eu/oidc/.well-known/openid-configuration";
var response = new HttpClient().GetAsync(discoveryEndpoint).Result;
var responseBody = response.Content.ReadAsStringAsync().Result;
JSONObject discovery = JsonValue.parse(responseBody);
String tokenEndpoint = discovery.getString(discovery, "token_endpoint");
String jwks_uri = discoverygetString(discovery, "jwks_uri");
ID token retrieval
Get ID token by doing an HTTP POST request to the token URL. The request should have the following parameters (URL encoded):
grant_type with the value 'authorization_code'
code with the authorisation code received (URL encoded) in the redirect_uri (see
Step 2)
redirect_uri with the redirect_uri value that was sent in
Step 1 (this parameter is optional if only one redirect_uri is configured during registration with Nets)
The following request headers have to be set:
- Authorization: Basic <base64-encoded customer-identifier:client-secret>
- Content-Type: application/x-www-form-urlencoded
curl -X POST 'https://www.ident-preprod1.nets.eu/oidc/token?grant_type=authorization_code'
-H 'Content-Type: application/x-www-form-urlencoded'
-H 'Authorization: Basic <<Base64Enocoded username:password>>'
-d 'code=<<URL encoded Authorization Code>>&redirect_uri=<<redirect_uri earlier passed in Authentication request>>'
ClientSecretBasic clientSecretBasic = new ClientSecretBasic(new ClientID(customerIdentifier), new Secret(clientSecret));
TokenRequest tokenReq = new TokenRequest(new URI(tokenEndPoint), clientSecretBasic, new AuthorizationCodeGrant(authCode, new URI(redirectUri)));
HTTPResponse tokenHTTPResp = tokenReq.toHTTPRequest().send();
if(400 == tokenHTTPResp.getStatusCode()) {
throw new Exception("Invalid response from token URL [statuscode=400].");
}
// Get JSON object from Token response
JSONObject tokenJsonObject = tokenHTTPResp.getContentAsJSONObject();
String idtoken = JSONObjectUtils.getString(tokenJsonObject, "id_token");
var values = new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "code" , <<authorization code>> },
{ "redirect_uri", <<redirect_uri>>}
};
var request = new HttpRequestMessage(HttpMethod.Post, new Uri(tokenEndpoint));
request.Content = new FormUrlEncodedContent(values);
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", encodedClientIdAndSecret);
var response = new HttpClient().SendAsync(request).Result;
var tokenJson = response.Content.ReadAsStringAsync().Result;
var jwt = JObject.Parse(tokenJson);
string idToken = jwt["id_token"].ToString();
ID token validation
The ID Token returned is signed, and it can optionally be encrypted. ID Token encryption is determined during customer configuration. Note: The use of encrypted ID Token is required for Finnish Trust Network customers offering identification with Finnish Bank IDs and Mobiilivarmenne.
The encrypted ID Token should be decrypted using the private key only if customers requested Nets to encrypt the ID Token after signing. Below is a code example for encrypted ID Token. Read for more information about ID Token encryption.
// Decrypt the recieved ID Token using the private key, if the token was encrypted
JWT jwt = JWTParser.parse(idtoken);
SignedJWT signedJWT = null;
if (jwt instanceof EncryptedJWT) {
PrivateKey privateKey = getPrivateKey("token-enc", mid);
JWEObject jweData = JWEObject.parse(encryptedRequest);
jweData.decrypt(new RSADecrypter(privateKey));
signedJWT = jweData.getPayload().toSignedJWT();
} else {
signedJWT = SignedJWT.parse(idtoken);
}
The signed ID Token should be validated using a public key that is returned from an HTTP GET request to the JWKS URL. This URL returns a JSON document with JSON array named keys. A JSON with matching kid value can be fetched from there which will give the correct public key for validation.
// Get JSON response from jwks_uri
HTTPResponse jwksResp = getHTTPRequest(jwks_uri, truststorePath, truststorePassword).send();
JSONObject jwksJsonObject = jwksResp.getContentAsJSONObject();
JSONArray keys = JSONObjectUtils.getJSONArray(jwksJsonObject("keys");
JSONObject jsonKey = null;
for (int i=0; i<keys.size(); i++) {
jsonKey = (JSONObject) keys.get(i);
Object kid = jsonKey.get("kid");
if (null != kid && kid.toString().equals(signedJWT.getHeader().getKeyID())) {
//match kid from header
break;
}
jsonKey = null; //not the kid we are looking for, go to the next
}
if (null != jsonKey) {
PublicKey publicKey = buildPublicKey(jsonKey);
if (null != publicKey) {
// Verify Signature
JWSVerifier verifier = new RSASSAVerifier((RSAPublicKey) publicKey);
if (!signedJWT.verify(verifier)) {
throw new JOSEException("Signature mismatch");
}
// Verified successfully
}
} else {
throw new Exception("No public key is found for IDToken signature verification");
}
var jwks = JObject.Parse(jwksJson);
var key = jwks[keys].First();
var jwk = JsonConvert.DeserializeObject<JsonWebKey>(key.ToString());
var validationParameters = new TokenValidationParameters
{
IssuerSigningKey = jwk,
ValidateIssuerSigningKey = true,
ValidateIssuer = false,
RequireExpirationTime = true,
//set clockskew to zero so tokens expire exactly at token expiration time
ClockSkew = TimeSpan.Zero,
ValidateLifetime = true,
ValidateAudience = false
};
ISecurityTokenValidator tokenValidator = new JwtSecurityTokenHandler();
var claims = tokenValidator.ValidateToken(idToken, validationParameters, out var validatedToken);
ID token claims
See the implementation of ClaimsService.buildPublicKey in the
demo app source code. Finally, retrieve the claims from the ID Token.
JWTClaimsSet jwtClaimsSet = signedJWT.getJWTClaimsSet();
String subjectDN = jwtClaimsSet.getStringClaim("dn");
String birthdate = jwtClaimsSet.getStringClaim("birthdate");
string subjectDN = claims.FindFirst("dn").Value;
string birthDate = claims.FindFirst("birthdate").Value;
The ID Token is generated and made available to the customer by E-Ident service. The end user identity and associated claims are also stored in E-Ident service. The identification request is configured by way of parameters that specify how the identification session will be set up.
The E-Ident demo app displays the retrieved claims after successful identification.
Together with the claims, the demo app also displays the full response from the ID Token URL in JSON format.