Ejemplo n.º 1
0
static char *test_jwt_get_string(apr_pool_t *pool) {
	//apr_jwt_get_string

	const char *s =
			"eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9"
			".eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ"
			".dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";

	apr_jwt_t *jwt = NULL;
	apr_jwt_error_t err;
	TST_ASSERT_ERR("apr_jwt_parse", apr_jwt_parse(pool, s, &jwt, NULL, &err),
			pool, err);

	char *dst;

	dst = NULL;
	TST_ASSERT("apr_jwt_get_string (1a)",
			apr_jwt_get_string(pool, jwt->header.value.json, "typ", TRUE, &dst, &err));
	TST_ASSERT_STR("apr_jwt_get_string (1b)", dst, "JWT");

	dst = NULL;
	TST_ASSERT("apr_jwt_get_string (2a)",
			apr_jwt_get_string(pool, jwt->header.value.json, "alg", TRUE, &dst, &err));
	TST_ASSERT_STR("apr_jwt_get_string (2b)", dst, "HS256");

	dst = NULL;
	TST_ASSERT("apr_jwt_get_string (3a)",
			apr_jwt_get_string(pool, jwt->header.value.json, "dummy", FALSE, &dst, &err));
	TST_ASSERT_STR("apr_jwt_get_string (3b)", dst, NULL);

	apr_jwt_destroy(jwt);

	return 0;
}
Ejemplo n.º 2
0
/*
 * parse JWT payload
 */
static apr_byte_t apr_jwt_parse_payload(apr_pool_t *pool, const char *s_payload,
		apr_jwt_payload_t *payload, apr_jwt_error_t *err) {

	/* decode the JWT JSON payload */
	if (apr_jwt_base64url_decode_object(pool, s_payload, &payload->value,
			err) == FALSE)
		return FALSE;

	/* get the (optional) "issuer" value from the JSON payload */
	apr_jwt_get_string(pool, payload->value.json, "iss", FALSE, &payload->iss,
			NULL);

	/* get the (optional) "exp" value from the JSON payload */
	apr_jwt_get_timestamp(pool, payload->value.json, "exp", FALSE,
			&payload->exp,
			NULL);

	/* get the (optional) "iat" value from the JSON payload */
	apr_jwt_get_timestamp(pool, payload->value.json, "iat", FALSE,
			&payload->iat,
			NULL);

	/* get the (optional) "sub" value from the JSON payload */
	apr_jwt_get_string(pool, payload->value.json, "sub", FALSE, &payload->sub,
			NULL);

	return TRUE;
}
Ejemplo n.º 3
0
/*
 * parse an EC JWK
 */
static apr_byte_t apr_jwk_parse_ec(apr_pool_t *pool, apr_jwk_t *jwk) {

	/* allocated space and set key type */
	jwk->type = APR_JWK_KEY_EC;
	jwk->key.ec = apr_pcalloc(pool, sizeof(apr_jwk_key_ec_t));

	/* parse x */
	char *s_x = NULL;
	apr_jwt_get_string(pool, &jwk->value, "x", &s_x);
	if (s_x == NULL)
		return FALSE;

	/* parse x size */
	jwk->key.ec->x_len = apr_jwt_base64url_decode(pool,
			(char **) &jwk->key.ec->x, s_x, 1);

	/* parse y */
	char *s_y = NULL;
	apr_jwt_get_string(pool, &jwk->value, "y", &s_y);
	if (s_y == NULL)
		return FALSE;

	/* parse y size */
	jwk->key.ec->y_len = apr_jwt_base64url_decode(pool,
			(char **) &jwk->key.ec->y, s_y, 1);

	/* that went well */
	return TRUE;
}
Ejemplo n.º 4
0
/*
 * parse an RSA JWK in raw format (n,e,d)
 */
