/* * use OpenID Connect Discovery to get metadata for the specified issuer */ apr_byte_t oidc_metadata_provider_retrieve(request_rec *r, oidc_cfg *cfg, const char *issuer, const char *url, json_t **j_metadata, const char **response) { /* get a handle to the directory config */ oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config, &auth_openidc_module); /* get provider metadata from the specified URL with the specified parameters */ if (oidc_util_http_get(r, url, NULL, NULL, NULL, cfg->provider.ssl_validate_server, response, cfg->http_timeout_short, cfg->outgoing_proxy, dir_cfg->pass_cookies) == FALSE) return FALSE; /* decode and see if it is not an error response somehow */ if (oidc_util_decode_json_and_check_error(r, *response, j_metadata) == FALSE) return FALSE; /* check to see if it is valid metadata */ if (oidc_metadata_provider_is_valid(r, *j_metadata, issuer) == FALSE) return FALSE; /* all OK */ return TRUE; }
/* * get claims from the OP UserInfo endpoint using the provided access_token */ apr_byte_t oidc_proto_resolve_userinfo(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *access_token, const char **response, json_t **claims) { ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_resolve_userinfo: entering, endpoint=%s, access_token=%s", provider->userinfo_endpoint_url, access_token); /* only do this if an actual endpoint was set */ if (provider->userinfo_endpoint_url == NULL) return FALSE; /* only do this if we have an access_token */ if (access_token == NULL) return FALSE; /* get the JSON response */ if (oidc_util_http_call(r, provider->userinfo_endpoint_url, OIDC_HTTP_GET, NULL, NULL, access_token, provider->ssl_validate_server, response, cfg->http_timeout_long, cfg->outgoing_proxy) == FALSE) return FALSE; /* decode and check for an "error" response */ return oidc_util_decode_json_and_check_error(r, *response, claims); }
/* * return JWKs for the specified issuer */ apr_byte_t oidc_metadata_jwks_get(request_rec *r, oidc_cfg *cfg, const oidc_jwks_uri_t *jwks_uri, json_t **j_jwks, apr_byte_t *refresh) { oidc_debug(r, "enter, jwks_uri=%s, refresh=%d", jwks_uri->url, *refresh); /* see if we need to do a forced refresh */ if (*refresh == TRUE) { oidc_debug(r, "doing a forced refresh of the JWKs from URI \"%s\"", jwks_uri->url); if (oidc_metadata_jwks_retrieve_and_cache(r, cfg, jwks_uri, j_jwks) == TRUE) return TRUE; // else: fallback on any cached JWKs } /* see if the JWKs is cached */ const char *value = NULL; cfg->cache->get(r, OIDC_CACHE_SECTION_JWKS, oidc_metadata_jwks_cache_key(r, jwks_uri->url), &value); if (value == NULL) { /* it is non-existing or expired: do a forced refresh */ *refresh = TRUE; return oidc_metadata_jwks_retrieve_and_cache(r, cfg, jwks_uri, j_jwks); } /* decode and see if it is not an error response somehow */ if (oidc_util_decode_json_and_check_error(r, value, j_jwks) == FALSE) return FALSE; return TRUE; }
/* * helper function to get the JWKs for the specified issuer */ static apr_byte_t oidc_metadata_jwks_retrieve_and_cache(request_rec *r, oidc_cfg *cfg, const oidc_jwks_uri_t *jwks_uri, json_t **j_jwks) { const char *response = NULL; /* get a handle to the directory config */ oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config, &auth_openidc_module); /* no valid provider metadata, get it at the specified URL with the specified parameters */ if (oidc_util_http_get(r, jwks_uri->url, NULL, NULL, NULL, jwks_uri->ssl_validate_server, &response, cfg->http_timeout_long, cfg->outgoing_proxy, dir_cfg->pass_cookies) == FALSE) return FALSE; /* decode and see if it is not an error response somehow */ if (oidc_util_decode_json_and_check_error(r, response, j_jwks) == FALSE) return FALSE; /* check to see if it is valid metadata */ if (oidc_metadata_jwks_is_valid(r, jwks_uri, *j_jwks) == FALSE) return FALSE; /* store the JWKs in the cache */ cfg->cache->set(r, OIDC_CACHE_SECTION_JWKS, oidc_metadata_jwks_cache_key(r, jwks_uri->url), response, apr_time_now() + apr_time_from_sec(jwks_uri->refresh_interval)); return TRUE; }
/* * get claims from the OP UserInfo endpoint using the provided access_token */ apr_byte_t oidc_proto_resolve_userinfo(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *access_token, const char **response, json_t **claims) { oidc_debug(r, "enter, endpoint=%s, access_token=%s", provider->userinfo_endpoint_url, access_token); /* get the JSON response */ if (oidc_util_http_get(r, provider->userinfo_endpoint_url, NULL, NULL, access_token, provider->ssl_validate_server, response, cfg->http_timeout_long, cfg->outgoing_proxy) == FALSE) return FALSE; /* decode and check for an "error" response */ return oidc_util_decode_json_and_check_error(r, *response, claims); }
/* * based on an account name, perform OpenID Connect Provider Issuer Discovery to find out the issuer and obtain and store its metadata */ apr_byte_t oidc_proto_account_based_discovery(request_rec *r, oidc_cfg *cfg, const char *acct, char **issuer) { // TODO: maybe show intermediate/progress screen "discovering..." oidc_debug(r, "enter, acct=%s", acct); const char *resource = apr_psprintf(r->pool, "acct:%s", acct); const char *domain = strrchr(acct, '@'); if (domain == NULL) { oidc_error(r, "invalid account name"); return FALSE; } domain++; const char *url = apr_psprintf(r->pool, "https://%s/.well-known/webfinger", domain); apr_table_t *params = apr_table_make(r->pool, 1); apr_table_addn(params, "resource", resource); apr_table_addn(params, "rel", "http://openid.net/specs/connect/1.0/issuer"); const char *response = NULL; if (oidc_util_http_get(r, url, params, NULL, NULL, cfg->provider.ssl_validate_server, &response, cfg->http_timeout_short, cfg->outgoing_proxy) == FALSE) { /* errors will have been logged by now */ return FALSE; } /* decode and see if it is not an error response somehow */ json_t *j_response = NULL; if (oidc_util_decode_json_and_check_error(r, response, &j_response) == FALSE) return FALSE; /* get the links parameter */ json_t *j_links = json_object_get(j_response, "links"); if ((j_links == NULL) || (!json_is_array(j_links))) { oidc_error(r, "response JSON object did not contain a \"links\" array"); json_decref(j_response); return FALSE; } /* get the one-and-only object in the "links" array */ json_t *j_object = json_array_get(j_links, 0); if ((j_object == NULL) || (!json_is_object(j_object))) { oidc_error(r, "response JSON object did not contain a JSON object as the first element in the \"links\" array"); json_decref(j_response); return FALSE; } /* get the href from that object, which is the issuer value */ json_t *j_href = json_object_get(j_object, "href"); if ((j_href == NULL) || (!json_is_string(j_href))) { oidc_error(r, "response JSON object did not contain a \"href\" element in the first \"links\" array object"); json_decref(j_response); return FALSE; } *issuer = apr_pstrdup(r->pool, json_string_value(j_href)); oidc_debug(r, "returning issuer \"%s\" for account \"%s\" after doing successful webfinger-based discovery", *issuer, acct); json_decref(j_response); return TRUE; }
/* * send a code/refresh request to the token endpoint and return the parsed contents */ static apr_byte_t oidc_proto_token_endpoint_request(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, apr_table_t *params, char **id_token, char **access_token, char **token_type, int *expires_in, char **refresh_token) { const char *response = NULL; /* see if we need to do basic auth or auth-through-post-params (both applied through the HTTP POST method though) */ const char *basic_auth = NULL; if ((provider->token_endpoint_auth == NULL) || (apr_strnatcmp(provider->token_endpoint_auth, "client_secret_basic") == 0)) { basic_auth = apr_psprintf(r->pool, "%s:%s", provider->client_id, provider->client_secret); } else { apr_table_addn(params, "client_id", provider->client_id); apr_table_addn(params, "client_secret", provider->client_secret); } /* add any configured extra static parameters to the token endpoint */ oidc_util_table_add_query_encoded_params(r->pool, params, provider->token_endpoint_params); /* send the refresh request to the token endpoint */ if (oidc_util_http_post_form(r, provider->token_endpoint_url, params, basic_auth, NULL, provider->ssl_validate_server, &response, cfg->http_timeout_long, cfg->outgoing_proxy) == FALSE) { oidc_warn(r, "error when calling the token endpoint (%s)", provider->token_endpoint_url); return FALSE; } /* check for errors, the response itself will have been logged already */ json_t *result = NULL; if (oidc_util_decode_json_and_check_error(r, response, &result) == FALSE) return FALSE; /* get the id_token from the parsed response */ oidc_json_object_get_string(r->pool, result, "id_token", id_token, NULL); /* get the access_token from the parsed response */ oidc_json_object_get_string(r->pool, result, "access_token", access_token, NULL); /* get the token type from the parsed response */ oidc_json_object_get_string(r->pool, result, "token_type", token_type, NULL); /* check the new token type */ if (token_type != NULL) { if (oidc_proto_validate_token_type(r, provider, *token_type) == FALSE) { oidc_warn(r, "access token type did not validate, dropping it"); *access_token = NULL; } } /* get the expires_in value */ oidc_json_object_get_int(r->pool, result, "expires_in", expires_in, -1); /* get the refresh_token from the parsed response */ oidc_json_object_get_string(r->pool, result, "refresh_token", refresh_token, NULL); json_decref(result); return TRUE; }
/* * resolves the code received from the OP in to an access_token and id_token and returns the parsed contents */ apr_byte_t oidc_proto_resolve_code(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *code, char **s_idtoken, char **s_access_token, char **s_token_type) { ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: entering"); const char *response = NULL; /* assemble the parameters for a call to the token endpoint */ apr_table_t *params = apr_table_make(r->pool, 5); apr_table_addn(params, "grant_type", "authorization_code"); apr_table_addn(params, "code", code); apr_table_addn(params, "redirect_uri", cfg->redirect_uri); /* see if we need to do basic auth or auth-through-post-params (both applied through the HTTP POST method though) */ const char *basic_auth = NULL; if ((apr_strnatcmp(provider->token_endpoint_auth, "client_secret_basic")) == 0) { basic_auth = apr_psprintf(r->pool, "%s:%s", provider->client_id, provider->client_secret); } else { apr_table_addn(params, "client_id", provider->client_id); apr_table_addn(params, "client_secret", provider->client_secret); } /* if (strcmp(provider->issuer, "https://sts.windows.net/b4ea3de6-839e-4ad1-ae78-c78e5c0cdc06/") == 0) { apr_table_addn(params, "resource", "https://graph.windows.net"); } */ /* resolve the code against the token endpoint */ if (oidc_util_http_call(r, provider->token_endpoint_url, OIDC_HTTP_POST_FORM, params, basic_auth, NULL, provider->ssl_validate_server, &response, cfg->http_timeout_long, cfg->outgoing_proxy) == FALSE) { ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: could not successfully resolve the \"code\" (%s) against the token endpoint (%s)", code, provider->token_endpoint_url); return FALSE; } /* check for errors, the response itself will have been logged already */ json_t *result = NULL; if (oidc_util_decode_json_and_check_error(r, response, &result) == FALSE) return FALSE; /* get the access_token from the parsed response */ json_t *access_token = json_object_get(result, "access_token"); if ((access_token != NULL) && (json_is_string(access_token))) { *s_access_token = apr_pstrdup(r->pool, json_string_value(access_token)); /* log and set the obtained acces_token */ ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: returned access_token: %s", *s_access_token); /* the provider must return the token type */ json_t *token_type = json_object_get(result, "token_type"); if ((token_type == NULL) || (!json_is_string(token_type))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_proto_resolve_code: response JSON object did not contain a token_type string"); json_decref(result); return FALSE; } *s_token_type = apr_pstrdup(r->pool, json_string_value(token_type)); } else { ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: response JSON object did not contain an access_token string"); } /* get the id_token from the response */ json_t *id_token = json_object_get(result, "id_token"); if ((id_token != NULL) && (json_is_string(id_token))) { *s_idtoken = apr_pstrdup(r->pool, json_string_value(id_token)); /* log and set the obtained id_token */ ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: returned id_token: %s", *s_idtoken); } json_decref(result); return TRUE; }
/* * resolve and validate an access_token against the configured Authorization Server */ static apr_byte_t oidc_oauth_resolve_access_token(request_rec *r, oidc_cfg *c, const char *access_token, json_t **token, char **response) { json_t *result = NULL; const char *json = NULL; /* see if we've got the claims for this access_token cached already */ c->cache->get(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, access_token, &json); if (json == NULL) { /* not cached, go out and validate the access_token against the Authorization server and get the JSON claims back */ if (oidc_oauth_validate_access_token(r, c, access_token, &json) == FALSE) { oidc_error(r, "could not get a validation response from the Authorization server"); return FALSE; } /* decode and see if it is not an error response somehow */ if (oidc_util_decode_json_and_check_error(r, json, &result) == FALSE) return FALSE; json_t *active = json_object_get(result, "active"); apr_time_t cache_until; if (active != NULL) { if (json_is_boolean(active)) { if (!json_is_true(active)) { oidc_debug(r, "\"active\" boolean object with value \"false\" found in response JSON object"); json_decref(result); return FALSE; } } else if (json_is_string(active)) { if (apr_strnatcasecmp(json_string_value(active), "true") != 0) { oidc_debug(r, "\"active\" string object with value that is not equal to \"true\" found in response JSON object: %s", json_string_value(active)); json_decref(result); return FALSE; } } else { oidc_debug(r, "no \"active\" boolean or string object found in response JSON object"); json_decref(result); return FALSE; } if (oidc_oauth_parse_and_cache_token_expiry(r, c, result, "exp", TRUE, FALSE, &cache_until) == FALSE) { json_decref(result); return FALSE; } /* set it in the cache so subsequent request don't need to validate the access_token and get the claims anymore */ c->cache->set(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, access_token, json, cache_until); } else { if (oidc_oauth_parse_and_cache_token_expiry(r, c, result, c->oauth.introspection_token_expiry_claim_name, apr_strnatcmp( c->oauth.introspection_token_expiry_claim_format, "absolute") == 0, c->oauth.introspection_token_expiry_claim_required, &cache_until) == FALSE) { json_decref(result); return FALSE; } /* set it in the cache so subsequent request don't need to validate the access_token and get the claims anymore */ c->cache->set(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, access_token, json, cache_until); } } else { /* we got the claims for this access_token in our cache, decode it in to a JSON structure */ json_error_t json_error; result = json_loads(json, 0, &json_error); if (result == NULL) { oidc_error(r, "cached JSON was corrupted: %s", json_error.text); return FALSE; } } /* return the access_token JSON object */ json_t *tkn = json_object_get(result, "access_token"); if ((tkn != NULL) && (json_is_object(tkn))) { /* * assume PingFederate validation: copy over those claims from the access_token * that are relevant for authorization purposes */ json_object_set(tkn, "client_id", json_object_get(result, "client_id")); json_object_set(tkn, "scope", json_object_get(result, "scope")); //oidc_oauth_spaced_string_to_array(r, result, "scope", tkn, "scopes"); /* return only the pimped access_token results */ *token = json_deep_copy(tkn); char *s_token = json_dumps(*token, 0); *response = apr_pstrdup(r->pool, s_token); free(s_token); json_decref(result); } else { //oidc_oauth_spaced_string_to_array(r, result, "scope", result, "scopes"); /* assume spec compliant introspection */ *token = result; *response = apr_pstrdup(r->pool, json); } return TRUE; }
/* * resolve and validate an access_token against the configured Authorization Server */ static apr_byte_t oidc_oauth_resolve_access_token(request_rec *r, oidc_cfg *c, const char *access_token, json_t **token, char **response) { json_t *result = NULL; const char *json = NULL; /* see if we've got the claims for this access_token cached already */ c->cache->get(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, access_token, &json); if (json == NULL) { /* not cached, go out and validate the access_token against the Authorization server and get the JSON claims back */ if (oidc_oauth_validate_access_token(r, c, access_token, &json) == FALSE) { oidc_error(r, "could not get a validation response from the Authorization server"); return FALSE; } /* decode and see if it is not an error response somehow */ if (oidc_util_decode_json_and_check_error(r, json, &result) == FALSE) return FALSE; json_t *active = json_object_get(result, "active"); if (active != NULL) { if ((!json_is_boolean(active)) || (!json_is_true(active))) { oidc_debug(r, "no \"active\" boolean object with value \"true\" found in response JSON object"); json_decref(result); return FALSE; } json_t *exp = json_object_get(result, "exp"); if ((exp != NULL) && (json_is_number(exp))) { /* set it in the cache so subsequent request don't need to validate the access_token and get the claims anymore */ c->cache->set(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, access_token, json, apr_time_from_sec(json_integer_value(exp))); } else if (json_integer_value(exp) <= 0) { oidc_debug(r, "response JSON object did not contain an \"exp\" integer number; introspection result will not be cached"); } } else { /* assume PingFederate validation: get and check the expiry timestamp */ json_t *expires_in = json_object_get(result, "expires_in"); if ((expires_in == NULL) || (!json_is_number(expires_in))) { oidc_error(r, "response JSON object did not contain an \"expires_in\" number"); json_decref(result); return FALSE; } if (json_integer_value(expires_in) <= 0) { oidc_warn(r, "\"expires_in\" number <= 0 (%" JSON_INTEGER_FORMAT "); token already expired...", json_integer_value(expires_in)); json_decref(result); return FALSE; } /* set it in the cache so subsequent request don't need to validate the access_token and get the claims anymore */ c->cache->set(r, OIDC_CACHE_SECTION_ACCESS_TOKEN, access_token, json, apr_time_now() + apr_time_from_sec(json_integer_value(expires_in))); } } else { /* we got the claims for this access_token in our cache, decode it in to a JSON structure */ json_error_t json_error; result = json_loads(json, 0, &json_error); if (result == NULL) { oidc_error(r, "cached JSON was corrupted: %s", json_error.text); return FALSE; } } /* return the access_token JSON object */ json_t *tkn = json_object_get(result, "access_token"); if ((tkn != NULL) && (json_is_object(tkn))) { /* * assume PingFederate validation: copy over those claims from the access_token * that are relevant for authorization purposes */ json_object_set(tkn, "client_id", json_object_get(result, "client_id")); json_object_set(tkn, "scope", json_object_get(result, "scope")); //oidc_oauth_spaced_string_to_array(r, result, "scope", tkn, "scopes"); /* return only the pimped access_token results */ *token = json_deep_copy(tkn); char *s_token = json_dumps(*token, 0); *response = apr_pstrdup(r->pool, s_token); free(s_token); json_decref(result); } else { //oidc_oauth_spaced_string_to_array(r, result, "scope", result, "scopes"); /* assume spec compliant introspection */ *token = result; *response = apr_pstrdup(r->pool, json); } return TRUE; }
/* * resolves the code received from the OP in to an access_token and id_token and returns the parsed contents */ apr_byte_t oidc_proto_resolve_code(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *code, char **s_idtoken, char **s_access_token, char **s_token_type) { ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: entering"); const char *response = NULL; /* assemble the parameters for a call to the token endpoint */ apr_table_t *params = apr_table_make(r->pool, 5); apr_table_addn(params, "grant_type", "authorization_code"); apr_table_addn(params, "code", code); apr_table_addn(params, "redirect_uri", cfg->redirect_uri); /* see if we need to do basic auth or auth-through-post-params (both applied through the HTTP POST method though) */ const char *basic_auth = NULL; if ((apr_strnatcmp(provider->token_endpoint_auth, "client_secret_basic")) == 0) { basic_auth = apr_psprintf(r->pool, "%s:%s", provider->client_id, provider->client_secret); } else { apr_table_addn(params, "client_id", provider->client_id); apr_table_addn(params, "client_secret", provider->client_secret); } /* see if we've configured any extra static parameters to the token endpoint */ if (provider->token_endpoint_params != NULL) { const char *key, *val; const char *p = provider->token_endpoint_params; while (*p && (val = ap_getword(r->pool, &p, '&'))) { key = ap_getword(r->pool, &val, '='); ap_unescape_url((char *) key); ap_unescape_url((char *) val); apr_table_addn(params, key, val); } } /* resolve the code against the token endpoint */ if (oidc_util_http_post_form(r, provider->token_endpoint_url, params, basic_auth, NULL, provider->ssl_validate_server, &response, cfg->http_timeout_long, cfg->outgoing_proxy) == FALSE) { ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: could not successfully resolve the \"code\" (%s) against the token endpoint (%s)", code, provider->token_endpoint_url); return FALSE; } /* check for errors, the response itself will have been logged already */ json_t *result = NULL; if (oidc_util_decode_json_and_check_error(r, response, &result) == FALSE) return FALSE; /* get the access_token from the parsed response */ json_t *access_token = json_object_get(result, "access_token"); if ((access_token != NULL) && (json_is_string(access_token))) { *s_access_token = apr_pstrdup(r->pool, json_string_value(access_token)); /* log and set the obtained acces_token */ ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: returned access_token: %s", *s_access_token); /* the provider must return the token type */ json_t *token_type = json_object_get(result, "token_type"); if ((token_type == NULL) || (!json_is_string(token_type))) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "oidc_proto_resolve_code: response JSON object did not contain a token_type string"); json_decref(result); return FALSE; } *s_token_type = apr_pstrdup(r->pool, json_string_value(token_type)); } else { ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: response JSON object did not contain an access_token string"); } /* get the id_token from the response */ json_t *id_token = json_object_get(result, "id_token"); if ((id_token != NULL) && (json_is_string(id_token))) { *s_idtoken = apr_pstrdup(r->pool, json_string_value(id_token)); /* log and set the obtained id_token */ ap_log_rerror(APLOG_MARK, OIDC_DEBUG, 0, r, "oidc_proto_resolve_code: returned id_token: %s", *s_idtoken); } json_decref(result); return TRUE; }
/* * register the client with the OP using Dynamic Client Registration */ static apr_byte_t oidc_metadata_client_register(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, json_t **j_client, const char **response) { /* get a handle to the directory config */ oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config, &auth_openidc_module); /* assemble the JSON registration request */ json_t *data = json_object(); json_object_set_new(data, "client_name", json_string(provider->client_name)); json_object_set_new(data, "redirect_uris", json_pack("[s]", cfg->redirect_uri)); json_t *response_types = json_array(); apr_array_header_t *flows = oidc_proto_supported_flows(r->pool); int i; for (i = 0; i < flows->nelts; i++) { json_array_append_new(response_types, json_string(((const char**) flows->elts)[i])); } json_object_set_new(data, "response_types", response_types); if (provider->client_contact != NULL) { json_object_set_new(data, "contacts", json_pack("[s]", provider->client_contact)); } if (provider->client_jwks_uri) { json_object_set_new(data, "jwks_uri", json_string(provider->client_jwks_uri)); } else if (cfg->public_keys != NULL) { json_object_set_new(data, "jwks_uri", json_string( apr_psprintf(r->pool, "%s?jwks=rsa", cfg->redirect_uri))); } if (provider->id_token_signed_response_alg != NULL) { json_object_set_new(data, "id_token_signed_response_alg", json_string(provider->id_token_signed_response_alg)); } if (provider->id_token_encrypted_response_alg != NULL) { json_object_set_new(data, "id_token_encrypted_response_alg", json_string(provider->id_token_encrypted_response_alg)); } if (provider->id_token_encrypted_response_enc != NULL) { json_object_set_new(data, "id_token_encrypted_response_enc", json_string(provider->id_token_encrypted_response_enc)); } if (provider->userinfo_signed_response_alg != NULL) { json_object_set_new(data, "userinfo_signed_response_alg", json_string(provider->userinfo_signed_response_alg)); } if (provider->userinfo_encrypted_response_alg != NULL) { json_object_set_new(data, "userinfo_encrypted_response_alg", json_string(provider->userinfo_encrypted_response_alg)); } if (provider->userinfo_encrypted_response_enc != NULL) { json_object_set_new(data, "userinfo_encrypted_response_enc", json_string(provider->userinfo_encrypted_response_enc)); } json_object_set_new(data, "initiate_login_uri", json_string(cfg->redirect_uri)); json_object_set_new(data, "logout_uri", json_string( apr_psprintf(r->pool, "%s?logout=%s", cfg->redirect_uri, OIDC_GET_STYLE_LOGOUT_PARAM_VALUE))); if (cfg->default_slo_url != NULL) { json_object_set_new(data, "post_logout_redirect_uris", json_pack("[s]", cfg->default_slo_url)); } if (provider->registration_endpoint_json != NULL) { json_error_t json_error; json_t *json = json_loads(provider->registration_endpoint_json, 0, &json_error); if (json == NULL) { oidc_error(r, "JSON parsing returned an error: %s", json_error.text); } else { if (!json_is_object(json)) { oidc_error(r, "parsed JSON did not contain a JSON object"); } else { oidc_util_json_merge(json, data); } json_decref(json); } } /* dynamically register the client with the specified parameters */ if (oidc_util_http_post_json(r, provider->registration_endpoint_url, data, NULL, provider->registration_token, provider->ssl_validate_server, response, cfg->http_timeout_short, cfg->outgoing_proxy, dir_cfg->pass_cookies) == FALSE) { json_decref(data); return FALSE; } json_decref(data); /* decode and see if it is not an error response somehow */ return oidc_util_decode_json_and_check_error(r, *response, j_client); }