/* * parse the JSON client metadata in to a oidc_provider_t struct */ apr_byte_t oidc_metadata_client_parse(request_rec *r, oidc_cfg *cfg, json_t *j_client, oidc_provider_t *provider) { /* get a handle to the client_id we need to use for this provider */ oidc_json_object_get_string(r->pool, j_client, "client_id", &provider->client_id, NULL); /* get a handle to the client_secret we need to use for this provider */ oidc_json_object_get_string(r->pool, j_client, "client_secret", &provider->client_secret, NULL); /* see if the token endpoint auth method defined in the client metadata overrides the provider one */ char *token_endpoint_auth = NULL; oidc_json_object_get_string(r->pool, j_client, "token_endpoint_auth_method", &token_endpoint_auth, NULL); if (token_endpoint_auth != NULL) { if ((apr_strnatcmp(token_endpoint_auth, "client_secret_post") == 0) || (apr_strnatcmp(token_endpoint_auth, "client_secret_basic") == 0)) { provider->token_endpoint_auth = apr_pstrdup(r->pool, token_endpoint_auth); } else { oidc_warn(r, "unsupported client auth method \"%s\" in client metadata for entry \"token_endpoint_auth_method\"", token_endpoint_auth); } } /* determine the response type if not set by .conf */ if (provider->response_type == NULL) { provider->response_type = cfg->provider.response_type; /* "response_types" is an array in the client metadata as by spec */ json_t *j_response_types = json_object_get(j_client, "response_types"); if ((j_response_types != NULL) && (json_is_array(j_response_types))) { /* if there's an array we'll prefer the configured response_type if supported */ if (oidc_util_json_array_has_value(r, j_response_types, provider->response_type) == FALSE) { /* if the configured response_type is not supported, we'll fallback to the first one that is listed */ json_t *j_response_type = json_array_get(j_response_types, 0); if (json_is_string(j_response_type)) { provider->response_type = apr_pstrdup(r->pool, json_string_value(j_response_type)); } } } } return TRUE; }
/* * get a value from the session based on the name from a name/value pair */ apr_byte_t oidc_session_get(request_rec *r, oidc_session_t *z, const char *key, const char **value) { /* just return the value for the key */ oidc_json_object_get_string(r->pool, z->state, key, (char **) value, NULL); 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; }
/* * parse the JSON provider metadata in to a oidc_provider_t struct but do not override values already set */ apr_byte_t oidc_metadata_provider_parse(request_rec *r, json_t *j_provider, oidc_provider_t *provider) { if (provider->issuer == NULL) { /* get the "issuer" from the provider metadata */ oidc_json_object_get_string(r->pool, j_provider, "issuer", &provider->issuer, NULL); } if (provider->authorization_endpoint_url == NULL) { /* get a handle to the authorization endpoint */ oidc_json_object_get_string(r->pool, j_provider, "authorization_endpoint", &provider->authorization_endpoint_url, NULL); } if (provider->token_endpoint_url == NULL) { /* get a handle to the token endpoint */ oidc_json_object_get_string(r->pool, j_provider, "token_endpoint", &provider->token_endpoint_url, NULL); } if (provider->userinfo_endpoint_url == NULL) { /* get a handle to the user_info endpoint */ oidc_json_object_get_string(r->pool, j_provider, "userinfo_endpoint", &provider->userinfo_endpoint_url, NULL); } if (provider->jwks_uri == NULL) { /* get a handle to the jwks_uri endpoint */ oidc_json_object_get_string(r->pool, j_provider, "jwks_uri", &provider->jwks_uri, NULL); } if (provider->registration_endpoint_url == NULL) { /* get a handle to the client registration endpoint */ oidc_json_object_get_string(r->pool, j_provider, "registration_endpoint", &provider->registration_endpoint_url, NULL); } if (provider->check_session_iframe == NULL) { /* get a handle to the check session iframe */ oidc_json_object_get_string(r->pool, j_provider, "check_session_iframe", &provider->check_session_iframe, NULL); } if (provider->end_session_endpoint == NULL) { /* get a handle to the end session endpoint */ oidc_json_object_get_string(r->pool, j_provider, "end_session_endpoint", &provider->end_session_endpoint, NULL); } if (provider->token_endpoint_auth == NULL) { /* find a supported token_endpoint_auth_method in the provider metadata */ json_t *j_token_endpoint_auth_methods_supported = json_object_get( j_provider, "token_endpoint_auth_methods_supported"); const char *token_endpoint_auth = NULL; /* loop through the array provided by the issuer and see if there's a supported method */ if ((j_token_endpoint_auth_methods_supported != NULL) && (json_is_array(j_token_endpoint_auth_methods_supported))) { 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_error(r, "unhandled in-array JSON object type [%d] in provider metadata for entry \"token_endpoint_auth_methods_supported\"", elem->type); continue; } /* take first supported method and prefer post over basic */ if ((apr_strnatcmp(json_string_value(elem), "client_secret_post") == 0) || (apr_strnatcmp(json_string_value(elem), "client_secret_basic") == 0)) { token_endpoint_auth = json_string_value(elem); break; } } } /* store the method if found */ if (token_endpoint_auth != NULL) { provider->token_endpoint_auth = apr_pstrdup(r->pool, token_endpoint_auth); } } return TRUE; }
/* * parse the JSON conf metadata in to a oidc_provider_t struct */ apr_byte_t oidc_metadata_conf_parse(request_rec *r, oidc_cfg *cfg, json_t *j_conf, oidc_provider_t *provider) { oidc_json_object_get_string(r->pool, j_conf, "client_jwks_uri", &provider->client_jwks_uri, cfg->provider.client_jwks_uri); oidc_json_object_get_string(r->pool, j_conf, "id_token_signed_response_alg", &provider->id_token_signed_response_alg, cfg->provider.id_token_signed_response_alg); oidc_json_object_get_string(r->pool, j_conf, "id_token_encrypted_response_alg", &provider->id_token_encrypted_response_alg, cfg->provider.id_token_encrypted_response_alg); oidc_json_object_get_string(r->pool, j_conf, "id_token_encrypted_response_enc", &provider->id_token_encrypted_response_enc, cfg->provider.id_token_encrypted_response_enc); /* get the (optional) signing & encryption settings for the userinfo response */ oidc_json_object_get_string(r->pool, j_conf, "userinfo_signed_response_alg", &provider->userinfo_signed_response_alg, cfg->provider.userinfo_signed_response_alg); oidc_json_object_get_string(r->pool, j_conf, "userinfo_encrypted_response_alg", &provider->userinfo_encrypted_response_alg, cfg->provider.userinfo_encrypted_response_alg); oidc_json_object_get_string(r->pool, j_conf, "userinfo_encrypted_response_enc", &provider->userinfo_encrypted_response_enc, cfg->provider.userinfo_encrypted_response_enc); /* find out if we need to perform SSL server certificate validation on the token_endpoint and user_info_endpoint for this provider */ oidc_json_object_get_int(r->pool, j_conf, "ssl_validate_server", &provider->ssl_validate_server, cfg->provider.ssl_validate_server); /* find out what scopes we should be requesting from this provider */ // TODO: use the provider "scopes_supported" to mix-and-match with what we've configured for the client // TODO: check that "openid" is always included in the configured scopes, right? oidc_json_object_get_string(r->pool, j_conf, "scope", &provider->scope, cfg->provider.scope); /* see if we've got a custom JWKs refresh interval */ oidc_json_object_get_int(r->pool, j_conf, "jwks_refresh_interval", &provider->jwks_refresh_interval, cfg->provider.jwks_refresh_interval); /* see if we've got a custom IAT slack interval */ oidc_json_object_get_int(r->pool, j_conf, "idtoken_iat_slack", &provider->idtoken_iat_slack, cfg->provider.idtoken_iat_slack); /* see if we've got a custom max session duration */ oidc_json_object_get_int(r->pool, j_conf, "session_max_duration", &provider->session_max_duration, cfg->provider.session_max_duration); /* see if we've got custom authentication request parameter values */ oidc_json_object_get_string(r->pool, j_conf, "auth_request_params", &provider->auth_request_params, cfg->provider.auth_request_params); /* see if we've got custom token endpoint parameter values */ oidc_json_object_get_string(r->pool, j_conf, "token_endpoint_params", &provider->token_endpoint_params, cfg->provider.token_endpoint_params); /* get the response mode to use */ oidc_json_object_get_string(r->pool, j_conf, "response_mode", &provider->response_mode, cfg->provider.response_mode); /* get the client name */ oidc_json_object_get_string(r->pool, j_conf, "client_name", &provider->client_name, cfg->provider.client_name); /* get the client contact */ oidc_json_object_get_string(r->pool, j_conf, "client_contact", &provider->client_contact, cfg->provider.client_contact); /* get the dynamic client registration token */ oidc_json_object_get_string(r->pool, j_conf, "registration_token", &provider->registration_token, cfg->provider.registration_token); /* see if we've got custom registration request parameter values */ oidc_json_object_get_string(r->pool, j_conf, "registration_endpoint_json", &provider->registration_endpoint_json, cfg->provider.registration_endpoint_json); /* get the flow to use */ oidc_json_object_get_string(r->pool, j_conf, "response_type", &provider->response_type, NULL); return TRUE; }