use of net.openid.conformance.condition.PreEnvironment in project conformance-suite by openid-certification.
the class ValidateJARMExpRecommendations method evaluate.
@Override
@PreEnvironment(required = { "jarm_response" })
public Environment evaluate(Environment env) {
Instant now = Instant.now();
Long exp = env.getLong("jarm_response", "claims.exp");
if (exp == null) {
throw error("'exp' claim missing from JARM response");
}
// exp recommended to be less than 10 minutes - added after JARM ID1:
// https://bitbucket.org/openid/fapi/commits/8ac0bc6059cfcfdb6c155efa2d992a1eb86e8b6c -
final int allowableLifeTimeMinutes = 10;
if (now.plusMillis(timeSkewMillis).plusSeconds(allowableLifeTimeMinutes * 60).isBefore(Instant.ofEpochSecond(exp))) {
throw error("JARM 'exp' time is further in the future than the recommended " + allowableLifeTimeMinutes + " minutes", args("expiration", new Date(exp * 1000L), "now", now));
}
// not mentioned by spec, but for the sake of sanity check the response has a lifetime of at least 10 seconds
// or it's likely to fall in various real world situations
// We don't use 'timeSkewMillis' here, as that would allow a zero expiry to pass
final int minimumLifeTimeSeconds = 10;
if (now.plusSeconds(minimumLifeTimeSeconds).isAfter(Instant.ofEpochSecond(exp))) {
throw error("JARM 'exp' time appears to be less than " + minimumLifeTimeSeconds + " seconds", args("expiration", new Date(exp * 1000L), "now", now));
}
logSuccess("JARM response 'exp' is less than " + allowableLifeTimeMinutes + " minutes", args("expiration", new Date(exp * 1000L), "now", now));
return env;
}
use of net.openid.conformance.condition.PreEnvironment in project conformance-suite by openid-certification.
the class ValidateJARMResponse method evaluate.
@Override
@PreEnvironment(required = { "jarm_response", "server", "client" })
public Environment evaluate(Environment env) {
// to check the audience
String clientId = env.getString("client", "client_id");
// to validate the issuer
String issuer = env.getString("server", "issuer");
// to check timestamps
Instant now = Instant.now();
// check all our testable values
if (Strings.isNullOrEmpty(clientId) || Strings.isNullOrEmpty(issuer)) {
throw error("Couldn't find values to test response against");
}
JsonElement iss = env.getElementFromObject("jarm_response", "claims.iss");
if (iss == null) {
throw error("Missing issuer");
}
if (!issuer.equals(env.getString("jarm_response", "claims.iss"))) {
throw error("Issuer mismatch", args("expected", issuer, "actual", env.getString("jarm_response", "claims.iss")));
}
JsonElement aud = env.getElementFromObject("jarm_response", "claims.aud");
if (aud == null) {
throw error("Missing audience");
}
if (aud.isJsonArray()) {
if (!aud.getAsJsonArray().contains(new JsonPrimitive(clientId))) {
throw error("Audience not found", args("expected", clientId, "actual", aud));
}
} else {
if (!clientId.equals(OIDFJSON.getString(aud))) {
throw error("Audience mismatch", args("expected", clientId, "actual", aud));
}
}
Long exp = env.getLong("jarm_response", "claims.exp");
if (exp == null) {
throw error("Missing expiration");
} else {
if (now.minusMillis(timeSkewMillis).isAfter(Instant.ofEpochSecond(exp))) {
throw error("Token expired", args("expiration", new Date(exp * 1000L), "now", now));
}
}
// iat and nbf are not required to be present, but should be valid if they are
// https://bitbucket.org/openid/fapi/issues/269/jarm-response-contents-clarifications
Long iat = env.getLong("jarm_response", "claims.iat");
if (iat != null) {
if (now.plusMillis(timeSkewMillis).isBefore(Instant.ofEpochSecond(iat))) {
throw error("Token issued in the future", args("issued-at", new Date(iat * 1000L), "now", now));
}
}
Long nbf = env.getLong("jarm_response", "claims.nbf");
if (nbf != null) {
if (now.plusMillis(timeSkewMillis).isBefore(Instant.ofEpochSecond(nbf))) {
// this is just something to log, it doesn't make the token invalid
log("Token has future not-before", args("not-before", new Date(nbf * 1000L), "now", now));
}
}
logSuccess("JARM response standard JWT claims are valid");
return env;
}
use of net.openid.conformance.condition.PreEnvironment in project conformance-suite by openid-certification.
the class ValidateLogoutTokenClaims method evaluate.
@Override
@PreEnvironment(required = { "logout_token", "server", "client" })
public Environment evaluate(Environment env) {
// to check the audience
String clientId = env.getString("client", "client_id");
// to validate the issuer
String issuer = env.getString("server", "issuer");
// to check timestamps
Instant now = Instant.now();
if (Strings.isNullOrEmpty(clientId) || Strings.isNullOrEmpty(issuer)) {
throw error("Couldn't find values to test token against");
}
// checks are in order listed in https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken
// iss
// REQUIRED. Issuer Identifier, as specified in Section 2 of [OpenID.Core].
String logoutTokenIss = env.getString("logout_token", "claims.iss");
if (logoutTokenIss == null) {
throw error("'iss' claim missing");
}
if (!issuer.equals(logoutTokenIss)) {
throw error("Issuer mismatch", args("expected", issuer, "actual", logoutTokenIss));
}
// sub
// OPTIONAL. Subject Identifier, as specified in Section 2 of [OpenID.Core].
// checked in CheckIdTokenSubMatchesLogoutToken & CheckLogoutTokenHasSubOrSid
// aud
// REQUIRED. Audience(s), as specified in Section 2 of [OpenID.Core].
JsonElement aud = env.getElementFromObject("logout_token", "claims.aud");
if (aud == null) {
throw error("'aud' claim missing");
}
if (aud.isJsonArray()) {
if (!aud.getAsJsonArray().contains(new JsonPrimitive(clientId))) {
throw error("'aud' array does not contain our client id", args("expected", clientId, "actual", aud));
}
} else {
if (!clientId.equals(OIDFJSON.getString(aud))) {
throw error("'aud' is not our client id", args("expected", clientId, "actual", aud));
}
}
// iat
// REQUIRED. Issued at time, as specified in Section 2 of [OpenID.Core].
Long iat = env.getLong("logout_token", "claims.iat");
if (iat == null) {
throw error("'iat' claim missing");
} else {
if (now.plusMillis(timeSkewMillis).isBefore(Instant.ofEpochSecond(iat))) {
throw error("Token 'iat' in the future", args("issued-at", new Date(iat * 1000L), "now", now));
}
}
// jti
// REQUIRED. Unique identifier for the token, as specified in Section 9 of [OpenID.Core].
String jti = env.getString("logout_token", "claims.jti");
if (jti == null) {
throw error("jti missing");
}
// events
// REQUIRED. Claim whose value is a JSON object containing the member name http://schemas.openid.net/event/backchannel-logout. This declares that the JWT is a Logout Token. The corresponding member value MUST be a JSON object and SHOULD be the empty JSON object {}.
JsonElement events = env.getElementFromObject("logout_token", "claims.events");
if (events == null) {
throw error("'events' claim missing");
}
if (!events.isJsonObject()) {
throw error("'events' claim is not a json object");
}
JsonObject eventsObj = events.getAsJsonObject();
if (eventsObj.size() != 1) {
throw error("'events' object does not contain exactly 1 entry", eventsObj);
}
JsonElement eventsValueElement = eventsObj.get("http://schemas.openid.net/event/backchannel-logout");
if (eventsValueElement == null) {
throw error("http://schemas.openid.net/event/backchannel-logout entry is missing from 'events' claim", eventsObj);
}
if (!eventsValueElement.isJsonObject()) {
throw error("http://schemas.openid.net/event/backchannel-logout is not a json object");
}
JsonObject eventsValue = (JsonObject) eventsValueElement;
if (eventsValue.size() != 0) {
throw error("http://schemas.openid.net/event/backchannel-logout is not an empty object", eventsObj);
}
// sid
// OPTIONAL. Session ID - String identifier for a Session. This represents a Session of a User Agent or device for a logged-in End-User at an RP. Different sid values are used to identify distinct sessions at an OP. The sid value need only be unique in the context of a particular issuer. Its contents are opaque to the RP. Its syntax is the same as an OAuth 2.0 Client Identifier.
// Checked in CheckIdTokenSidMatchesLogoutToken & CheckLogoutTokenHasSubOrSid
// nonce checked in CheckLogoutTokenNoNonce
logSuccess("logout token iss, aud, iat, jti and events claims passed validation checks");
return env;
}
use of net.openid.conformance.condition.PreEnvironment in project conformance-suite by openid-certification.
the class ValidateMTLSCertificatesAsX509 method evaluate.
@Override
@PreEnvironment(required = "mutual_tls_authentication")
public Environment evaluate(Environment env) {
String certString = env.getString("mutual_tls_authentication", "cert");
String keyString = env.getString("mutual_tls_authentication", "key");
String caString = env.getString("mutual_tls_authentication", "ca");
if (Strings.isNullOrEmpty(certString) || Strings.isNullOrEmpty(keyString)) {
throw error("Couldn't find TLS client certificate or key for MTLS");
}
CertificateFactory certFactory = null;
try {
certFactory = CertificateFactory.getInstance("X.509", "BC");
} catch (CertificateException | NoSuchProviderException | IllegalArgumentException e) {
throw error("Couldn't get CertificateFactory", e);
}
X509Certificate certificate = generateCertificateFromMTLSCert(certString, certFactory);
validateMTLSKey(certString, keyString, certificate);
if (!Strings.isNullOrEmpty(caString)) {
validateMTLSCa(env, certFactory, caString);
}
logSuccess("Mutual TLS authentication cert validated as X.509");
return env;
}
use of net.openid.conformance.condition.PreEnvironment in project conformance-suite by openid-certification.
the class ValidateResourceResponseSignature method evaluate.
@Override
@PreEnvironment(required = { "endpoint_response_jwt", "org_server_jwks" })
public Environment evaluate(Environment env) {
String idToken = env.getString("endpoint_response_jwt", "value");
// to validate the signature
JsonObject serverJwks = env.getObject("org_server_jwks");
verifyJwsSignature(idToken, serverJwks, "endpoint_response_jwt", true, "organization");
return env;
}
Aggregations