Commit 9987dc3b authored by Moritz Schlarb's avatar Moritz Schlarb

Merge tag 'upstream/2.3.3'

Upstream version 2.3.3
parents 2184de8e 99f94b06
......@@ -38,3 +38,5 @@ reporting bugs, providing fixes, suggesting useful features or other:
Moritz Schlarb <https://github.com/moschlar>
remi-cc <https://github.com/remi-cc>
hihellobolke <https://github.com/hihellobolke>
Horatiu Eugen Vlad <https://github.com/iconoeugen>
cristichiru <https://github.com/cristichiru>
11/16/2017
- add support for passing userinfo as a JSON object or JWT; see #311
- release 2.3.3
11/13/2017
- add support for authentication to the introspection endpoint with a bearer token using OIDCOAuthIntrospectionClientAuthBearerToken; thanks @cristichiru
- bump to 2.3.3rc3
11/08/2017
- address a number of static code analysis issues
- bump to 2.3.3rc2
10/10/2017
- avoid crash when no scheme is set on OIDCProviderMetadataURL; closes #303; thanks @iconoeugen
- bump to 2.3.3rc1
10/6/2017
- avoid crash when no OIDCOAuthClientID is set for remote access token validation
- don't enforce "iat" slack checks on locally validaed JWT access tokens
- bump to 2.3.3rc0
09/18/2017
- release 2.3.2
......@@ -159,7 +180,7 @@
03/30/2017
- merge feature branch back to master:
- better support for Single Page Applications, see:
https://github.com/pingidentity/mod_auth_openidc/wiki/Single-Page-Applications
https://github.com/zmartzone/mod_auth_openidc/wiki/Single-Page-Applications
- add session info hook that is configurable through OIDCInfoHook
- add "AuthType auth-openidc" option that allows both "oauth20" and "openid-connect" on the same path
- add encryption for all cache entries instead of just session data through OIDCCacheEncrypt
......
Preferably you should use one of the pre-compiled binary packages, available for
various platforms, see:
https://github.com/pingidentity/mod_auth_openidc/wiki#11-where-can-i-get-binary-packages
https://github.com/zmartzone/mod_auth_openidc/wiki#11-where-can-i-get-binary-packages
and proceed with the Configuration section below.
If your platform is not supported or you want to run the latest code,
......
[![Build Status](https://travis-ci.org/pingidentity/mod_auth_openidc.svg?branch=master)](https://travis-ci.org/pingidentity/mod_auth_openidc)
[![Build Status](https://travis-ci.org/zmartzone/mod_auth_openidc.svg?branch=master)](https://travis-ci.org/zmartzone/mod_auth_openidc)
mod_auth_openidc
================
......@@ -35,14 +35,14 @@ of claims provided in the `id_token`/ `userinfo` claims.
- [OpenID Provider Discovery](http://openid.net/specs/openid-connect-discovery-1_0.html)
- [OAuth 2.0 Form Post Response Mode](http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html)
- [Proof Key for Code Exchange by OAuth Public Clients](https://tools.ietf.org/html/rfc7636)
- [OpenID Connect Session Management](http://openid.net/specs/openid-connect-session-1_0.html). See the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/Session-Management) for information
- [OpenID Connect Session Management](http://openid.net/specs/openid-connect-session-1_0.html). See the [Wiki](https://github.com/zmartzone/mod_auth_openidc/wiki/Session-Management) for information
on how to configure it.
Alternatively the module can operate as an OAuth 2.0 Resource Server to an OAuth 2.0 Authorization Server,
introspecting/validating bearer Access Tokens conforming to [OAuth 2.0 Token Introspection](https://tools.ietf.org/html/rfc7662) (or similar),
or verifiying them locally if they are JWTs.
The `REMOTE_USER` variable setting, passing claims in HTTP headers and authorization based on `Require` primitives
works in the same way as described for OpenID Connect above. See the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server) for information
works in the same way as described for OpenID Connect above. See the [Wiki](https://github.com/zmartzone/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server) for information
on how to configure it.
For an exhaustive description of all configuration options, see the file `auth_openidc.conf`
......@@ -83,7 +83,7 @@ Require claim hd:<your-domain>
```
The above is an authorization example of an exact match of a provided claim against a string value.
For more authorization options see the [Wiki page on Authorization](https://github.com/pingidentity/mod_auth_openidc/wiki/Authorization).
For more authorization options see the [Wiki page on Authorization](https://github.com/zmartzone/mod_auth_openidc/wiki/Authorization).
### Quickstart with a generic OpenID Connect Provider
......@@ -110,7 +110,7 @@ OIDCCryptoPassphrase <password>
Require valid-user
</Location>
```
For details on configuring multiple providers see the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/Multiple-Providers).
For details on configuring multiple providers see the [Wiki](https://github.com/zmartzone/mod_auth_openidc/wiki/Multiple-Providers).
### PingFederate OAuth 2.0 Resource Server
......@@ -134,7 +134,7 @@ OIDCOAuthClientSecret 2Federate
#Require claim scope~\bprofile\b
</Location>
```
For details and additional options on the OAuth 2.0 Resource Server setup see the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server).
For details and additional options on the OAuth 2.0 Resource Server setup see the [Wiki](https://github.com/zmartzone/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server).
### Quickstart with a generic OAuth 2.0 Resource Server
......@@ -142,7 +142,7 @@ Using "local" validation of JWT bearer tokens:
1. install and load `mod_auth_openidc.so` in your Apache server
1. configure your protected APIs/locations with `AuthType oauth20` and `Require claim` directives to restrict access to specific clients/scopes/claims/resource-owners
1. configure local or remote bearer token validation following the [Wiki](https://github.com/pingidentity/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server)
1. configure local or remote bearer token validation following the [Wiki](https://github.com/zmartzone/mod_auth_openidc/wiki/OAuth-2.0-Resource-Server)
```apache
# local validation
......@@ -158,7 +158,7 @@ Support
-------
See the Wiki pages with Frequently Asked Questions at:
https://github.com/pingidentity/mod_auth_openidc/wiki
https://github.com/zmartzone/mod_auth_openidc/wiki
There is a Google Group/mailing list at:
[mod_auth_openidc@googlegroups.com](mailto:mod_auth_openidc@googlegroups.com)
The corresponding forum/archive is at:
......@@ -173,6 +173,5 @@ The Github issues tracker should be used only for bugs reports and feature reque
Disclaimer
----------
*This software is open sourced by Ping Identity but not supported commercially
by Ping Identity, see also the DISCLAIMER file in this directory. For commercial support
*This software is open sourced by ZmartZone IAM. For commercial support
you can contact [ZmartZone IAM](https://www.zmartzone.eu) as described above.*
......@@ -289,6 +289,12 @@
# When not defined "client_secret_basic" is used.
#OIDCOAuthIntrospectionEndpointAuth [ client_secret_basic | client_secret_post | client_secret_jwt | private_key_jwt]
# Some OP do not accept basic or post, only bearer tokens in the Authorization header.
# Specify here a static token to be used for authorizing the call to the introspection endpoint.
# If empty, the introspected token will be used for authorization as well.
# If unset, one of the methods specified by OIDCOAuthIntrospectionEndpointAuth will be used.
#OIDCOAuthIntrospectionClientAuthBearerToken [ a-static-bearer-token | ]
# Filename that contains the PEM-formatted client certificate used to authenticate the
# caller in token introspection calls to the OAuth 2.0 Authorization server.
#OIDCOAuthIntrospectionEndpointCert <filename>
......@@ -636,6 +642,14 @@
# When not defined the default "claims" is used.
#OIDCPassIDTokenAs [claims|payload|serialized]+
# Define the way(s) in which the claims resolved from the userinfo endpoint are passed to the application according to OIDCPassClaimsAs.
# Must be one or several of:
# "claims" : the userinfo claims are passed in individual headers/environment variables
# "json" : a self-contained userinfo JSON object is passed in the "OIDC_userinfo_json" header/environment variable
# "jwt" : a signed/encrypted JWT (if available!) optionally resolved from the userinfo endpoint is passed in the "OIDC_userinfo_jwt" header/environment variable
# When not defined the default "claims" is used.
#OIDCPassUserInfoAs [claims|json|jwt]+
# Define the way in which the claims and tokens are passed to the application environment:
# "none": no claims/tokens are passed
# "environment": claims/tokens are passed as environment variables
......@@ -702,7 +716,7 @@
#OIDCPassRefreshToken [On|Off]
# Request Object/URI settings expressed as a string that is a "double-quote-escaped" JSON object. For example:
# "{ \"copy_from_request\": [ \"claims\", \"response_type\", \"response_mode\", \"login_hint\", \"id_token_hint\", \"nonce\", \"state\", \"redirect_uri\", \"scope\", \"client_id\" ], \"static\": { \"some\": \"value\", \"some_nested\": { \"some_array\": [ 1,2,3] } }, \"crypto\": { \"sign_alg\": \"HS256\", \"crypt_alg\": \"A256KW\", \"crypt_enc\": \"A256CBC-HS512\" }, \"url\": \"https://www.pingidentity.nl/protected/\", \"request_object_type\" : \"request\" }"
# "{ \"copy_from_request\": [ \"claims\", \"response_type\", \"response_mode\", \"login_hint\", \"id_token_hint\", \"nonce\", \"state\", \"redirect_uri\", \"scope\", \"client_id\" ], \"static\": { \"some\": \"value\", \"some_nested\": { \"some_array\": [ 1,2,3] } }, \"crypto\": { \"sign_alg\": \"HS256\", \"crypt_alg\": \"A256KW\", \"crypt_enc\": \"A256CBC-HS512\" }, \"url\": \"https://www.zmartzone.eu/protected/\", \"request_object_type\" : \"request\" }"
# Parameters:
# copy_from_request (array) : array of query parameter names copied from request
# copy_and_remove_from_request (array) : array of parameter names copied from request and removed as query parameter
......
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.69 for mod_auth_openidc 2.3.2.
# Generated by GNU Autoconf 2.69 for mod_auth_openidc 2.3.3.
#
# Report bugs to <hans.zandbelt@zmartzone.eu>.
#
......@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='mod_auth_openidc'
PACKAGE_TARNAME='mod_auth_openidc'
PACKAGE_VERSION='2.3.2'
PACKAGE_STRING='mod_auth_openidc 2.3.2'
PACKAGE_VERSION='2.3.3'
PACKAGE_STRING='mod_auth_openidc 2.3.3'
PACKAGE_BUGREPORT='hans.zandbelt@zmartzone.eu'
PACKAGE_URL=''
......@@ -1269,7 +1269,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
\`configure' configures mod_auth_openidc 2.3.2 to adapt to many kinds of systems.
\`configure' configures mod_auth_openidc 2.3.3 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
......@@ -1331,7 +1331,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
short | recursive ) echo "Configuration of mod_auth_openidc 2.3.2:";;
short | recursive ) echo "Configuration of mod_auth_openidc 2.3.3:";;
esac
cat <<\_ACEOF
......@@ -1445,7 +1445,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
mod_auth_openidc configure 2.3.2
mod_auth_openidc configure 2.3.3
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
......@@ -1747,7 +1747,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
It was created by mod_auth_openidc $as_me 2.3.2, which was
It was created by mod_auth_openidc $as_me 2.3.3, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
......@@ -2096,7 +2096,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
NAMEVER=mod_auth_openidc-2.3.2
NAMEVER=mod_auth_openidc-2.3.3
# This section defines the --with-apxs2 option.
......@@ -4886,7 +4886,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
This file was extended by mod_auth_openidc $as_me 2.3.2, which was
This file was extended by mod_auth_openidc $as_me 2.3.3, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
......@@ -4939,7 +4939,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
mod_auth_openidc config.status 2.3.2
mod_auth_openidc config.status 2.3.3
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
......
AC_INIT([mod_auth_openidc],[2.3.2],[hans.zandbelt@zmartzone.eu])
AC_INIT([mod_auth_openidc],[2.3.3],[hans.zandbelt@zmartzone.eu])
AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION())
......
......@@ -207,8 +207,10 @@
#define OIDCCryptoPassphrase "OIDCCryptoPassphrase"
#define OIDCClaimDelimiter "OIDCClaimDelimiter"
#define OIDCPassIDTokenAs "OIDCPassIDTokenAs"
#define OIDCPassUserInfoAs "OIDCPassUserInfoAs"
#define OIDCOAuthClientID "OIDCOAuthClientID"
#define OIDCOAuthClientSecret "OIDCOAuthClientSecret"
#define OIDCOAuthIntrospectionClientAuthBearerToken "OIDCOAuthIntrospectionClientAuthBearerToken"
#define OIDCOAuthIntrospectionEndpoint "OIDCOAuthIntrospectionEndpoint"
#define OIDCOAuthIntrospectionEndpointMethod "OIDCOAuthIntrospectionEndpointMethod"
#define OIDCOAuthIntrospectionEndpointParams "OIDCOAuthIntrospectionEndpointParams"
......@@ -719,6 +721,18 @@ static const char * oidc_set_pass_idtoken_as(cmd_parms *cmd, void *dummy,
return OIDC_CONFIG_DIR_RV(cmd, rv);
}
/*
* define how to pass the userinfo/claims in HTTP headers
*/
static const char * oidc_set_pass_userinfo_as(cmd_parms *cmd, void *dummy,
const char *v1, const char *v2, const char *v3) {
oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
cmd->server->module_config, &auth_openidc_module);
const char *rv = oidc_parse_pass_userinfo_as(cmd->pool, v1, v2, v3,
&cfg->pass_userinfo_as);
return OIDC_CONFIG_DIR_RV(cmd, rv);
}
/*
* define which method of pass an OAuth Bearer token is accepted
*/
......@@ -965,6 +979,19 @@ const char *oidc_set_auth_request_method(cmd_parms *cmd, void *struct_ptr,
return OIDC_CONFIG_DIR_RV(cmd, rv);
}
/*
* set the introspection authorization static bearer token
*/
static const char *oidc_set_client_auth_bearer_token(cmd_parms *cmd,
void *struct_ptr, const char *args) {
oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
cmd->server->module_config, &auth_openidc_module);
char *w = ap_getword_conf(cmd->pool, &args);
cfg->oauth.introspection_client_auth_bearer_token =
(*w == '\0' || *args != 0) ? "" : w;
return NULL;
}
/*
* create a new server config record with defaults
*/
......@@ -1028,6 +1055,7 @@ void *oidc_create_server_config(apr_pool_t *pool, server_rec *svr) {
c->oauth.introspection_endpoint_method = OIDC_DEFAULT_OAUTH_ENDPOINT_METHOD;
c->oauth.introspection_endpoint_params = NULL;
c->oauth.introspection_endpoint_auth = NULL;
c->oauth.introspection_client_auth_bearer_token = NULL;
c->oauth.introspection_token_param_name =
OIDC_DEFAULT_OAUTH_TOKEN_PARAM_NAME;
......@@ -1080,6 +1108,7 @@ void *oidc_create_server_config(apr_pool_t *pool, server_rec *svr) {
c->remote_user_claim.reg_exp = NULL;
c->remote_user_claim.replace = NULL;
c->pass_idtoken_as = OIDC_PASS_IDTOKEN_AS_CLAIMS;
c->pass_userinfo_as = OIDC_PASS_USERINFO_AS_CLAIMS;
c->cookie_http_only = OIDC_DEFAULT_COOKIE_HTTPONLY;
c->cookie_same_site = OIDC_DEFAULT_COOKIE_SAME_SITE;
......@@ -1320,6 +1349,10 @@ void *oidc_merge_server_config(apr_pool_t *pool, void *BASE, void *ADD) {
add->oauth.introspection_endpoint_auth != NULL ?
add->oauth.introspection_endpoint_auth :
base->oauth.introspection_endpoint_auth;
c->oauth.introspection_client_auth_bearer_token =
add->oauth.introspection_client_auth_bearer_token != NULL ?
add->oauth.introspection_client_auth_bearer_token :
base->oauth.introspection_client_auth_bearer_token;
c->oauth.introspection_token_param_name =
apr_strnatcmp(add->oauth.introspection_token_param_name,
OIDC_DEFAULT_OAUTH_TOKEN_PARAM_NAME) != 0 ?
......@@ -1468,6 +1501,9 @@ void *oidc_merge_server_config(apr_pool_t *pool, void *BASE, void *ADD) {
c->pass_idtoken_as =
add->pass_idtoken_as != OIDC_PASS_IDTOKEN_AS_CLAIMS ?
add->pass_idtoken_as : base->pass_idtoken_as;
c->pass_userinfo_as =
add->pass_userinfo_as != OIDC_PASS_USERINFO_AS_CLAIMS ?
add->pass_userinfo_as : base->pass_userinfo_as;
c->cookie_http_only =
add->cookie_http_only != OIDC_DEFAULT_COOKIE_HTTPONLY ?
add->cookie_http_only : base->cookie_http_only;
......@@ -1615,7 +1651,7 @@ char *oidc_cfg_dir_authn_header(request_rec *r) {
return dir_cfg->authn_header;
}
int oidc_cfg_dir_pass_info_in_headers(request_rec *r) {
apr_byte_t oidc_cfg_dir_pass_info_in_headers(request_rec *r) {
oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
&auth_openidc_module);
if (dir_cfg->pass_info_in_headers == OIDC_CONFIG_POS_INT_UNSET)
......@@ -1623,7 +1659,7 @@ int oidc_cfg_dir_pass_info_in_headers(request_rec *r) {
return dir_cfg->pass_info_in_headers;
}
int oidc_cfg_dir_pass_info_in_envvars(request_rec *r) {
apr_byte_t oidc_cfg_dir_pass_info_in_envvars(request_rec *r) {
oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
&auth_openidc_module);
if (dir_cfg->pass_info_in_env_vars == OIDC_CONFIG_POS_INT_UNSET)
......@@ -1631,7 +1667,7 @@ int oidc_cfg_dir_pass_info_in_envvars(request_rec *r) {
return dir_cfg->pass_info_in_env_vars;
}
int oidc_cfg_dir_pass_refresh_token(request_rec *r) {
apr_byte_t oidc_cfg_dir_pass_refresh_token(request_rec *r) {
oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
&auth_openidc_module);
if (dir_cfg->pass_refresh_token == OIDC_CONFIG_POS_INT_UNSET)
......@@ -1639,7 +1675,7 @@ int oidc_cfg_dir_pass_refresh_token(request_rec *r) {
return dir_cfg->pass_refresh_token;
}
int oidc_cfg_dir_accept_token_in(request_rec *r) {
apr_byte_t oidc_cfg_dir_accept_token_in(request_rec *r) {
oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
&auth_openidc_module);
if (dir_cfg->oauth_accept_token_in == OIDC_CONFIG_POS_INT_UNSET)
......@@ -1814,7 +1850,8 @@ static int oidc_check_config_openid_openidc(server_rec *s, oidc_cfg *c) {
OIDCProviderAuthorizationEndpoint);
} else {
apr_uri_parse(s->process->pconf, c->provider.metadata_url, &r_uri);
if (apr_strnatcmp(r_uri.scheme, "http") == 0) {
if ((r_uri.scheme == NULL)
|| (apr_strnatcmp(r_uri.scheme, "https") != 0)) {
oidc_swarn(s,
"the URL scheme (%s) of the configured " OIDCProviderMetadataURL " SHOULD be \"https\" for security reasons!",
r_uri.scheme);
......@@ -2087,7 +2124,8 @@ static int oidc_post_config(apr_pool_t *pool, apr_pool_t *p1, apr_pool_t *p2,
}
#endif /* OPENSSL_NO_THREADID */
#endif /* defined(OPENSSL_THREADS) && APR_HAS_THREADS */
apr_pool_cleanup_register(pool, s, oidc_cleanup_parent, apr_pool_cleanup_null);
apr_pool_cleanup_register(pool, s, oidc_cleanup_parent,
apr_pool_cleanup_null);
server_rec *sp = s;
while (sp != NULL) {
......@@ -2149,7 +2187,7 @@ static void oidc_child_init(apr_pool_t *p, server_rec *s) {
}
sp = sp->next;
}
apr_pool_cleanup_register(p, s, oidc_cleanup_child, apr_pool_cleanup_null);
apr_pool_cleanup_register(p, s, oidc_cleanup_child, apr_pool_cleanup_null);
}
/*
......@@ -2201,7 +2239,7 @@ void oidc_register_hooks(apr_pool_t *pool) {
const command_rec oidc_config_cmds[] = {
AP_INIT_TAKE1(OIDCProviderMetadataURL,
oidc_set_string_slot,
oidc_set_url_slot,
(void*)APR_OFFSETOF(oidc_cfg, provider.metadata_url),
RSRC_CONF,
"OpenID Connect OP configuration metadata URL."),
......@@ -2461,6 +2499,11 @@ const command_rec oidc_config_cmds[] = {
NULL,
RSRC_CONF,
"The format in which the id_token is passed in (a) header(s); must be one or more of: claims|payload|serialized"),
AP_INIT_TAKE123(OIDCPassUserInfoAs,
oidc_set_pass_userinfo_as,
NULL,
RSRC_CONF,
"The format in which the userinfo is passed in (a) header(s); must be one or more of: claims|json|jwt"),
AP_INIT_TAKE1(OIDCOAuthClientID,
oidc_set_string_slot,
......@@ -2494,6 +2537,11 @@ const command_rec oidc_config_cmds[] = {
(void *)APR_OFFSETOF(oidc_cfg, oauth.introspection_endpoint_auth),
RSRC_CONF,
"Specify an authentication method for the OAuth AS Introspection Endpoint (e.g.: client_secret_basic)"),
AP_INIT_RAW_ARGS(OIDCOAuthIntrospectionClientAuthBearerToken,
oidc_set_client_auth_bearer_token,
NULL,
RSRC_CONF,
"Specify a bearer token to authorize against the OAuth AS Introspection Endpoint (e.g.: 55554ee-2491-11e3-be72-001fe2e44345 or empty to use the introspected token itself)"),
AP_INIT_TAKE1(OIDCOAuthIntrospectionEndpointCert,
oidc_set_string_slot,
(void*)APR_OFFSETOF(oidc_cfg, oauth.introspection_endpoint_tls_client_cert),
......
......@@ -188,7 +188,7 @@ int oidc_jwt_alg2kty(oidc_jwt_t *jwt) {
/*
* return the key size for an algorithm
*/
int oidc_alg2keysize(const char *alg) {
unsigned int oidc_alg2keysize(const char *alg) {
if (alg == NULL)
return 0;
......@@ -738,6 +738,8 @@ apr_byte_t oidc_jwt_parse(apr_pool_t *pool, const char *input_json,
return FALSE;
*j_jwt = oidc_jwt_new(pool, FALSE, FALSE);
if (*j_jwt == NULL)
return FALSE;
oidc_jwt_t *jwt = *j_jwt;
jwt->cjose_jws = cjose_jws_import(s_json, strlen(s_json), &cjose_err);
......
......@@ -243,6 +243,6 @@ const char *oidc_jwt_hdr_get(oidc_jwt_t *jwt, const char *key);
/* return the key type of a JWT */
int oidc_jwt_alg2kty(oidc_jwt_t *jwt);
/* return the key size for an algorithm */
int oidc_alg2keysize(const char *alg);
unsigned int oidc_alg2keysize(const char *alg);
#endif /* MOD_AUTH_OPENIDC_JOSE_H_ */
......@@ -1272,11 +1272,11 @@ apr_byte_t oidc_metadata_client_parse(request_rec *r, oidc_cfg *cfg,
if ((apr_strnatcmp(token_endpoint_auth, OIDC_PROTO_CLIENT_SECRET_POST)
== 0)
|| (apr_strnatcmp(token_endpoint_auth,
OIDC_PROTO_CLIENT_SECRET_BASIC) == 0)
|| (apr_strnatcmp(token_endpoint_auth,
OIDC_PROTO_CLIENT_SECRET_JWT) == 0)
OIDC_PROTO_CLIENT_SECRET_BASIC) == 0)
|| (apr_strnatcmp(token_endpoint_auth,
OIDC_PROTO_PRIVATE_KEY_JWT) == 0)) {
OIDC_PROTO_CLIENT_SECRET_JWT) == 0)
|| (apr_strnatcmp(token_endpoint_auth,
OIDC_PROTO_PRIVATE_KEY_JWT) == 0)) {
provider->token_endpoint_auth = apr_pstrdup(r->pool,
token_endpoint_auth);
} else {
......
This diff is collapsed.
......@@ -123,11 +123,18 @@ APLOG_USE_MODULE(auth_openidc);
/* pass id_token as individual claims in headers (default) */
#define OIDC_PASS_IDTOKEN_AS_CLAIMS 1
/* pass id_token payload as JSON object in header*/
/* pass id_token payload as JSON object in header */
#define OIDC_PASS_IDTOKEN_AS_PAYLOAD 2
/* pass id_token in compact serialized format in header*/
/* pass id_token in compact serialized format in header */
#define OIDC_PASS_IDTOKEN_AS_SERIALIZED 4
/* pass userinfo as individual claims in headers (default) */
#define OIDC_PASS_USERINFO_AS_CLAIMS 1
/* pass userinfo payload as JSON object in header */
#define OIDC_PASS_USERINFO_AS_JSON_OBJECT 2
/* pass userinfo as a JWT in header (when returned as a JWT) */
#define OIDC_PASS_USERINFO_AS_JWT 4
#define OIDC_OAUTH_ACCEPT_TOKEN_IN_DEFAULT 0
/* accept bearer token in header (default) */
#define OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER 1
......@@ -297,6 +304,7 @@ typedef struct oidc_oauth_t {
char *introspection_endpoint_method;
char *introspection_endpoint_params;
char *introspection_endpoint_auth;
char *introspection_client_auth_bearer_token;
char *introspection_token_param_name;
char *introspection_token_expiry_claim_name;
char *introspection_token_expiry_claim_format;
......@@ -375,6 +383,7 @@ typedef struct oidc_cfg {
char *claim_prefix;
oidc_remote_user_claim_t remote_user_claim;
int pass_idtoken_as;
int pass_userinfo_as;
int cookie_http_only;
int cookie_same_site;
......@@ -550,6 +559,8 @@ apr_byte_t oidc_oauth_get_bearer_token(request_rec *r, const char **access_token
#define OIDC_APP_INFO_ACCESS_TOKEN_EXP "access_token_expires"
#define OIDC_APP_INFO_ID_TOKEN "id_token"
#define OIDC_APP_INFO_ID_TOKEN_PAYLOAD "id_token_payload"
#define OIDC_APP_INFO_USERINFO_JSON "userinfo_json"
#define OIDC_APP_INFO_USERINFO_JWT "userinfo_jwt"
typedef json_t oidc_proto_state_t;
......@@ -580,14 +591,14 @@ void oidc_proto_state_set_prompt(oidc_proto_state_t *proto_state, const char *pr
void oidc_proto_state_set_pkce_state(oidc_proto_state_t *proto_state, const char *pkce_state);
void oidc_proto_state_set_timestamp_now(oidc_proto_state_t *proto_state);
apr_byte_t oidc_proto_token_endpoint_auth(request_rec *r, oidc_cfg *cfg, const char *token_endpoint_auth, const char *client_id, const char *client_secret, const char *audience, apr_table_t *params, char **basic_auth_str);
apr_byte_t oidc_proto_token_endpoint_auth(request_rec *r, oidc_cfg *cfg, const char *token_endpoint_auth, const char *client_id, const char *client_secret, const char *audience, apr_table_t *params, char **basic_auth_str, char **bearer_auth_str);
char *oidc_proto_peek_jwt_header(request_rec *r, const char *jwt, char **alg);
int oidc_proto_authorization_request(request_rec *r, struct oidc_provider_t *provider, const char *login_hint, const char *redirect_uri, const char *state, oidc_proto_state_t *proto_state, const char *id_token_hint, const char *code_challenge, const char *auth_request_params, const char *path_scope);
apr_byte_t oidc_proto_is_post_authorization_response(request_rec *r, oidc_cfg *cfg);
apr_byte_t oidc_proto_is_redirect_authorization_response(request_rec *r, oidc_cfg *cfg);
apr_byte_t oidc_proto_refresh_request(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *rtoken, char **id_token, char **access_token, char **token_type, int *expires_in, char **refresh_token);
apr_byte_t oidc_proto_resolve_userinfo(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *id_token_sub, const char *access_token, char **response);
apr_byte_t oidc_proto_resolve_userinfo(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *id_token_sub, const char *access_token, char **response, char **userinfo_jwt);
apr_byte_t oidc_proto_account_based_discovery(request_rec *r, oidc_cfg *cfg, const char *acct, char **issuer);
apr_byte_t oidc_proto_url_based_discovery(request_rec *r, oidc_cfg *cfg, const char *url, char **issuer);
apr_byte_t oidc_proto_parse_idtoken(request_rec *r, oidc_cfg *cfg, oidc_provider_t *provider, const char *id_token, const char *nonce, oidc_jwt_t **jwt, apr_byte_t is_code_flow);
......@@ -651,10 +662,10 @@ char *oidc_cfg_dir_discover_url(request_rec *r);
char *oidc_cfg_dir_cookie(request_rec *r);
char *oidc_cfg_dir_cookie_path(request_rec *r);
char *oidc_cfg_dir_authn_header(request_rec *r);
int oidc_cfg_dir_pass_info_in_headers(request_rec *r);
int oidc_cfg_dir_pass_info_in_envvars(request_rec *r);
int oidc_cfg_dir_pass_refresh_token(request_rec *r);
int oidc_cfg_dir_accept_token_in(request_rec *r);
apr_byte_t oidc_cfg_dir_pass_info_in_headers(request_rec *r);
apr_byte_t oidc_cfg_dir_pass_info_in_envvars(request_rec *r);
apr_byte_t oidc_cfg_dir_pass_refresh_token(request_rec *r);
apr_byte_t oidc_cfg_dir_accept_token_in(request_rec *r);
char *oidc_cfg_dir_accept_token_in_option(request_rec *r, const char *key);
int oidc_cfg_token_introspection_interval(request_rec *r);
int oidc_cfg_dir_preserve_post(request_rec *r);
......@@ -691,7 +702,7 @@ apr_byte_t oidc_util_get_request_parameter(request_rec *r, char *name, char **va
char *oidc_util_encode_json_object(request_rec *r, json_t *json, size_t flags);
apr_byte_t oidc_util_decode_json_object(request_rec *r, const char *str, json_t **json);
apr_byte_t oidc_util_decode_json_and_check_error(request_rec *r, const char *str, json_t **json);
int oidc_util_http_send(request_rec *r, const char *data, int data_len, const char *content_type, int success_rvalue);
int oidc_util_http_send(request_rec *r, const char *data, size_t data_len, const char *content_type, int success_rvalue);
int oidc_util_html_send(request_rec *r, const char *title, const char *html_head, const char *on_load, const char *html_body, int status_code);
char *oidc_util_escape_string(const request_rec *r, const char *str);
char *oidc_util_unescape_string(const request_rec *r, const char *str);
......@@ -722,7 +733,7 @@ apr_byte_t oidc_util_jwt_create(request_rec *r, const char *secret, json_t *payl
apr_byte_t oidc_util_jwt_verify(request_rec *r, const char *secret, const char *compact_encoded_jwt, json_t **result);
char *oidc_util_get_chunked_cookie(request_rec *r, const char *cookieName, int cookie_chunk_size);
void oidc_util_set_chunked_cookie(request_rec *r, const char *cookieName, const char *cookieValue, apr_time_t expires, int chunkSize, const char *ext);
apr_byte_t oidc_util_create_symmetric_key(request_rec *r, const char *client_secret, int r_key_len, const char *hash_algo, apr_byte_t set_kid, oidc_jwk_t **jwk);
apr_byte_t oidc_util_create_symmetric_key(request_rec *r, const char *client_secret, unsigned int r_key_len, const char *hash_algo, apr_byte_t set_kid, oidc_jwk_t **jwk);
apr_hash_t * oidc_util_merge_symmetric_key(apr_pool_t *pool, apr_hash_t *private_keys, oidc_jwk_t *jwk);
const char *oidc_util_get_provided_token_binding_id(const request_rec *r);
char *oidc_util_http_query_encoded_url(request_rec *r, const char *url, const apr_table_t *params);
......@@ -791,6 +802,8 @@ apr_byte_t oidc_session_save(request_rec *r, oidc_session_t *z, apr_byte_t first
apr_byte_t oidc_session_kill(request_rec *r, oidc_session_t *z);
apr_byte_t oidc_session_free(request_rec *r, oidc_session_t *z);
void oidc_session_set_userinfo_jwt(request_rec *r, oidc_session_t *z, const char *userinfo_jwt);
const char * oidc_session_get_userinfo_jwt(request_rec *r, oidc_session_t *z);
void oidc_session_set_userinfo_claims(request_rec *r, oidc_session_t *z, const char *claims);
const char * oidc_session_get_userinfo_claims(request_rec *r, oidc_session_t *z);
json_t *oidc_session_get_userinfo_claims_json(request_rec *r, oidc_session_t *z);
......
......@@ -63,8 +63,11 @@
*/
static apr_byte_t oidc_oauth_validate_access_token(request_rec *r, oidc_cfg *c,
const char *token, char **response) {
oidc_debug(r, "enter");
char *basic_auth = NULL;
char *bearer_auth = NULL;
/* assemble parameters to call the token endpoint for validation */
apr_table_t *params = apr_table_make(r->pool, 4);
......@@ -80,20 +83,20 @@ static apr_byte_t oidc_oauth_validate_access_token(request_rec *r, oidc_cfg *c,
if (oidc_proto_token_endpoint_auth(r, c,
c->oauth.introspection_endpoint_auth, c->oauth.client_id,
c->oauth.client_secret, c->oauth.introspection_endpoint_url, params,
&basic_auth) == FALSE)
&basic_auth, &bearer_auth) == FALSE)
return FALSE;
/* call the endpoint with the constructed parameter set and return the resulting response */
return apr_strnatcmp(c->oauth.introspection_endpoint_method,
OIDC_INTROSPECTION_METHOD_GET) == 0 ?
oidc_util_http_get(r, c->oauth.introspection_endpoint_url, params,
basic_auth, NULL, c->oauth.ssl_validate_server, response,
basic_auth, bearer_auth, c->oauth.ssl_validate_server, response,
c->http_timeout_long, c->outgoing_proxy,
oidc_dir_cfg_pass_cookies(r),
oidc_util_get_full_path(r->pool, c->oauth.introspection_endpoint_tls_client_cert),
oidc_util_get_full_path(r->pool, c->oauth.introspection_endpoint_tls_client_key)) :
oidc_util_http_post_form(r, c->oauth.introspection_endpoint_url,
params, basic_auth, NULL, c->oauth.ssl_validate_server,
params, basic_auth, bearer_auth, c->oauth.ssl_validate_server,
response, c->http_timeout_long, c->outgoing_proxy,
oidc_dir_cfg_pass_cookies(r),
oidc_util_get_full_path(r->pool, c->oauth.introspection_endpoint_tls_client_cert),
......@@ -107,7 +110,7 @@ apr_byte_t oidc_oauth_get_bearer_token(request_rec *r,
const char **access_token) {
/* get the directory specific setting on how the token can be passed in */
int accept_token_in = oidc_cfg_dir_accept_token_in(r);
apr_byte_t accept_token_in = oidc_cfg_dir_accept_token_in(r);
const char *cookie_name = oidc_cfg_dir_accept_token_in_option(r,
OIDC_OAUTH_ACCEPT_TOKEN_IN_OPTION_COOKIE_NAME);
......@@ -465,7 +468,6 @@ static apr_byte_t oidc_oauth_resolve_access_token(request_rec *r, oidc_cfg *c,
* TODO: document that we're reusing the following settings from the OIDC config section:
* - JWKs URI refresh interval
* - encryption key material (OIDCPrivateKeyFiles)
* - iat slack (OIDCIDTokenIatSlack)
*
* OIDCOAuthRemoteUserClaim client_id
* # 32x 61 hex
......@@ -496,9 +498,11 @@ static apr_byte_t oidc_oauth_validate_jwt_access_token(request_rec *r,
oidc_debug(r, "successfully parsed JWT with header: %s",
jwt->header.value.str);
/* validate the access token JWT, validating optional exp + iat */
if (oidc_proto_validate_jwt(r, jwt, NULL, FALSE, FALSE,
c->provider.idtoken_iat_slack) == FALSE) {
/*
* validate the access token JWT by validating the (optional) exp claim
* don't enforce anything around iat since it doesn't make much sense for access tokens
*/
if (oidc_proto_validate_jwt(r, jwt, NULL, FALSE, FALSE, -1) == FALSE) {
oidc_jwt_destroy(jwt);
return FALSE;
}
......@@ -675,8 +679,8 @@ int oidc_oauth_check_userid(request_rec *r, oidc_cfg *c) {
/* set the user authentication HTTP header if set and required */
char *authn_header = oidc_cfg_dir_authn_header(r);
int pass_headers = oidc_cfg_dir_pass_info_in_headers(r);
int pass_envvars = oidc_cfg_dir_pass_info_in_envvars(r);
apr_byte_t pass_headers = oidc_cfg_dir_pass_info_in_headers(r);
apr_byte_t pass_envvars = oidc_cfg_dir_pass_info_in_envvars(r);
if ((r->user != NULL) && (authn_header != NULL))
oidc_util_hdr_in_set(r, authn_header, r->user);
......
......@@ -414,7 +414,8 @@ const char *oidc_valid_response_type(apr_pool_t *pool, const char *arg) {
if (oidc_proto_flow_is_supported(pool, arg) == FALSE) {
return apr_psprintf(pool,
"oidc_valid_response_type: type must be one of %s",
apr_array_pstrcat(pool, oidc_proto_supported_flows(pool), OIDC_CHAR_PIPE));
apr_array_pstrcat(pool, oidc_proto_supported_flows(pool),
OIDC_CHAR_PIPE));
}
return NULL;
}
......@@ -456,7 +457,8 @@ const char *oidc_valid_signed_response_alg(apr_pool_t *pool, const char *arg) {
"unsupported/invalid signing algorithm '%s'; must be one of [%s]",
arg,
apr_array_pstrcat(pool,
oidc_jose_jws_supported_algorithms(pool), OIDC_CHAR_PIPE));
oidc_jose_jws_supported_algorithms(pool),
OIDC_CHAR_PIPE));
}
return NULL;
}
......@@ -470,7 +472,8 @@ const char *oidc_valid_encrypted_response_alg(apr_pool_t *pool, const char *arg)
"unsupported/invalid encryption algorithm '%s'; must be one of [%s]",
arg,
apr_array_pstrcat(pool,
oidc_jose_jwe_supported_algorithms(pool), OIDC_CHAR_PIPE));
oidc_jose_jwe_supported_algorithms(pool),
OIDC_CHAR_PIPE));
}
return NULL;
}
......@@ -484,7 +487,8 @@ const char *oidc_valid_encrypted_response_enc(apr_pool_t *pool, const char *arg)
"unsupported/invalid encryption type '%s'; must be one of [%s]",
arg,
apr_array_pstrcat(pool,
oidc_jose_jwe_supported_encryptions(pool), OIDC_CHAR_PIPE));
oidc_jose_jwe_supported_encryptions(pool),
OIDC_CHAR_PIPE));
}
return NULL;
}
......@@ -699,6 +703,58 @@ const char *oidc_parse_pass_idtoken_as(apr_pool_t *pool, const char *v1,
return NULL;
}
#define OIDC_PASS_USERINFO_AS_CLAIMS_STR "claims"
#define OIDC_PASS_USERINFO_AS_JSON_OBJECT_STR "json"
#define OIDC_PASS_USERINFO_AS_JWT_STR "jwt"
/*
* convert a "pass userinfo as" value to an integer
*/
static int oidc_parse_pass_userinfo_as_str2int(const char *v) {
if (apr_strnatcmp(v, OIDC_PASS_USERINFO_AS_CLAIMS_STR) == 0)
return OIDC_PASS_USERINFO_AS_CLAIMS;
if (apr_strnatcmp(v, OIDC_PASS_USERINFO_AS_JSON_OBJECT_STR) == 0)
return OIDC_PASS_USERINFO_AS_JSON_OBJECT;
if (apr_strnatcmp(v, OIDC_PASS_USERINFO_AS_JWT_STR) == 0)
return OIDC_PASS_USERINFO_AS_JWT;
return -1;
}
/*
* parse a "pass id token as" value from the provided strings
*/
const char *oidc_parse_pass_userinfo_as(apr_pool_t *pool, const char *v1,
const char *v2, const char *v3, int *int_value) {
static char *options[] = {
OIDC_PASS_USERINFO_AS_CLAIMS_STR,
OIDC_PASS_USERINFO_AS_JSON_OBJECT_STR,
OIDC_PASS_USERINFO_AS_JWT_STR,
NULL };
const char *rv = NULL;
rv = oidc_valid_string_option(pool, v1, options);
if (rv != NULL)
return rv;
*int_value = oidc_parse_pass_userinfo_as_str2int(v1);
if (v2 == NULL)
return NULL;
rv = oidc_valid_string_option(pool, v2, options);
if (rv != NULL)
return rv;
*int_value |= oidc_parse_pass_userinfo_as_str2int(v2);
if (v3 == NULL)
return NULL;
rv = oidc_valid_string_option(pool, v3, options);
if (rv != NULL)
return rv;
*int_value |= oidc_parse_pass_userinfo_as_str2int(v3);
return NULL;
}
#define OIDC_OAUTH_ACCEPT_TOKEN_IN_HEADER_STR "header"
#define OIDC_OAUTH_ACCEPT_TOKEN_IN_POST_STR "post"
#define OIDC_OAUTH_ACCEPT_TOKEN_IN_QUERY_STR "query"
......@@ -1087,7 +1143,6 @@ const char *oidc_token_binding_policy2str(apr_pool_t *pool, int v) {
return NULL;
}