static apr_byte_t apr_jwk_parse_rsa_raw(apr_pool_t *pool, json_t *json,
		apr_jwk_key_rsa_t **jwk_key_rsa, apr_jwt_error_t *err) {

	/* allocate space */
	*jwk_key_rsa = apr_pcalloc(pool, sizeof(apr_jwk_key_rsa_t));
	apr_jwk_key_rsa_t *key = *jwk_key_rsa;

	/* parse the mandatory modulus */
	char *s_modulus = NULL;
	if (apr_jwt_get_string(pool, json, "n", TRUE, &s_modulus, err) == FALSE)
		return FALSE;

	/* base64url decode the modulus and get its size */
	key->modulus_len = apr_jwt_base64url_decode(pool, (char **) &key->modulus,
			s_modulus, 1);
	if (key->modulus_len <= 0) {
		apr_jwt_error(err, "apr_jwt_base64url_decode of modulus failed");
		return FALSE;
	}

	/* parse the mandatory exponent */
	char *s_exponent = NULL;
	if (apr_jwt_get_string(pool, json, "e", TRUE, &s_exponent, err) == FALSE)
		return FALSE;

	/* base64url decode the exponent and get its size */
	key->exponent_len = apr_jwt_base64url_decode(pool, (char **) &key->exponent,
			s_exponent, 1);
	if (key->exponent_len <= 0) {
		apr_jwt_error(err, "apr_jwt_base64url_decode of exponent failed");
		return FALSE;
	}

	/* parse the optional private exponent */
	char *s_private_exponent = NULL;
	apr_jwt_get_string(pool, json, "d", FALSE, &s_private_exponent, NULL);
	if (s_private_exponent != NULL) {
		/* base64url decode the private exponent and get its size */
		key->private_exponent_len = apr_jwt_base64url_decode(pool,
				(char **) &key->private_exponent, s_private_exponent, 1);
		if (key->private_exponent_len <= 0) {
			apr_jwt_error(err,
					"apr_jwt_base64url_decode of private exponent failed");
			return FALSE;
		}
	}

	/* that went well */
	return TRUE;
}
Ejemplo n.º 5
0
/*
 * set the unique user identifier that will be propagated in the Apache r->user and REMOTE_USER variables
 */
static apr_byte_t oidc_proto_set_remote_user(request_rec *r, oidc_cfg *c,
		oidc_provider_t *provider, apr_jwt_t *jwt, char **user) {

	char *issuer = provider->issuer;
	char *claim_name = apr_pstrdup(r->pool, c->remote_user_claim);
	int n = strlen(claim_name);
	int post_fix_with_issuer = (claim_name[n - 1] == '@');
	if (post_fix_with_issuer) {
		claim_name[n - 1] = '\0';
		issuer =
				(strstr(issuer, "https://") == NULL) ?
						apr_pstrdup(r->pool, issuer) :
						apr_pstrdup(r->pool, issuer + strlen("https://"));
	}

	/* extract the username claim (default: "sub") from the id_token payload */
	char *username = NULL;
	apr_jwt_get_string(r->pool, &jwt->payload.value, claim_name, &username);

	if (username == NULL) {
		oidc_error(r,
				"OIDCRemoteUserClaim is set to \"%s\", but the id_token JSON payload did not contain a \"%s\" string",
				c->remote_user_claim, claim_name);
		return FALSE;
	}

	/* set the unique username in the session (will propagate to r->user/REMOTE_USER) */
	*user = post_fix_with_issuer ?
			apr_psprintf(r->pool, "%s@%s", username, issuer) :
			apr_pstrdup(r->pool, username);

	oidc_debug(r, "set remote_user to \"%s\"", *user);

	return TRUE;
}
Ejemplo n.º 6
0
/*
 * parse JSON JWK
 */
apr_byte_t apr_jwk_parse_json(apr_pool_t *pool, json_t *json, apr_jwk_t **j_jwk,
		apr_jwt_error_t *err) {

	/* check that we've actually got a JSON value back */
	if (json == NULL) {
		apr_jwt_error(err, "JWK JSON is NULL");
		return FALSE;
	}

	/* check that the value is a JSON object */
	if (!json_is_object(json)) {
		apr_jwt_error(err, "JWK JSON is not a JSON object");
		return FALSE;
	}

	/* allocate memory for the JWK */
	*j_jwk = apr_pcalloc(pool, sizeof(apr_jwk_t));
	apr_jwk_t *jwk = *j_jwk;

	/* get the mandatory key type */
	char *kty = NULL;
	if (apr_jwt_get_string(pool, json, "kty", TRUE, &kty, err) == FALSE)
		return FALSE;

	/* get the optional kid */
	apr_jwt_get_string(pool, json, "kid", FALSE, &jwk->kid, NULL);

	/* parse the key */
	if (apr_strnatcmp(kty, "RSA") == 0)
		return apr_jwk_parse_rsa(pool, json, jwk, err);

	if (apr_strnatcmp(kty, "EC") == 0)
		return apr_jwk_parse_ec(pool, json, jwk, err);

	if (apr_strnatcmp(kty, "oct") == 0)
		return apr_jwk_parse_oct(pool, json, jwk, err);

	apr_jwt_error(err,
			"wrong or unsupported JWK key representation \"%s\" (\"RSA\", \"EC\" and \"oct\" are supported key types)",
			kty);

	return FALSE;
}
Ejemplo n.º 7
0
/*
 * parse a JWT header
 */
