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 {
......
......@@ -1035,8 +1035,9 @@ apr_byte_t oidc_get_provider_from_session(request_rec *r, oidc_cfg *c,
/*
* store claims resolved from the userinfo endpoint in the session
*/
static void oidc_store_userinfo_claims(request_rec *r, oidc_session_t *session,
oidc_provider_t *provider, const char *claims) {
static void oidc_store_userinfo_claims(request_rec *r, oidc_cfg *c,
oidc_session_t *session, oidc_provider_t *provider, const char *claims,
const char *userinfo_jwt) {
oidc_debug(r, "enter");
......@@ -1049,12 +1050,18 @@ static void oidc_store_userinfo_claims(request_rec *r, oidc_session_t *session,
*/
oidc_session_set_userinfo_claims(r, session, claims);
if (c->session_type != OIDC_SESSION_TYPE_CLIENT_COOKIE) {
/* this will also clear the entry if a JWT was not returned at this point */
oidc_session_set_userinfo_jwt(r, session, userinfo_jwt);
}
} else {
/*
* clear the existing claims because we could not refresh them
*/
oidc_session_set_userinfo_claims(r, session, NULL);
oidc_session_set_userinfo_jwt(r, session, NULL);
}
/* store the last refresh time if we've configured a userinfo refresh interval */
......@@ -1117,7 +1124,7 @@ static apr_byte_t oidc_refresh_access_token(request_rec *r, oidc_cfg *c,
*/
static const char *oidc_retrieve_claims_from_userinfo_endpoint(request_rec *r,
oidc_cfg *c, oidc_provider_t *provider, const char *access_token,
oidc_session_t *session, char *id_token_sub) {
oidc_session_t *session, char *id_token_sub, char **userinfo_jwt) {
oidc_debug(r, "enter");
......@@ -1155,7 +1162,7 @@ static const char *oidc_retrieve_claims_from_userinfo_endpoint(request_rec *r,
/* try to get claims from the userinfo endpoint using the provided access token */
char *result = NULL;
if (oidc_proto_resolve_userinfo(r, c, provider, id_token_sub, access_token,
&result) == FALSE) {
&result, userinfo_jwt) == FALSE) {
/* see if we have an existing session and we are refreshing the user info claims */
if (session != NULL) {
......@@ -1167,7 +1174,7 @@ static const char *oidc_retrieve_claims_from_userinfo_endpoint(request_rec *r,
/* try again with the new access token */
if (oidc_proto_resolve_userinfo(r, c, provider, id_token_sub,
access_token, &result) == FALSE) {
access_token, &result, userinfo_jwt) == FALSE) {
oidc_error(r,
"resolving user info claims with the refreshed access token failed, nothing will be stored in the session");
......@@ -1204,6 +1211,7 @@ static apr_byte_t oidc_refresh_claims_from_userinfo_endpoint(request_rec *r,
oidc_provider_t *provider = NULL;
const char *claims = NULL;
const char *access_token = NULL;
char *userinfo_jwt = NULL;
/* get the current provider info */
if (oidc_get_provider_from_session(r, cfg, session, &provider) == FALSE)
......@@ -1234,10 +1242,11 @@ static apr_byte_t oidc_refresh_claims_from_userinfo_endpoint(request_rec *r,
/* retrieve the current claims */
claims = oidc_retrieve_claims_from_userinfo_endpoint(r, cfg,
provider, access_token, session, NULL);
provider, access_token, session, NULL, &userinfo_jwt);
/* store claims resolved from userinfo endpoint */
oidc_store_userinfo_claims(r, session, provider, claims);
oidc_store_userinfo_claims(r, cfg, session, provider, claims,
userinfo_jwt);
/* indicated something changed */
return TRUE;
......@@ -1276,8 +1285,8 @@ static void oidc_copy_tokens_to_request_state(request_rec *r,
static apr_byte_t oidc_session_pass_tokens_and_save(request_rec *r,
oidc_cfg *cfg, oidc_session_t *session, apr_byte_t needs_save) {
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);
/* set the refresh_token in the app headers/variables, if enabled for this location/directory */
const char *refresh_token = oidc_session_get_refresh_token(r, session);
......@@ -1353,8 +1362,8 @@ static int oidc_handle_existing_session(request_rec *r, oidc_cfg *cfg,
/* get the header name in which the remote user name needs to be passed */
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);
/* verify current cookie domain against issued cookie domain */
if (oidc_check_cookie_domain(r, cfg, session) == FALSE)
......@@ -1385,9 +1394,37 @@ static int oidc_handle_existing_session(request_rec *r, oidc_cfg *cfg,
/* copy id_token and claims from session to request state and obtain their values */
oidc_copy_tokens_to_request_state(r, session, &s_id_token, &s_claims);
/* set the claims in the app headers */
if (oidc_set_app_claims(r, cfg, session, s_claims) == FALSE)
return HTTP_INTERNAL_SERVER_ERROR;
if ((cfg->pass_userinfo_as & OIDC_PASS_USERINFO_AS_CLAIMS)) {
/* set the userinfo claims in the app headers */
if (oidc_set_app_claims(r, cfg, session, s_claims) == FALSE)
return HTTP_INTERNAL_SERVER_ERROR;
}
if ((cfg->pass_userinfo_as & OIDC_PASS_USERINFO_AS_JSON_OBJECT)) {
/* pass the userinfo JSON object to the app in a header or environment variable */
oidc_util_set_app_info(r, OIDC_APP_INFO_USERINFO_JSON, s_claims,
OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
}
if ((cfg->pass_userinfo_as & OIDC_PASS_USERINFO_AS_JWT)) {
if (cfg->session_type != OIDC_SESSION_TYPE_CLIENT_COOKIE) {
/* get the compact serialized JWT from the session */
const char *s_userinfo_jwt = oidc_session_get_userinfo_jwt(r,
session);
if (s_userinfo_jwt != NULL) {
/* pass the compact serialized JWT to the app in a header or environment variable */
oidc_util_set_app_info(r, OIDC_APP_INFO_USERINFO_JWT,
s_userinfo_jwt,
OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
} else {
oidc_debug(r,
"configured to pass userinfo in a JWT, but no such JWT was found in the session (probably no such JWT was returned from the userinfo endpoint)");
}
} else {
oidc_error(r,
"session type \"client-cookie\" does not allow storing/passing a userinfo JWT; use \"" OIDCSessionType " server-cache\" for that");
}
}
if ((cfg->pass_idtoken_as & OIDC_PASS_IDTOKEN_AS_CLAIMS)) {
/* set the id_token in the app headers */
......@@ -1596,7 +1633,7 @@ static apr_byte_t oidc_save_in_session(request_rec *r, oidc_cfg *c,
const char *remoteUser, const char *id_token, oidc_jwt_t *id_token_jwt,
const char *claims, const char *access_token, const int expires_in,
const char *refresh_token, const char *session_state, const char *state,
const char *original_url) {
const char *original_url, const char *userinfo_jwt) {
/* store the user in the session */
session->remote_user = remoteUser;
......@@ -1645,7 +1682,7 @@ static apr_byte_t oidc_save_in_session(request_rec *r, oidc_cfg *c,
provider->end_session_endpoint);
/* store claims resolved from userinfo endpoint */
oidc_store_userinfo_claims(r, session, provider, claims);
oidc_store_userinfo_claims(r, c, session, provider, claims, userinfo_jwt);
/* see if we have an access_token */
if (access_token != NULL) {
......@@ -1838,6 +1875,7 @@ static int oidc_handle_authorization_response(request_rec *r, oidc_cfg *c,
int expires_in = oidc_parse_expires_in(r,
apr_table_get(params, OIDC_PROTO_EXPIRES_IN));
char *userinfo_jwt = NULL;
/*
* optionally resolve additional claims against the userinfo endpoint
......@@ -1845,7 +1883,7 @@ static int oidc_handle_authorization_response(request_rec *r, oidc_cfg *c,
*/
const char *claims = oidc_retrieve_claims_from_userinfo_endpoint(r, c,
provider, apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN), NULL,
jwt->payload.sub);
jwt->payload.sub, &userinfo_jwt);
/* restore the original protected URL that the user was trying to access */
const char *original_url = oidc_proto_state_get_original_url(proto_state);
......@@ -1882,7 +1920,8 @@ static int oidc_handle_authorization_response(request_rec *r, oidc_cfg *c,
apr_table_get(params, OIDC_PROTO_ACCESS_TOKEN), expires_in,
apr_table_get(params, OIDC_PROTO_REFRESH_TOKEN),
apr_table_get(params, OIDC_PROTO_SESSION_STATE),
apr_table_get(params, OIDC_PROTO_STATE), original_url) == FALSE)
apr_table_get(params, OIDC_PROTO_STATE), original_url,
userinfo_jwt) == FALSE)
return HTTP_INTERNAL_SERVER_ERROR;
} else {
......@@ -2580,17 +2619,21 @@ static int oidc_handle_logout(request_rec *r, oidc_cfg *c,
if (id_token_hint != NULL) {
logout_request = apr_psprintf(r->pool, "%s%sid_token_hint=%s",
logout_request,
strchr(logout_request, OIDC_CHAR_QUERY) != NULL ?
OIDC_STR_AMP : OIDC_STR_QUERY,
oidc_util_escape_string(r, id_token_hint));
strchr(logout_request ? logout_request : "",
OIDC_CHAR_QUERY) != NULL ?
OIDC_STR_AMP :
OIDC_STR_QUERY,
oidc_util_escape_string(r, id_token_hint));
}
if (url != NULL) {
logout_request = apr_psprintf(r->pool,
"%s%spost_logout_redirect_uri=%s", logout_request,
strchr(logout_request, OIDC_CHAR_QUERY) != NULL ?
OIDC_STR_AMP : OIDC_STR_QUERY,
oidc_util_escape_string(r, url));
strchr(logout_request ? logout_request : "",
OIDC_CHAR_QUERY) != NULL ?
OIDC_STR_AMP :
OIDC_STR_QUERY,
oidc_util_escape_string(r, url));
}
url = logout_request;
}
......@@ -2873,10 +2916,9 @@ end:
/* pass optional error message to the return URL */
if (error_code != NULL)
return_to = apr_psprintf(r->pool, "%s%serror_code=%s", return_to,
strchr(return_to, OIDC_CHAR_QUERY) ?
strchr(return_to ? return_to : "", OIDC_CHAR_QUERY) ?
OIDC_STR_AMP :
OIDC_STR_QUERY,
oidc_util_escape_string(r, error_code));
OIDC_STR_QUERY, oidc_util_escape_string(r, error_code));
/* add the redirect location header */
oidc_util_hdr_out_location_set(r, return_to);
......
......@@ -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);