/* * check whether the provided JWT is a valid id_token for the specified "provider" */ static apr_byte_t oidc_proto_validate_idtoken(request_rec *r, oidc_provider_t *provider, apr_jwt_t *jwt, const char *nonce) { oidc_cfg *cfg = ap_get_module_config(r->server->module_config, &auth_openidc_module); ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_validate_idtoken: entering jwt.header=\"%s\", jwt.payload=\%s\", nonce=%s", jwt->header.value.str, jwt->payload.value.str, nonce); /* if a nonce is not passed, we're doing a ("code") flow where the nonce is optional */ if (nonce != NULL) { /* if present, verify the nonce */ if (oidc_proto_validate_nonce(r, cfg, provider, nonce, jwt) == FALSE) return FALSE; } /* issuer is mandatory in id_token */ if (jwt->payload.iss == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_proto_validate_idtoken: response JSON object did not contain an \"iss\" string"); return FALSE; } /* check if the issuer matches the requested value */ if (oidc_util_issuer_match(provider->issuer, jwt->payload.iss) == FALSE) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_proto_validate_idtoken: configured issuer (%s) does not match received \"iss\" value in id_token (%s)", provider->issuer, jwt->payload.iss); return FALSE; } /* check exp */ if (oidc_proto_validate_exp(r, jwt) == FALSE) return FALSE; /* check iat */ if (oidc_proto_validate_iat(r, provider, jwt) == FALSE) return FALSE; /* check if the required-by-spec "sub" claim is present */ if (jwt->payload.sub == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_proto_validate_idtoken: id_token JSON payload did not contain the required-by-spec \"sub\" string value"); return FALSE; } /* verify the "aud" and "azp" values */ if (oidc_proto_validate_aud_and_azp(r, cfg, provider, &jwt->payload) == FALSE) return FALSE; return TRUE; }
/* * check to see if JSON provider metadata is valid */ static apr_byte_t oidc_metadata_provider_is_valid(request_rec *r, json_t *j_provider, const char *issuer) { /* get the "issuer" from the provider metadata and double-check that it matches what we looked for */ json_t *j_issuer = json_object_get(j_provider, "issuer"); if ((j_issuer == NULL) || (!json_is_string(j_issuer))) { oidc_error(r, "provider (%s) JSON metadata did not contain an \"issuer\" string", issuer); return FALSE; } /* check that the issuer matches */ if (issuer != NULL) { if (oidc_util_issuer_match(issuer, json_string_value(j_issuer)) == FALSE) { oidc_warn(r, "requested issuer (%s) does not match the \"issuer\" value in the provider metadata file: %s", issuer, json_string_value(j_issuer)); //return FALSE; } } /* verify that the provider supports the a flow that we implement */ json_t *j_response_types_supported = json_object_get(j_provider, "response_types_supported"); if ((j_response_types_supported != NULL) && (json_is_array(j_response_types_supported))) { int i = 0; for (i = 0; i < json_array_size(j_response_types_supported); i++) { json_t *elem = json_array_get(j_response_types_supported, i); if (!json_is_string(elem)) { oidc_error(r, "unhandled in-array JSON non-string object type [%d]", elem->type); continue; } if (oidc_proto_flow_is_supported(r->pool, json_string_value(elem))) break; } if (i == json_array_size(j_response_types_supported)) { oidc_warn(r, "could not find a supported response type in provider metadata (%s) for entry \"response_types_supported\"; assuming that \"code\" flow is supported...", issuer); //return FALSE; } } else { oidc_warn(r, "provider (%s) JSON metadata did not contain a \"response_types_supported\" array; assuming that \"code\" flow is supported...", issuer); // TODO: hey, this is required-by-spec stuff right? } /* verify that the provider supports a response_mode that we implement */ json_t *response_modes_supported = json_object_get(j_provider, "response_modes_supported"); if ((response_modes_supported != NULL) && (json_is_array(response_modes_supported))) { int i = 0; for (i = 0; i < json_array_size(response_modes_supported); i++) { json_t *elem = json_array_get(response_modes_supported, i); if (!json_is_string(elem)) { oidc_error(r, "unhandled in-array JSON non-string object type [%d]", elem->type); continue; } if ((apr_strnatcmp(json_string_value(elem), "fragment") == 0) || (apr_strnatcmp(json_string_value(elem), "query") == 0) || (apr_strnatcmp(json_string_value(elem), "form_post") == 0)) break; } if (i == json_array_size(response_modes_supported)) { oidc_warn(r, "could not find a supported response mode in provider metadata (%s) for entry \"response_modes_supported\"", issuer); return FALSE; } } else { oidc_debug(r, "provider (%s) JSON metadata did not contain a \"response_modes_supported\" array; assuming that \"fragment\" and \"query\" are supported", issuer); } /* check the required authorization endpoint */ if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider, "authorization_endpoint", TRUE) == FALSE) return FALSE; /* check the optional token endpoint */ if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider, "token_endpoint", FALSE) == FALSE) return FALSE; /* check the optional user info endpoint */ if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider, "userinfo_endpoint", FALSE) == FALSE) return FALSE; /* check the optional JWKs URI */ if (oidc_metadata_is_valid_uri(r, "provider", issuer, j_provider, "jwks_uri", FALSE) == FALSE) return FALSE; /* find out what type of authentication the token endpoint supports (we only support post or basic) */ json_t *j_token_endpoint_auth_methods_supported = json_object_get( j_provider, "token_endpoint_auth_methods_supported"); if ((j_token_endpoint_auth_methods_supported == NULL) || (!json_is_array(j_token_endpoint_auth_methods_supported))) { oidc_debug(r, "provider (%s) JSON metadata did not contain a \"token_endpoint_auth_methods_supported\" array, assuming \"client_secret_basic\" is supported", issuer); } else { int i; for (i = 0; i < json_array_size(j_token_endpoint_auth_methods_supported); i++) { json_t *elem = json_array_get( j_token_endpoint_auth_methods_supported, i); if (!json_is_string(elem)) { oidc_warn(r, "unhandled in-array JSON object type [%d] in provider (%s) metadata for entry \"token_endpoint_auth_methods_supported\"", elem->type, issuer); continue; } if (strcmp(json_string_value(elem), "client_secret_post") == 0) { break; } if (strcmp(json_string_value(elem), "client_secret_basic") == 0) { break; } } if (i == json_array_size(j_token_endpoint_auth_methods_supported)) { oidc_error(r, "could not find a supported value [client_secret_post|client_secret_basic] in provider (%s) metadata for entry \"token_endpoint_auth_methods_supported\"", issuer); return FALSE; } } return TRUE; }