예제 #1
0
/*
 * 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;
}
예제 #2
0
/*
 * 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);
}
예제 #3
0
/*
 * 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;
}
예제 #4
0
/*
 * 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;
}
예제 #5
0
/*
 * 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);
}
예제 #6
0
/*
 * 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;
}
예제 #7
0
/*
 * 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;
}
예제 #8
0
/*
 * 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;
}
예제 #9
0
/*
 * 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;
}
예제 #10
0
/*
 * 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;
}
예제 #11
0
/*
 * 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;
}
예제 #12
0
/*
 * 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);
}