/* * 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_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); }
/* * 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; }
/* * 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); }
/* * validate an access token against the validation endpoint of the Authorization server and gets a response back */ static apr_byte_t oidc_oauth_validate_access_token(request_rec *r, oidc_cfg *c, const char *token, 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 parameters to call the token endpoint for validation */ apr_table_t *params = apr_table_make(r->pool, 4); /* add any configured extra static parameters to the introspection endpoint */ oidc_util_table_add_query_encoded_params(r->pool, params, c->oauth.introspection_endpoint_params); /* add the access_token itself */ apr_table_addn(params, c->oauth.introspection_token_param_name, token); /* see if we want to do basic auth or post-param-based auth */ const char *basic_auth = NULL; if ((c->oauth.client_id != NULL) && (c->oauth.client_secret != NULL)) { if ((c->oauth.introspection_endpoint_auth != NULL) && (apr_strnatcmp(c->oauth.introspection_endpoint_auth, "client_secret_post") == 0)) { apr_table_addn(params, "client_id", c->oauth.client_id); apr_table_addn(params, "client_secret", c->oauth.client_secret); } else { basic_auth = apr_psprintf(r->pool, "%s:%s", c->oauth.client_id, c->oauth.client_secret); } } /* call the endpoint with the constructed parameter set and return the resulting response */ return apr_strnatcmp(c->oauth.introspection_endpoint_method, "GET") == 0 ? oidc_util_http_get(r, c->oauth.introspection_endpoint_url, params, basic_auth, NULL, c->oauth.ssl_validate_server, response, c->http_timeout_long, c->outgoing_proxy, dir_cfg->pass_cookies) : oidc_util_http_post_form(r, c->oauth.introspection_endpoint_url, params, basic_auth, NULL, c->oauth.ssl_validate_server, response, c->http_timeout_long, c->outgoing_proxy, dir_cfg->pass_cookies); }
/* * 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; }