Example #1
0
/*
 * check to see if dynamically registered JSON client metadata is valid and has not expired
 */
static apr_byte_t oidc_metadata_client_is_valid(request_rec *r,
		json_t *j_client, const char *issuer) {

	/* get a handle to the client_id we need to use for this provider */
	json_t *j_client_id = json_object_get(j_client, "client_id");
	if ((j_client_id == NULL) || (!json_is_string(j_client_id))) {
		oidc_error(r,
				"client (%s) JSON metadata did not contain a \"client_id\" string",
				issuer);
		return FALSE;
	}

	/* get a handle to the client_secret we need to use for this provider */
	json_t *j_client_secret = json_object_get(j_client, "client_secret");
	if ((j_client_secret == NULL) || (!json_is_string(j_client_secret))) {
		oidc_warn(r,
				"client (%s) JSON metadata did not contain a \"client_secret\" string",
				issuer);
		//return FALSE;
	}

	/* the expiry timestamp from the JSON object */
	json_t *expires_at = json_object_get(j_client, "client_secret_expires_at");
	if ((expires_at == NULL) || (!json_is_integer(expires_at))) {
		oidc_debug(r,
				"client (%s) metadata did not contain a \"client_secret_expires_at\" setting",
				issuer);
		/* assume that it never expires */
		return TRUE;
	}

	/* see if it is unrestricted */
	if (json_integer_value(expires_at) == 0) {
		oidc_debug(r,
				"client (%s) metadata never expires (client_secret_expires_at=0)",
				issuer);
		return TRUE;
	}

	/* check if the value >= now */
	if (apr_time_sec(apr_time_now()) > json_integer_value(expires_at)) {
		oidc_warn(r, "client (%s) secret expired", issuer);
		return FALSE;
	}

	oidc_debug(r, "client (%s) metadata is valid", issuer);

	return TRUE;
}
Example #2
0
/*
 * set the unique user identifier that will be propagated in the Apache r->user and REMOTE_USER variables
 */
static apr_byte_t oidc_oauth_set_remote_user(request_rec *r, oidc_cfg *c,
		json_t *token) {

	/* get the configured claim name to populate REMOTE_USER with (defaults to "Username") */
	char *claim_name = apr_pstrdup(r->pool, c->oauth.remote_user_claim.claim_name);

	/* get the claim value from the resolved token JSON response to use as the REMOTE_USER key */
	json_t *username = json_object_get(token, claim_name);
	if ((username == NULL) || (!json_is_string(username))) {
		oidc_warn(r, "response JSON object did not contain a \"%s\" string",
				claim_name);
		return FALSE;
	}

	r->user = apr_pstrdup(r->pool, json_string_value(username));

	if (c->oauth.remote_user_claim.reg_exp != NULL) {

		char *error_str = NULL;
		if (oidc_util_regexp_first_match(r->pool, r->user, c->oauth.remote_user_claim.reg_exp, &r->user, &error_str) == FALSE) {
			oidc_error(r, "oidc_util_regexp_first_match failed: %s", error_str);
			r->user = NULL;
			return FALSE;
		}
	}

	oidc_debug(r, "set REMOTE_USER to claim %s=%s", claim_name,
			json_string_value(username));

	return TRUE;
}
Example #3
0
/*
 * Apache >=2.4 authorization routine: match the claims from the authenticated user against the Require primitive
 */
authz_status oidc_authz_worker24(request_rec *r, const json_t * const claims, const char *require_args) {

	int count_oauth_claims = 0;
	const char *t, *w;

	/* needed for anonymous authentication */
	if (r->user == NULL) return AUTHZ_DENIED_NO_USER;

	/* if no claims, impossible to satisfy */
	if (!claims) return AUTHZ_DENIED;

	/* loop over the Required specifications */
	t = require_args;
	while ((w = ap_getword_conf(r->pool, &t)) && w[0]) {

		count_oauth_claims++;

		oidc_debug(r, "evaluating claim specification: %s", w);

		/* see if we can match any of out input claims against this Require'd value */
		if (oidc_authz_match_claim(r, w, claims) == TRUE) {

			oidc_debug(r, "require claim '%s' matched", w);
			return AUTHZ_GRANTED;
		}
	}

	/* if there wasn't anything after the Require claims directive... */
	if (count_oauth_claims == 0) {
		oidc_warn(r,
				"'require claim' missing specification(s) in configuration, denying");
	}

	return AUTHZ_DENIED;
}
Example #4
0
/*
 * load the session from the request context, create a new one if no luck
 */