apr_byte_t apr_jwt_parse_header(apr_pool_t *pool, const char *s_header,
                                apr_jwt_header_t *header) {

    /* decode the JWT JSON header */
    if (apr_jwt_base64url_decode_object(pool, s_header, &header->value) == FALSE)
        return FALSE;

    /* parse the (optional) signing algorithm */
    apr_jwt_get_string(pool, &header->value, "alg", &header->alg);

    /* check that the mandatory algorithm was set */
    if (header->alg == NULL)
        return FALSE;

    /* parse the (optional) kid */
    apr_jwt_get_string(pool, &header->value, "kid", &header->kid);

    /* parse the (optional) enc */
    apr_jwt_get_string(pool, &header->value, "enc", &header->enc);

    return TRUE;
}
Ejemplo n.º 8
0
/*
 * parse an RSA JWK in raw format (n,e,d)
 */
static apr_byte_t apr_jwk_parse_rsa_raw(apr_pool_t *pool, apr_jwt_value_t *jwk,
		apr_jwk_key_rsa_t **jwk_key_rsa) {

	/* allocate space */
	*jwk_key_rsa = apr_pcalloc(pool, sizeof(apr_jwk_key_rsa_t));
	apr_jwk_key_rsa_t *key = *jwk_key_rsa;

	/* parse the modulus */
	char *s_modulus = NULL;
	apr_jwt_get_string(pool, jwk, "n", &s_modulus);
	if (s_modulus == NULL)
		return FALSE;

	/* parse the modulus size */
	key->modulus_len = apr_jwt_base64url_decode(pool, (char **) &key->modulus,
			s_modulus, 1);

	/* parse the exponent */
	char *s_exponent = NULL;
	apr_jwt_get_string(pool, jwk, "e", &s_exponent);
	if (s_exponent == NULL)
		return FALSE;

	/* parse the exponent size */
	key->exponent_len = apr_jwt_base64url_decode(pool, (char **) &key->exponent,
			s_exponent, 1);

	/* parse the private exponent */
	char *s_private_exponent = NULL;
	apr_jwt_get_string(pool, jwk, "d", &s_private_exponent);
	if (s_private_exponent != NULL) {
		/* parse the private exponent size */
		key->private_exponent_len = apr_jwt_base64url_decode(pool,
				(char **) &key->private_exponent, s_private_exponent, 1);
	}

	/* that went well */
	return TRUE;
}
Ejemplo n.º 9
0
/*
 * parse a JWT header
 */
static apr_byte_t apr_jwt_parse_header_object(apr_pool_t *pool,
		const char *s_header, apr_jwt_header_t *header, apr_jwt_error_t *err) {

	/* decode the JWT JSON header */
	if (apr_jwt_base64url_decode_object(pool, s_header, &header->value,
			err) == FALSE)
		return FALSE;

	/* parse the (mandatory) signing algorithm */
	if (apr_jwt_get_string(pool, header->value.json, "alg", TRUE, &header->alg,
			err) == FALSE)
		return FALSE;

	/* parse the (optional) kid */
	apr_jwt_get_string(pool, header->value.json, "kid", FALSE, &header->kid,
			NULL);

	/* parse the (optional) enc */
	apr_jwt_get_string(pool, header->value.json, "enc", FALSE, &header->enc,
			NULL);

	return TRUE;
}
Ejemplo n.º 10
0
/*
 * parse an RSA JWK
 */
static apr_byte_t apr_jwk_parse_rsa(apr_pool_t *pool, apr_jwk_t *jwk) {

	jwk->type = APR_JWK_KEY_RSA;

	char *s_test = NULL;
	apr_jwt_get_string(pool, &jwk->value, "n", &s_test);
	if (s_test != NULL)
		return apr_jwk_parse_rsa_raw(pool, &jwk->value, &jwk->key.rsa);

	json_t *v = json_object_get(jwk->value.json, "x5c");
	if (v != NULL)
		return apr_jwk_parse_rsa_x5c(pool, jwk);

	return FALSE;
}
Ejemplo n.º 11
0
/*
 * parse an EC JWK
 */
