/* * validate a JWT access token (locally) * * TODO: document that we're reusing the following settings from the OIDC config section: * - JWKs URI refresh interval * - encryption key material (OIDCPrivateKeyFiles) * - iat slack (OIDCIDTokenIatSlack) * * OIDCOAuthRemoteUserClaim client_id * # 32x 61 hex * OIDCOAuthVerifySharedKeys aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa */ static apr_byte_t oidc_oauth_validate_jwt_access_token(request_rec *r, oidc_cfg *c, const char *access_token, json_t **token, char **response) { apr_jwt_error_t err; apr_jwt_t *jwt = NULL; if (apr_jwt_parse(r->pool, access_token, &jwt, oidc_util_merge_symmetric_key(r->pool, c->private_keys, c->oauth.client_secret, NULL), &err) == FALSE) { oidc_error(r, "could not parse JWT from access_token: %s", apr_jwt_e2s(r->pool, err)); return FALSE; } oidc_debug(r, "successfully parsed JWT with header: %s", jwt->header.value.str); /* validate the access token JWT, validating optional exp + iat */ if (oidc_proto_validate_jwt(r, jwt, NULL, FALSE, FALSE, c->provider.idtoken_iat_slack) == FALSE) { apr_jwt_destroy(jwt); return FALSE; } oidc_debug(r, "verify JWT against %d statically configured public keys and %d shared keys, with JWKs URI set to %s", c->oauth.verify_public_keys ? apr_hash_count(c->oauth.verify_public_keys) : 0, c->oauth.verify_shared_keys ? apr_hash_count(c->oauth.verify_shared_keys) : 0, c->oauth.verify_jwks_uri); oidc_jwks_uri_t jwks_uri = { c->oauth.verify_jwks_uri, c->provider.jwks_refresh_interval, c->oauth.ssl_validate_server }; if (oidc_proto_jwt_verify(r, c, jwt, &jwks_uri, oidc_util_merge_key_sets(r->pool, c->oauth.verify_public_keys, c->oauth.verify_shared_keys)) == FALSE) { oidc_error(r, "JWT access token signature could not be validated, aborting"); apr_jwt_destroy(jwt); return FALSE; } oidc_debug(r, "successfully verified JWT access token: %s", jwt->payload.value.str); *token = jwt->payload.value.json; *response = jwt->payload.value.str; return TRUE; }
static char * test_proto_validate_jwt(request_rec *r) { apr_jwt_t *jwt = NULL; apr_jwt_error_t err; const char *s_secret = "secret"; const char *s_issuer = "https://localhost"; apr_time_t now = apr_time_sec(apr_time_now()); const char *s_jwt_header = "{" "\"alg\": \"HS256\"" "}"; const char *s_jwt_payload = "{" "\"nonce\": \"543210,\"," "\"iat\": %" APR_TIME_T_FMT "," "\"sub\": \"alice\"," "\"iss\": \"%s\"," "\"aud\": \"bob\"," "\"exp\": %" APR_TIME_T_FMT "}"; s_jwt_payload = apr_psprintf(r->pool, s_jwt_payload, now, s_issuer, now + 600); char *s_jwt_header_encoded = NULL; oidc_base64url_encode(r, &s_jwt_header_encoded, s_jwt_header, strlen(s_jwt_header), 1); char *s_jwt_payload_encoded = NULL; oidc_base64url_encode(r, &s_jwt_payload_encoded, s_jwt_payload, strlen(s_jwt_payload), 1); char *s_jwt_message = apr_psprintf(r->pool, "%s.%s", s_jwt_header_encoded, s_jwt_payload_encoded); unsigned int md_len = 0; unsigned char md[EVP_MAX_MD_SIZE]; const EVP_MD *digest = EVP_get_digestbyname("sha256"); TST_ASSERT("HMAC", HMAC(digest, (const unsigned char * )s_secret, strlen(s_secret), (const unsigned char * )s_jwt_message, strlen(s_jwt_message), md, &md_len) != 0); char *s_jwt_signature_encoded = NULL; oidc_base64url_encode(r, &s_jwt_signature_encoded, (const char *) md, md_len, 1); char *s_jwt = apr_psprintf(r->pool, "%s.%s.%s", s_jwt_header_encoded, s_jwt_payload_encoded, s_jwt_signature_encoded); TST_ASSERT_ERR("apr_jwt_parse", apr_jwt_parse(r->pool, s_jwt, &jwt, NULL, &err), r->pool, err); TST_ASSERT_ERR("apr_jws_verify", apr_jws_verify(r->pool, jwt, oidc_util_merge_symmetric_key(r->pool, NULL, s_secret, NULL), &err), r->pool, err); TST_ASSERT_ERR("oidc_proto_validate_jwt", oidc_proto_validate_jwt(r, jwt, s_issuer, TRUE, TRUE, 10), r->pool, err); apr_jwt_destroy(jwt); return 0; }