static apr_status_t oidc_session_load_22(request_rec *r, session_rec **zz) {

	oidc_cfg *c = ap_get_module_config(r->server->module_config,
			&auth_openidc_module);

	/* first see if this is a sub-request and it was set already in the main request */
	if (((*zz) = (session_rec *) oidc_request_state_get(r, "session")) != NULL) {
		oidc_debug(r, "loading session from request state");
		return APR_SUCCESS;
	}

	/* allocate space for the session object and fill it */
	session_rec *z = (*zz = apr_pcalloc(r->pool, sizeof(session_rec)));
	z->pool = r->pool;

	/* get a new uuid for this session */
	z->uuid = (apr_uuid_t *) apr_pcalloc(z->pool, sizeof(apr_uuid_t));
	apr_uuid_get(z->uuid);

	z->remote_user = NULL;
	z->encoded = NULL;
	z->entries = apr_table_make(z->pool, 10);

	apr_status_t rc = APR_SUCCESS;
	if (c->session_type == OIDC_SESSION_TYPE_22_SERVER_CACHE) {
		/* load the session from the cache */
		rc = oidc_session_load_cache(r, z);
	} else if (c->session_type == OIDC_SESSION_TYPE_22_CLIENT_COOKIE) {
		/* load the session from a self-contained cookie */
		rc = oidc_session_load_cookie(r, z);
	} else {
		oidc_error(r, "oidc_session_load_22: unknown session type: %d",
				c->session_type);
		rc = APR_EGENERAL;
	}

	/* see if it worked out */
	if (rc != APR_SUCCESS)
		return rc;

	/* yup, now decode the info */
	if (oidc_session_identity_decode(r, z) != APR_SUCCESS)
		return APR_EGENERAL;

	/* check whether it has expired */
	if (apr_time_now() > z->expiry) {

		oidc_warn(r, "session restored from cache has expired");
		apr_table_clear(z->entries);
		z->expiry = 0;
		z->encoded = NULL;

		return APR_EGENERAL;
	}

	/* store this session in the request context, so it is available to sub-requests */
	oidc_request_state_set(r, "session", (const char *) z);

	return APR_SUCCESS;
}
Example #5
0
/*
 * parse (custom/configurable) token expiry claim in introspection result
 */
static apr_byte_t oidc_oauth_parse_and_cache_token_expiry(request_rec *r,
		oidc_cfg *c, json_t *introspection_response,
		const char *expiry_claim_name, int expiry_format_absolute,
		int expiry_claim_is_mandatory, apr_time_t *cache_until) {

	oidc_debug(r, "expiry_claim_name=%s, expiry_format_absolute=%d, expiry_claim_is_mandatory=%d", expiry_claim_name, expiry_format_absolute, expiry_claim_is_mandatory);

	json_t *expiry = json_object_get(introspection_response, expiry_claim_name);

	if (expiry == NULL) {
		if (expiry_claim_is_mandatory) {
			oidc_error(r,
					"introspection response JSON object did not contain an \"%s\" claim",
					expiry_claim_name);
			return FALSE;
		}
		return TRUE;
	}

	if (!json_is_integer(expiry)) {
		if (expiry_claim_is_mandatory) {
			oidc_error(r,
					"introspection response JSON object contains a \"%s\" claim but it is not a JSON integer",
					expiry_claim_name);
			return FALSE;
		}
		oidc_warn(r,
				"introspection response JSON object contains a \"%s\" claim that is not an (optional) JSON integer: the introspection result will NOT be cached",
				expiry_claim_name);
		return TRUE;
	}

	json_int_t value = json_integer_value(expiry);
	if (value <= 0) {
		oidc_warn(r,
				"introspection response JSON object integer number value <= 0 (%ld); introspection result will not be cached",
				(long)value);
		return TRUE;
	}

	*cache_until = apr_time_from_sec(value);
	if (expiry_format_absolute == FALSE)
		(*cache_until) += apr_time_now();

	return TRUE;
}
Example #6
0
/*
 * load a session from the cache/cookie
 */