static apr_byte_t apr_jwk_parse_ec(apr_pool_t *pool, json_t *json,
		apr_jwk_t *jwk, apr_jwt_error_t *err) {

	/* allocated space and set key type */
	jwk->type = APR_JWK_KEY_EC;
	jwk->key.ec = apr_pcalloc(pool, sizeof(apr_jwk_key_ec_t));

	/* parse x */
	char *s_x = NULL;
	if (apr_jwt_get_string(pool, json, "x", TRUE, &s_x, err) == FALSE)
		return FALSE;

	/* base64url decode x and get its size */
	jwk->key.ec->x_len = apr_jwt_base64url_decode(pool,
			(char **) &jwk->key.ec->x, s_x, 1);
	if (jwk->key.ec->x_len <= 0) {
		apr_jwt_error(err, "apr_jwt_base64url_decode of x length failed");
		return FALSE;
	}

	/* parse y */
	char *s_y = NULL;
	if (apr_jwt_get_string(pool, json, "y", TRUE, &s_y, err) == FALSE)
		return FALSE;

	/* base64url decode y and get its size */
	jwk->key.ec->y_len = apr_jwt_base64url_decode(pool,
			(char **) &jwk->key.ec->y, s_y, 1);
	if (jwk->key.ec->y_len <= 0) {
		apr_jwt_error(err, "apr_jwt_base64url_decode of y length failed");
		return FALSE;
	}

	/* that went well */
	return TRUE;
}
Ejemplo n.º 12
0
/*
 * if a nonce was passed in the authorization request (and stored in the browser state),
 * check that it matches the nonce value in the id_token payload
 */
static apr_byte_t oidc_proto_validate_nonce(request_rec *r, oidc_cfg *cfg,
		oidc_provider_t *provider, const char *nonce, apr_jwt_t *jwt) {

	/* see if we have this nonce cached already */
	const char *replay = NULL;
	cfg->cache->get(r, OIDC_CACHE_SECTION_NONCE, nonce, &replay);
	if (replay != NULL) {
		oidc_error(r,
				"the nonce value (%s) passed in the browser state was found in the cache already; possible replay attack!?",
				nonce);
		return FALSE;
	}

	/* get the "nonce" value in the id_token payload */
	char *j_nonce = NULL;
	apr_jwt_get_string(r->pool, &jwt->payload.value, "nonce", &j_nonce);

	if (j_nonce == NULL) {
		oidc_error(r,
				"id_token JSON payload did not contain a \"nonce\" string");
		return FALSE;
	}

	/* see if the nonce in the id_token matches the one that we sent in the authorization request */
	if (apr_strnatcmp(nonce, j_nonce) != 0) {
		oidc_error(r,
				"the nonce value (%s) in the id_token did not match the one stored in the browser session (%s)",
				j_nonce, nonce);
		return FALSE;
	}

	/*
	 * nonce cache duration (replay prevention window) is the 2x the configured
	 * slack on the timestamp (+-) for token issuance plus 10 seconds for safety
	 */
	apr_time_t nonce_cache_duration = apr_time_from_sec(
			provider->idtoken_iat_slack * 2 + 10);

	/* store it in the cache for the calculated duration */
	cfg->cache->set(r, OIDC_CACHE_SECTION_NONCE, nonce, nonce,
			apr_time_now() + nonce_cache_duration);

	oidc_debug(r,
			"nonce \"%s\" validated successfully and is now cached for %" APR_TIME_T_FMT " seconds",
			nonce, apr_time_sec(nonce_cache_duration));

	return TRUE;
}
Ejemplo n.º 13
0
/*
 * parse an RSA JWK
 */
static apr_byte_t apr_jwk_parse_rsa(apr_pool_t *pool, json_t *json,
		apr_jwk_t *jwk, apr_jwt_error_t *err) {

	jwk->type = APR_JWK_KEY_RSA;

	char *s_test = NULL;
	apr_jwt_get_string(pool, json, "n", FALSE, &s_test, NULL);
	if (s_test != NULL)
		return apr_jwk_parse_rsa_raw(pool, json, &jwk->key.rsa, err);

	json_t *v = json_object_get(json, "x5c");
	if (v != NULL)
		return apr_jwk_parse_rsa_x5c(pool, json, jwk, err);

	apr_jwt_error(err,
			"wrong or unsupported RSA key representation, no \"n\" or \"x5c\" key found in JWK JSON value");
	return FALSE;
}
Ejemplo n.º 14
0
/*
 * parse a an octet sequence used to represent a symmetric key
 */