apr_byte_t oidc_session_load(request_rec *r, oidc_session_t **zz) {
	oidc_cfg *c = ap_get_module_config(r->server->module_config,
			&auth_openidc_module);

	apr_byte_t rc = FALSE;
	const char *ses_p_tb_id = NULL, *env_p_tb_id = NULL;

	/* allocate space for the session object and fill it */
	oidc_session_t *z = (*zz = apr_pcalloc(r->pool, sizeof(oidc_session_t)));
	oidc_session_clear(r, z);

	if (c->session_type == OIDC_SESSION_TYPE_SERVER_CACHE)
		/* load the session from the cache */
		rc = oidc_session_load_cache(r, z);

	/* if we get here we configured client-cookie or retrieving from the cache failed */
	if ((c->session_type == OIDC_SESSION_TYPE_CLIENT_COOKIE)
			|| ((rc == FALSE) && oidc_cfg_session_cache_fallback_to_cookie(r)))
		/* load the session from a self-contained cookie */
		rc = oidc_session_load_cookie(r, c, z);

	if ((rc == TRUE) && (z->state != NULL)) {

		json_t *j_expires = json_object_get(z->state, OIDC_SESSION_EXPIRY_KEY);
		if (j_expires)
			z->expiry = apr_time_from_sec(json_integer_value(j_expires));

		/* check whether it has expired */
		if (apr_time_now() > z->expiry) {

			oidc_warn(r, "session restored from cache has expired");
			oidc_session_clear(r, z);

		} else {

			oidc_session_get(r, z, OIDC_SESSION_PROVIDED_TOKEN_BINDING_KEY,
					&ses_p_tb_id);

			if (ses_p_tb_id != NULL) {
				env_p_tb_id = oidc_util_get_provided_token_binding_id(r);
				if ((env_p_tb_id == NULL)
						|| (apr_strnatcmp(env_p_tb_id, ses_p_tb_id) != 0)) {
					oidc_error(r,
							"the Provided Token Binding ID stored in the session doesn't match the one presented by the user agent");
					oidc_session_clear(r, z);
				}
			}

			oidc_session_get(r, z, OIDC_SESSION_REMOTE_USER_KEY,
					&z->remote_user);
		}
	}

	return rc;
}
/*
 * 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;
}
Example #8
0
static apr_status_t oidc_session_load_cookie(request_rec *r, session_rec *z) {
	oidc_dir_cfg *d = ap_get_module_config(r->per_dir_config,
			&auth_openidc_module);

	char *cookieValue = oidc_util_get_cookie(r, d->cookie);
	if (cookieValue != NULL) {
		if (oidc_base64url_decode_decrypt_string(r, (char **) &z->encoded,
				cookieValue) <= 0) {
			//oidc_util_set_cookie(r, d->cookie, "");
			oidc_warn(r, "cookie value possibly corrupted");
			return APR_EGENERAL;
		}
	}

	return APR_SUCCESS;
}
Example #9
0
/*
 * get the key from the (possibly cached) set of JWKs on the jwk_uri that corresponds with the key specified in the header
 */
static apr_jwk_t *oidc_proto_get_key_from_jwk_uri(request_rec *r, oidc_cfg *cfg,
		oidc_provider_t *provider, apr_jwt_header_t *jwt_hdr, const char *type,
		apr_byte_t *refresh) {
	json_t *j_jwks = NULL;
	apr_jwk_t *jwk = NULL;

	/* get the set of JSON Web Keys for this provider (possibly by downloading them from the specified provider->jwk_uri) */
	oidc_metadata_jwks_get(r, cfg, provider, &j_jwks, refresh);
	if (j_jwks == NULL) {
		oidc_error(r, "could not resolve JSON Web Keys");
		return NULL;
	}

	/* get the key corresponding to the kid from the header, referencing the key that was used to sign this message */
	if (oidc_proto_get_key_from_jwks(r, jwt_hdr, j_jwks, type, &jwk) == FALSE) {
		json_decref(j_jwks);
		return NULL;
	}

	/* see what we've got back */
	if ((jwk == NULL) && (refresh == FALSE)) {

		/* we did not get a key, but we have not refreshed the JWKs from the jwks_uri yet */

		oidc_warn(r,
				"could not find a key in the cached JSON Web Keys, doing a forced refresh");

		/* get the set of JSON Web Keys for this provider forcing a fresh download from the specified provider->jwk_uri) */
		*refresh = TRUE;
		oidc_metadata_jwks_get(r, cfg, provider, &j_jwks, refresh);
		if (j_jwks == NULL) {
			oidc_error(r, "could not refresh JSON Web Keys");
			return NULL;
		}

		/* get the key from the refreshed set of JWKs */
		if (oidc_proto_get_key_from_jwks(r, jwt_hdr, j_jwks, type,
				&jwk) == FALSE) {
			json_decref(j_jwks);
			return NULL;
		}
	}

	json_decref(j_jwks);

	return jwk;
}
Example #10
0
/*
 * see if we have provider metadata and check its validity
 * if not, use OpenID Connect Discovery to get it, check it and store it
 */
static apr_byte_t oidc_metadata_provider_get(request_rec *r, oidc_cfg *cfg,
		const char *issuer, json_t **j_provider, apr_byte_t allow_discovery) {

	/* holds the response data/string/JSON from the OP */
	const char *response = NULL;

	/* get the full file path to the provider metadata for this issuer */
	const char *provider_path = oidc_metadata_provider_file_path(r, issuer);

	/* see if we have valid metadata already, if so, return it */
	if (oidc_metadata_file_read_json(r, provider_path, j_provider) == TRUE) {

		/* return the validation result */
		return oidc_metadata_provider_is_valid(r, *j_provider, issuer);
	}

	if (!allow_discovery) {
		oidc_warn(r,
				"no metadata found for the requested issuer (%s), and Discovery is not allowed",
				issuer);
		return FALSE;
	}

	// TODO: how to do validity/expiry checks on provider metadata

	/* assemble the URL to the .well-known OpenID metadata */
	const char *url = apr_psprintf(r->pool, "%s",
			((strstr(issuer, "http://") == issuer)
					|| (strstr(issuer, "https://") == issuer)) ?
							issuer : apr_psprintf(r->pool, "https://%s", issuer));
	url = apr_psprintf(r->pool, "%s%s.well-known/openid-configuration", url,
			url[strlen(url) - 1] != '/' ? "/" : "");

	/* get the metadata for the issuer using OpenID Connect Discovery and validate it */
	if (oidc_metadata_provider_retrieve(r, cfg, issuer, url, j_provider,
			&response) == FALSE)
		return FALSE;

	/* since it is valid, write the obtained provider metadata file */
	if (oidc_metadata_file_write(r, provider_path, response) == FALSE)
		return FALSE;

	return TRUE;
}
Example #11
0
/*
 * check a hash value in the id_token against the corresponding hash calculated over a provided value
 */
static apr_byte_t oidc_proto_validate_hash_value(request_rec *r,
		oidc_provider_t *provider, apr_jwt_t *jwt, const char *response_type,
		const char *value, const char *key,
		apr_array_header_t *required_for_flows) {

	/*
	 * get the hash value from the id_token
	 */
	char *hash = NULL;
	apr_jwt_get_string(r->pool, &jwt->payload.value, key, &hash);

	/*
	 * check if the hash was present
	 */
	if (hash == NULL) {

		/* no hash..., now see if the flow required it */
		int i;
		for (i = 0; i < required_for_flows->nelts; i++) {
			if (oidc_util_spaced_string_equals(r->pool, response_type,
					((const char**) required_for_flows->elts)[i])) {
				oidc_warn(r, "flow is \"%s\", but no %s found in id_token",
						response_type, key);
				return FALSE;
			}
		}

		/* no hash but it was not required anyway */
		return TRUE;
	}

	/*
	 * we have a hash, validate it and return the result
	 */
	return oidc_proto_validate_hash(r, jwt->header.alg, hash, value, key);
}
Example #12
0
static apr_byte_t oidc_authz_match_value(request_rec *r, const char *spec_c,
		json_t *val, const char *key) {

	int i = 0;

	/* see if it is a string and it (case-insensitively) matches the Require'd value */
	if (json_is_string(val)) {

		if (apr_strnatcmp(json_string_value(val), spec_c) == 0)
			return TRUE;

		/* see if it is a integer and it equals the Require'd value */
	} else if (json_is_integer(val)) {

		if (json_integer_value(val) == atoi(spec_c))
			return TRUE;

		/* see if it is a boolean and it (case-insensitively) matches the Require'd value */
	} else if (json_is_boolean(val)) {

		if (apr_strnatcmp(json_is_true(val) ? "true" : "false", spec_c) == 0)
			return TRUE;

		/* if it is an array, we'll walk it */
	} else if (json_is_array(val)) {

		/* compare the claim values */
		for (i = 0; i < json_array_size(val); i++) {

			json_t *elem = json_array_get(val, i);

			if (json_is_string(elem)) {
				/*
				 * approximately compare the claim value (ignoring
				 * whitespace). At this point, spec_c points to the
				 * NULL-terminated value pattern.
				 */
				if (apr_strnatcmp(json_string_value(elem), spec_c) == 0)
					return TRUE;

			} else if (json_is_boolean(elem)) {

				if (apr_strnatcmp(
				json_is_true(elem) ? "true" : "false", spec_c) == 0)
					return TRUE;

			} else if (json_is_integer(elem)) {

				if (json_integer_value(elem) == atoi(spec_c))
					return TRUE;

			} else {

				oidc_warn(r,
						"unhandled in-array JSON object type [%d] for key \"%s\"",
						elem->type, (const char * ) key);
			}

		}

	} else {
		oidc_warn(r, "unhandled JSON object type [%d] for key \"%s\"",
				val->type, (const char * ) key);
	}

	return FALSE;
}
Example #13
0
/*
 * 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;
}
Example #14
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;
}
Example #15
0
/*
 * verify the signature on an id_token
 */