static apr_byte_t apr_jwk_parse_oct(apr_pool_t *pool, json_t *json,
		apr_jwk_t *jwk, apr_jwt_error_t *err) {

	/* allocated space and set key type */
	jwk->type = APR_JWK_KEY_OCT;
	jwk->key.oct = apr_pcalloc(pool, sizeof(apr_jwk_key_oct_t));

	/* parse k */
	char *s_k = NULL;
	if (apr_jwt_get_string(pool, json, "k", TRUE, &s_k, err) == FALSE)
		return FALSE;

	/* base64url decode k and get its size */
	jwk->key.oct->k_len = apr_jwt_base64url_decode(pool,
			(char **) &jwk->key.oct->k, s_k, 1);
	if (jwk->key.oct->k_len <= 0) {
		apr_jwt_error(err, "apr_jwt_base64url_decode of k length failed");
		return FALSE;
	}

	/* that went well */
	return TRUE;
}
Ejemplo n.º 15
0
/*
 * parse JSON JWK
 */
apr_byte_t apr_jwk_parse_json(apr_pool_t *pool, json_t *j_json,
		const char *s_json, apr_jwk_t **j_jwk) {

	/* check that we've actually got a JSON value back */
	if (j_json == NULL)
		return FALSE;

	/* check that the value is a JSON object */
	if (!json_is_object(j_json))
		return FALSE;

	/* allocate memory for the JWK */
	*j_jwk = apr_pcalloc(pool, sizeof(apr_jwk_t));
	apr_jwk_t *jwk = *j_jwk;

	/* set the raw JSON/string representations */
	jwk->value.json = j_json;
	jwk->value.str = apr_pstrdup(pool, s_json);

	/* get the key type */
	char *kty = NULL;
	if (apr_jwt_get_string(pool, &jwk->value, "kty", &kty) == FALSE)
		return FALSE;

	/* kty is mandatory */
	if (kty == NULL)
		return FALSE;

	/* parse the key */
	if (apr_strnatcmp(kty, "RSA") == 0)
		return apr_jwk_parse_rsa(pool, jwk);

	if (apr_strnatcmp(kty, "EC") == 0)
		return apr_jwk_parse_ec(pool, jwk);

	return FALSE;
}
Ejemplo n.º 16
0
/*
 * check a hash value in the id_token against the corresponding hash calculated over a provided value
 */
static apr_byte_t oidc_proto_validate_hash_value(request_rec *r,
		oidc_provider_t *provider, apr_jwt_t *jwt, const char *response_type,
		const char *value, const char *key,
		apr_array_header_t *required_for_flows) {

	/*
	 * get the hash value from the id_token
	 */
	char *hash = NULL;
	apr_jwt_get_string(r->pool, &jwt->payload.value, key, &hash);

	/*
	 * check if the hash was present
	 */
	if (hash == NULL) {

		/* no hash..., now see if the flow required it */
		int i;
		for (i = 0; i < required_for_flows->nelts; i++) {
			if (oidc_util_spaced_string_equals(r->pool, response_type,
					((const char**) required_for_flows->elts)[i])) {
				ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r,
						"oidc_proto_validate_hash_value: flow is \"%s\", but no %s found in id_token",
						response_type, key);
				return FALSE;
			}
		}

		/* no hash but it was not required anyway */
		return TRUE;
	}

	/*
	 * we have a hash, validate it and return the result
	 */
	return oidc_proto_validate_hash(r, jwt->header.alg, hash, value, key);
}
Ejemplo n.º 17
0
/*
 * get the key from the JWKs that corresponds with the key specified in the header
 */