apr_byte_t oidc_proto_idtoken_verify_signature(request_rec *r, oidc_cfg *cfg,
		oidc_provider_t *provider, apr_jwt_t *jwt, apr_byte_t *refresh) {

	apr_byte_t result = FALSE;

	if (apr_jws_signature_is_hmac(r->pool, jwt)) {

		oidc_debug(r,
				"verifying HMAC signature on id_token: header=%s, message=%s",
				jwt->header.value.str, jwt->message);

		result = apr_jws_verify_hmac(r->pool, jwt, provider->client_secret,
				strlen(provider->client_secret));

	} else if (apr_jws_signature_is_rsa(r->pool, jwt)
#if (OPENSSL_VERSION_NUMBER >= 0x01000000)
			|| apr_jws_signature_is_ec(r->pool, jwt)
#endif
					) {

		/* get the key from the JWKs that corresponds with the key specified in the header */
		apr_jwk_t *jwk = oidc_proto_get_key_from_jwk_uri(r, cfg, provider,
				&jwt->header,
				apr_jws_signature_is_rsa(r->pool, jwt) ? "RSA" : "EC", refresh);

		if (jwk != NULL) {

			oidc_debug(r,
					"verifying RSA/EC signature on id_token: header=%s, message=%s",
					jwt->header.value.str, jwt->message);

			result =
					apr_jws_signature_is_rsa(r->pool, jwt) ?
							apr_jws_verify_rsa(r->pool, jwt, jwk) :
#if (OPENSSL_VERSION_NUMBER >= 0x01000000)
							apr_jws_verify_ec(r->pool, jwt, jwk);
#else
			FALSE;
#endif

		} else {

			oidc_warn(r, "could not find a key in the JSON Web Keys");

			if (*refresh == FALSE) {

				oidc_debug(r, "force refresh of the JWKS");

				/* do it again, forcing a JWKS refresh */
				*refresh = TRUE;
				result = oidc_proto_idtoken_verify_signature(r, cfg, provider,
						jwt, refresh);
			}
		}

	} else {

		oidc_warn(r,
				"cannot verify id_token; unsupported algorithm \"%s\", must be RSA or HMAC",
				jwt->header.alg);

	}

	oidc_debug(r, "verification result of signature with algorithm \"%s\": %s",
			jwt->header.alg, (result == TRUE) ? "TRUE" : "FALSE");

	return result;
}
Example #16
0
/*
 * get the key from the JWKs that corresponds with the key specified in the header
 */
static apr_byte_t oidc_proto_get_key_from_jwks(request_rec *r,
		apr_jwt_header_t *jwt_hdr, json_t *j_jwks, const char *type,
		apr_jwk_t **result) {

	char *x5t = NULL;
	apr_jwt_get_string(r->pool, &jwt_hdr->value, "x5t", &x5t);

	oidc_debug(r, "search for kid \"%s\" or thumbprint x5t \"%s\"",
			jwt_hdr->kid, x5t);

	/* get the "keys" JSON array from the JWKs object */
	json_t *keys = json_object_get(j_jwks, "keys");
	if ((keys == NULL) || !(json_is_array(keys))) {
		oidc_error(r, "\"keys\" array element is not a JSON array");
		return FALSE;
	}

	int i;
	for (i = 0; i < json_array_size(keys); i++) {

		/* get the next element in the array */
		json_t *elem = json_array_get(keys, i);

		/* check that it is a JSON object */
		if (!json_is_object(elem)) {
			oidc_warn(r,
					"\"keys\" array element is not a JSON object, skipping");
			continue;
		}

		/* get the key type and see if it is the RSA type that we are looking for */
		json_t *kty = json_object_get(elem, "kty");
		if ((!json_is_string(kty))
				|| (strcmp(json_string_value(kty), type) != 0))
			continue;

		/* see if we were looking for a specific kid, if not we'll return the first one found */
		if ((jwt_hdr->kid == NULL) && (x5t == NULL)) {
			oidc_debug(r, "no kid/x5t to match, return first key found");

			apr_jwk_parse_json(r->pool, elem, NULL, result);
			break;
		}

		/* we are looking for a specific kid, get the kid from the current element */
		json_t *ekid = json_object_get(elem, "kid");
		if ((ekid != NULL) && json_is_string(ekid) && (jwt_hdr->kid != NULL)) {
			/* compare the requested kid against the current element */
			if (apr_strnatcmp(jwt_hdr->kid, json_string_value(ekid)) == 0) {
				oidc_debug(r, "found matching kid: \"%s\"", jwt_hdr->kid);

				apr_jwk_parse_json(r->pool, elem, NULL, result);
				break;
			}
		}

		/* we are looking for a specific x5t, get the x5t from the current element */
		json_t *ex5t = json_object_get(elem, "kid");
		if ((ex5t != NULL) && json_is_string(ex5t) && (x5t != NULL)) {
			/* compare the requested kid against the current element */
			if (apr_strnatcmp(x5t, json_string_value(ex5t)) == 0) {
				oidc_debug(r, "found matching x5t: \"%s\"", x5t);

				apr_jwk_parse_json(r->pool, elem, NULL, result);
				break;
			}
		}

	}

	return TRUE;
}
Example #17
0
/*
 * check the required parameters for the various flows after resolving the authorization code
 */
apr_byte_t oidc_proto_validate_code_response(request_rec *r,
		const char *response_type, char **id_token, char **access_token,
		char **token_type) {

	oidc_debug(r, "enter");

	/*
	 * check id_token parameter
	 */
	if (!oidc_util_spaced_string_contains(r->pool, response_type, "id_token")) {

		if (*id_token == NULL) {
			oidc_error(r,
					"requested flow is \"%s\" but no \"id_token\" parameter found in the code response",
					response_type);
			return FALSE;
		}

	} else {

		if (*id_token != NULL) {
			oidc_warn(r,
					"requested flow is \"%s\" but there is an \"id_token\" parameter in the code response that will be dropped",
					response_type);
			*id_token = NULL;
		}

	}

	/*
	 * check access_token parameter
	 */
	if (!oidc_util_spaced_string_contains(r->pool, response_type, "token")) {

		if (*access_token == NULL) {
			oidc_error(r,
					"requested flow is \"%s\" but no \"access_token\" parameter found in the code response",
					response_type);
			return FALSE;
		}

		if (*token_type == NULL) {
			oidc_error(r,
					"requested flow is \"%s\" but no \"token_type\" parameter found in the code response",
					response_type);
			return FALSE;
		}

	} else {

		if (*access_token != NULL) {
			oidc_warn(r,
					"requested flow is \"%s\" but there is an \"access_token\" parameter in the code response that will be dropped",
					response_type);
			*access_token = NULL;
		}

		if (*token_type != NULL) {
			oidc_warn(r,
					"requested flow is \"%s\" but there is a \"token_type\" parameter in the code response that will be dropped",
					response_type);
			*token_type = NULL;
		}

	}

	return TRUE;
}
Example #18
0
/*
 * check the required parameters for the various flows on receipt of the authorization response
 */
apr_byte_t oidc_proto_validate_authorization_response(request_rec *r,
		const char *response_type, const char *requested_response_mode,
		char **code, char **id_token, char **access_token, char **token_type,
		const char *used_response_mode) {

	oidc_debug(r,
			"enter, response_type=%s, requested_response_mode=%s, code=%s, id_token=%s, access_token=%s, token_type=%s, used_response_mode=%s",
			response_type, requested_response_mode, *code, *id_token,
			*access_token, *token_type, used_response_mode);

	/* check the requested response mode against the one used by the OP */
	if ((requested_response_mode != NULL)
			&& (strcmp(requested_response_mode, used_response_mode)) != 0) {
		/*
		 * only warn because I'm not sure that most OPs will respect a requested
		 * response_mode and rather use the default for the flow
		 */
		oidc_warn(r,
				"requested response_mode is \"%s\" the provider used \"%s\" for the authorization response...",
				requested_response_mode, used_response_mode);
	}

	/*
	 * check code parameter
	 */
	if (oidc_util_spaced_string_contains(r->pool, response_type, "code")) {

		if (*code == NULL) {
			oidc_error(r,
					"requested flow is \"%s\" but no \"code\" parameter found in the authorization response",
					response_type);
			return FALSE;
		}

	} else {

		if (*code != NULL) {
			oidc_warn(r,
					"requested flow is \"%s\" but there is a \"code\" parameter in the authorization response that will be dropped",
					response_type);
			*code = NULL;
		}
	}

	/*
	 * check id_token parameter
	 */
	if (oidc_util_spaced_string_contains(r->pool, response_type, "id_token")) {

		if (*id_token == NULL) {
			oidc_error(r,
					"requested flow is \"%s\" but no \"id_token\" parameter found in the authorization response",
					response_type);
			return FALSE;
		}

	} else {

		if (*id_token != NULL) {
			oidc_warn(r,
					"requested flow is \"%s\" but there is an \"id_token\" parameter in the authorization response that will be dropped",
					response_type);
			*id_token = NULL;
		}

	}

	/*
	 * check access_token parameter
	 */
	if (oidc_util_spaced_string_contains(r->pool, response_type, "token")) {

		if (*access_token == NULL) {
			oidc_error(r,
					"requested flow is \"%s\" but no \"access_token\" parameter found in the authorization response",
					response_type);
			return FALSE;
		}

		if (*token_type == NULL) {
			oidc_error(r,
					"requested flow is \"%s\" but no \"token_type\" parameter found in the authorization response",
					response_type);
			return FALSE;
		}

	} else {

		if (*access_token != NULL) {
			oidc_warn(r,
					"requested flow is \"%s\" but there is an \"access_token\" parameter in the authorization response that will be dropped",
					response_type);
			*access_token = NULL;
		}

		if (*token_type != NULL) {
			oidc_warn(r,
					"requested flow is \"%s\" but there is a \"token_type\" parameter in the authorization response that will be dropped",
					response_type);
			*token_type = NULL;
		}

	}

	return TRUE;
}
Example #19
0
/*
 * store a value in the shared memory cache
 */