static apr_byte_t oidc_proto_get_key_from_jwks(request_rec *r,
		apr_jwt_header_t *jwt_hdr, json_t *j_jwks, const char *type,
		apr_jwk_t **result) {

	char *x5t = NULL;
	apr_jwt_get_string(r->pool, &jwt_hdr->value, "x5t", &x5t);

	oidc_debug(r, "search for kid \"%s\" or thumbprint x5t \"%s\"",
			jwt_hdr->kid, x5t);

	/* get the "keys" JSON array from the JWKs object */
	json_t *keys = json_object_get(j_jwks, "keys");
	if ((keys == NULL) || !(json_is_array(keys))) {
		oidc_error(r, "\"keys\" array element is not a JSON array");
		return FALSE;
	}

	int i;
	for (i = 0; i < json_array_size(keys); i++) {

		/* get the next element in the array */
		json_t *elem = json_array_get(keys, i);

		/* check that it is a JSON object */
		if (!json_is_object(elem)) {
			oidc_warn(r,
					"\"keys\" array element is not a JSON object, skipping");
			continue;
		}

		/* get the key type and see if it is the RSA type that we are looking for */
		json_t *kty = json_object_get(elem, "kty");
		if ((!json_is_string(kty))
				|| (strcmp(json_string_value(kty), type) != 0))
			continue;

		/* see if we were looking for a specific kid, if not we'll return the first one found */
		if ((jwt_hdr->kid == NULL) && (x5t == NULL)) {
			oidc_debug(r, "no kid/x5t to match, return first key found");

			apr_jwk_parse_json(r->pool, elem, NULL, result);
			break;
		}

		/* we are looking for a specific kid, get the kid from the current element */
		json_t *ekid = json_object_get(elem, "kid");
		if ((ekid != NULL) && json_is_string(ekid) && (jwt_hdr->kid != NULL)) {
			/* compare the requested kid against the current element */
			if (apr_strnatcmp(jwt_hdr->kid, json_string_value(ekid)) == 0) {
				oidc_debug(r, "found matching kid: \"%s\"", jwt_hdr->kid);

				apr_jwk_parse_json(r->pool, elem, NULL, result);
				break;
			}
		}

		/* we are looking for a specific x5t, get the x5t from the current element */
		json_t *ex5t = json_object_get(elem, "kid");
		if ((ex5t != NULL) && json_is_string(ex5t) && (x5t != NULL)) {
			/* compare the requested kid against the current element */
			if (apr_strnatcmp(x5t, json_string_value(ex5t)) == 0) {
				oidc_debug(r, "found matching x5t: \"%s\"", x5t);

				apr_jwk_parse_json(r->pool, elem, NULL, result);
				break;
			}
		}

	}

	return TRUE;
}
Ejemplo n.º 18
0
/*
 * validate the "aud" and "azp" claims in the id_token payload
 */
static apr_byte_t oidc_proto_validate_aud_and_azp(request_rec *r, oidc_cfg *cfg,
		oidc_provider_t *provider, apr_jwt_payload_t *id_token_payload) {

	char *azp = NULL;
	apr_jwt_get_string(r->pool, &id_token_payload->value, "azp", &azp);

	/*
	 * the "azp" claim is only needed when the id_token has a single audience value and that audience
	 * is different than the authorized party; it MAY be included even when the authorized party is
	 * the same as the sole audience.
	 */
	if ((azp != NULL) && (apr_strnatcmp(azp, provider->client_id) != 0)) {
		oidc_error(r,
				"the \"azp\" claim (%s) is present in the id_token, but is not equal to the configured client_id (%s)",
				azp, provider->client_id);
		return FALSE;
	}

	/* get the "aud" value from the JSON payload */
	json_t *aud = json_object_get(id_token_payload->value.json, "aud");
	if (aud != NULL) {

		/* check if it is a single-value */
		if (json_is_string(aud)) {

			/* a single-valued audience must be equal to our client_id */
			if (apr_strnatcmp(json_string_value(aud), provider->client_id)
					!= 0) {
				oidc_error(r,
						"the configured client_id (%s) did not match the \"aud\" claim value (%s) in the id_token",
						provider->client_id, json_string_value(aud));
				return FALSE;
			}

			/* check if this is a multi-valued audience */
		} else if (json_is_array(aud)) {

			if ((json_array_size(aud) > 1) && (azp == NULL)) {
				oidc_debug(r,
						"the \"aud\" claim value in the id_token is an array with more than 1 element, but \"azp\" claim is not present (a SHOULD in the spec...)");
			}

			if (oidc_util_json_array_has_value(r, aud,
					provider->client_id) == FALSE) {
				oidc_error(r,
						"our configured client_id (%s) could not be found in the array of values for \"aud\" claim",
						provider->client_id);
				return FALSE;
			}
		} else {
			oidc_error(r,
					"id_token JSON payload \"aud\" claim is not a string nor an array");
			return FALSE;
		}

	} else {
		oidc_error(r, "id_token JSON payload did not contain an \"aud\" claim");
		return FALSE;
	}

	return TRUE;
}