static apr_byte_t oidc_cache_shm_set(request_rec *r, const char *section,
		const char *key, const char *value, apr_time_t expiry) {

	oidc_debug(r, "enter, section=\"%s\", key=\"%s\", value size=%llu", section,
			key, value ? (unsigned long long )strlen(value) : 0);

	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
			&auth_openidc_module);
	oidc_cache_cfg_shm_t *context = (oidc_cache_cfg_shm_t *) cfg->cache_cfg;

	oidc_cache_shm_entry_t *match, *free, *lru;
	oidc_cache_shm_entry_t *t;
	apr_time_t current_time;
	int i;
	apr_time_t age;

	const char *section_key = oidc_cache_shm_get_key(r->pool, section, key);

	/* check that the passed in key is valid */
	if (strlen(section_key) > OIDC_CACHE_SHM_KEY_MAX) {
		oidc_error(r, "could not store value since key size is too large (%s)",
				section_key);
		return FALSE;
	}

	/* check that the passed in value is valid */
	if ((value != NULL) && (strlen(value) > (cfg->cache_shm_entry_size_max - sizeof(oidc_cache_shm_entry_t)))) {
		oidc_error(r, "could not store value since value size is too large (%llu > %lu); consider increasing OIDCCacheShmEntrySizeMax",
				(unsigned long long)strlen(value), (unsigned long)(cfg->cache_shm_entry_size_max - sizeof(oidc_cache_shm_entry_t)));
		return FALSE;
	}

	/* grab the global lock */
	if (oidc_cache_mutex_lock(r, context->mutex) == FALSE)
		return FALSE;

	/* get a pointer to the shared memory block */
	t = apr_shm_baseaddr_get(context->shm);

	/* get the current time */
	current_time = apr_time_now();

	/* loop over the block, looking for the key */
	match = NULL;
	free = NULL;
	lru = t;
	for (i = 0; i < cfg->cache_shm_size_max; i++, OIDC_CACHE_SHM_ADD_OFFSET(t, cfg->cache_shm_entry_size_max)) {

		/* see if this slot is free */
		if (t->section_key[0] == '\0') {
			if (free == NULL)
				free = t;
			continue;
		}

		/* see if a value already exists for this key */
		if (apr_strnatcmp(t->section_key, section_key) == 0) {
			match = t;
			break;
		}

		/* see if this slot has expired */
		if (t->expires <= current_time) {
			if (free == NULL)
				free = t;
			continue;
		}

		/* see if this slot was less recently used than the current pointer */
		if (t->access < lru->access) {
			lru = t;
		}

	}

	/* if we have no free slots, issue a warning about the LRU entry */
	if (match == NULL && free == NULL) {
		age = (current_time - lru->access) / 1000000;
		if (age < 3600) {
			oidc_warn(r,
					"dropping LRU entry with age = %" APR_TIME_T_FMT "s, which is less than one hour; consider increasing the shared memory caching space (which is %d now) with the (global) OIDCCacheShmMax setting.",
					age, cfg->cache_shm_size_max);
		}
	}

	/* pick the best slot: choose one with a matching key over a free slot, over a least-recently-used one */
	t = match ? match : (free ? free : lru);

	/* see if we need to clear or set the value */
	if (value != NULL) {

		/* fill out the entry with the provided data */
		strcpy(t->section_key, section_key);
		strcpy(t->value, value);
		t->expires = expiry;
		t->access = current_time;

	} else {

		t->section_key[0] = '\0';
		t->access = 0;

	}

	/* release the global lock */
	oidc_cache_mutex_unlock(r, context->mutex);

	return TRUE;
}
Example #20
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;
}
Example #21
0
/*
 * Apache <2.4 authorization routine: match the claims from the authenticated user against the Require primitive
 */
int oidc_authz_worker(request_rec *r, const json_t * const claims,
		const require_line * const reqs, int nelts) {
	const int m = r->method_number;
	const char *token;
	const char *requirement;
	int i;
	int have_oauthattr = 0;
	int count_oauth_claims = 0;

	/* go through applicable Require directives */
	for (i = 0; i < nelts; ++i) {

		/* ignore this Require if it's in a <Limit> section that exclude this method */
		if (!(reqs[i].method_mask & (AP_METHOD_BIT << m))) {
			continue;
		}

		/* ignore if it's not a "Require claim ..." */
		requirement = reqs[i].requirement;

		token = ap_getword_white(r->pool, &requirement);

		if (apr_strnatcasecmp(token, OIDC_REQUIRE_NAME) != 0) {
			continue;
		}

		/* ok, we have a "Require claim" to satisfy */
		have_oauthattr = 1;

		/*
		 * If we have an applicable claim, but no claims were sent in the request, then we can
		 * just stop looking here, because it's not satisfiable. The code after this loop will
		 * give the appropriate response.
		 */
		if (!claims) {
			break;
		}

		/*
		 * iterate over the claim specification strings in this require directive searching
		 * for a specification that matches one of the claims.
		 */
		while (*requirement) {
			token = ap_getword_conf(r->pool, &requirement);
			count_oauth_claims++;

			oidc_debug(r, "evaluating claim specification: %s", token);

			if (oidc_authz_match_claim(r, token, claims) == TRUE) {

				/* if *any* claim matches, then authorization has succeeded and all of the others are ignored */
				oidc_debug(r, "require claim '%s' matched", token);
				return OK;
			}
		}
	}

	/* if there weren't any "Require claim" directives, we're irrelevant */
	if (!have_oauthattr) {
		oidc_debug(r, "no claim statements found, not performing authz");
		return DECLINED;
	}
	/* if there was a "Require claim", but no actual claims, that's cause to warn the admin of an iffy configuration */
	if (count_oauth_claims == 0) {
		oidc_warn(r,
				"'require claim' missing specification(s) in configuration, declining");
		return DECLINED;
	}

	/* log the event, also in Apache speak */
	oidc_debug(r, "authorization denied for client session");
	ap_note_auth_failure(r);

	return HTTP_UNAUTHORIZED;
}
Example #22
0
/*
 * get the authorization header that should contain a bearer token
 */
static apr_byte_t oidc_oauth_get_bearer_token(request_rec *r,
		const char **access_token) {

	/* get a handle to the directory config */
	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
			&auth_openidc_module);

	*access_token = NULL;

	if ((dir_cfg->oauth_accept_token_in & OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER)
			|| (dir_cfg->oauth_accept_token_in
					== OIDC_OAUTH_ACCEPT_TOKEN_IN_DEFAULT)) {

		/* get the authorization header */
		const char *auth_line;
		auth_line = apr_table_get(r->headers_in, "Authorization");
		if (auth_line) {
			oidc_debug(r, "authorization header found");

			/* look for the Bearer keyword */
			if (apr_strnatcasecmp(ap_getword(r->pool, &auth_line, ' '),
					"Bearer") == 0) {

				/* skip any spaces after the Bearer keyword */
				while (apr_isspace(*auth_line)) {
					auth_line++;
				}

				/* copy the result in to the access_token */
				*access_token = apr_pstrdup(r->pool, auth_line);

			} else {
				oidc_warn(r,
						"client used unsupported authentication scheme: %s",
						r->uri);
			}
		}
	}

	if ((*access_token == NULL) && (r->method_number == M_POST)
			&& (dir_cfg->oauth_accept_token_in & OIDC_OAUTH_ACCEPT_TOKEN_IN_POST)) {
		apr_table_t *params = apr_table_make(r->pool, 8);
		if (oidc_util_read_post_params(r, params) == TRUE) {
			*access_token = apr_table_get(params, "access_token");
		}
	}

	if ((*access_token == NULL)
			&& (dir_cfg->oauth_accept_token_in
					& OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY)) {
		apr_table_t *params = apr_table_make(r->pool, 8);
		oidc_util_read_form_encoded_params(r, params, r->args);
		*access_token = apr_table_get(params, "access_token");
	}

	if ((*access_token == NULL)
			&& (dir_cfg->oauth_accept_token_in
					& OIDC_OAUTH_ACCEPT_TOKEN_IN_COOKIE)) {

		const char *cookie_name = apr_hash_get(
				dir_cfg->oauth_accept_token_options, "cookie-name",
				APR_HASH_KEY_STRING);
		const char *auth_line = oidc_util_get_cookie(r, cookie_name);
		if (auth_line != NULL) {

			/* copy the result in to the access_token */
			*access_token = apr_pstrdup(r->pool, auth_line);

		} else {
			oidc_warn(r, "no cookie found with name: %s", cookie_name);
		}
	}

	if (*access_token == NULL) {
		oidc_debug(r,
				"no bearer token found in the allowed methods (authorization header, post, query parameter or cookie)");
		return FALSE;
	}

	/* log some stuff */
	oidc_debug(r, "bearer token: %s", *access_token);
	return TRUE;
}