[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#991811: unblock: libapache2-mod-auth-openidc/2.4.9-1



Attached debdiff
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/auth_openidc.conf libapache2-mod-auth-openidc-2.4.9/auth_openidc.conf
--- libapache2-mod-auth-openidc-2.4.4.1/auth_openidc.conf	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/auth_openidc.conf	2021-07-22 18:31:06.000000000 +0200
@@ -17,7 +17,11 @@
 # - encryption of the (temporary) state cookie
 # - encryption of cache entries, that may include the session cookie, see: OIDCCacheEncrypt and OIDCSessionType
 # Note that an encrypted cache mechanism can be shared between servers if they use the same OIDCCryptoPassphrase
-#OIDCCryptoPassphrase <passphrase>
+# Only for Apache >= 2.4.x:
+# If the value begins with exec: the resulting command will be executed and the
+# first line returned to standard output by the program will be used as the password.
+# The command may be absolute or relative to the web server root.
+#OIDCCryptoPassphrase [ <passphrase> | "exec:/path/to/otherProgram arg1" ]
 
 #
 # All other entries below this are optional though some may be required in a
@@ -144,6 +148,9 @@
 
 # The refresh interval in seconds for the claims obtained from the userinfo endpoint
 # When not defined the default is 0, i.e. the claims are retrieved only at session creation time.
+# If refreshing fails, it is assumed that the access token is expired and an attempt will be made
+# to refresh the access token using the refresh token grant, after which a second attempt is made
+# to obtain claims from the userinfo endpoint with the new access token.
 # NB: this can be overridden on a per-OP basis in the .conf file using the key: userinfo_refresh_interval
 #OIDCUserInfoRefreshInterval <seconds>
 
@@ -223,6 +230,14 @@
 # NB: this can be overridden on a per-OP basis in the .conf file using the key: token_endpoint_tls_client_key
 #OIDCClientTokenEndpointKey <filename>
 
+# Password for the PEM-formatted private key that belongs to the client certificate used to authenticate the
+# Client in calls to the token endpoint of the OAuth 2.0 Authorization server.
+# If the value begins with exec: the resulting command will be executed and the
+# first line returned to standard output by the program will be used as the password.
+# The command may be absolute or relative to the web server root.
+# NB: this can be overridden on a per-OP basis in the .conf file using the key: token_endpoint_tls_client_key_pwd
+#OIDCClientTokenEndpointKeyPassword [ <passphrase> | "exec:/path/to/otherProgram arg1" ]
+
 # The client name that the client registers in dynamic registration with the OP.
 # When not defined, no client name will be sent with the registration request.
 # NB: this can be overridden on a per-OP basis in the .conf file using the key: client_name
@@ -380,7 +395,9 @@
 # to be refreshed by introspecting (and validating) it again against the Authorization Server.
 # (can be configured on a per-path basis)
 # When not defined the value is 0, which means it only expires after the `exp` (or alternative,
-# see OIDCOAuthTokenExpiryClaim) hint as returned by the Authorization Server
+# see OIDCOAuthTokenExpiryClaim) hint as returned by the Authorization Server.
+# When set to -1, caching of the introspection results is disabled and the token will be introspected
+# on each request presenting it.
 #OIDCOAuthTokenIntrospectionInterval <seconds>
 
 # Require a valid SSL server certificate when communicating with the Authorization Server
@@ -424,7 +441,7 @@
 # Backrefrences must be in the form $1, $2.. etc.
 # E.g. to extract username in the form DOMAIN\userid from e-mail style address you may use
 #   ^(.*)@([^.]+)\..+$ $2\\$1
-#OIDCOAuthRemoteUserClaim <claim-name> [<regular-expression>]
+#OIDCOAuthRemoteUserClaim <claim-name> [<regular-expression>] [substitution-string]
 
 # Define the way(s) in which bearer OAuth 2.0 access tokens can be passed to this Resource Server.
 # Must be one or several of:
@@ -568,8 +585,7 @@
 #  c) validated OAuth 2.0 tokens
 #  d) JWK sets that have been retrieved from jwk_uri's
 #  e) resolved OP metadata when using OIDCProviderMetadataUrl
-#  f) JWT ID claims (jti) when using OP-init-SSO
-#  g) temporary state associated with Request URI's
+#  f) temporary state associated with Request URI's
 # must be one of \"shm\", \"memcache\", \"file\" or, if Redis support is compiled in, \"redis\" 
 # When not defined, "shm" (shared memory) is used.
 #OIDCCacheType [shm|memcache|file[|redis]]
@@ -616,6 +632,18 @@
 # When not specified, no authentication is performed.
 #OIDCRedisCachePassword <password>
 
+# Logical database to select on the Redis server: https://redis.io/commands/select
+# When not defined the default database 0 is used.
+#OIDCRedisCacheDatabase <number>
+
+# Timeout for connecting to the Redis servers.
+# When not defined the default connect timeout is 5 seconds.
+#OIDCRedisCacheConnectTimeout <seconds>
+
+# Timeout waiting for a response of the Redis servers after a request was sent.
+# When not defined the default timeout is 5 seconds.
+#OIDCRedisCacheTimeout <seconds>
+
 ########################################################################################
 #
 # Advanced Settings
@@ -649,10 +677,16 @@
 # When not defined a bare-bones internal template is used.
 #OIDCHTMLErrorTemplate <filename>
 
-# Defines a default URL to be used in case of 3rd-party or OP initiated
-# SSO when no explicit target_link_uri has been provided. The user is also
-# sent to this URL is in case an invalid authorization response was received.
-# When not defined, 3rd-party SSO must be done with a specified \"target_link_uri\" parameter.
+# Defines a default URL to be used in case of 3rd-party-init-SSO when no explicit target_link_uri
+# has been provided. The user is also redirected to this URL in case an invalid authorization
+# response was received.
+#
+# By default, when no OIDCDefaultURL is set, an expired state cookie will lead to an HTML error page
+# being sent to the browser explaining what happened. To copy that (legacy) behaviour when OIDCDefaultURL is set,
+# so that the browser is no longer redirected to the OIDCDefaultURL in case of state cookie expiry, use:
+#   SetEnvIfExpr true OIDC_NO_DEFAULT_URL_ON_STATE_TIMEOUT=true 
+#
+# The default is to not redirect the browser to any URL but return an HTTP/HTML error to the user.
 #OIDCDefaultURL <default-url>
 
 # Defines a default URL where the user is sent to after logout, which may be overridden explicitly during logout.
@@ -703,7 +737,7 @@
 # Backrefrences must be in the form $1, $2.. etc.
 # E.g. to extract username in the form DOMAIN\userid from e-mail style address you may use
 #  ^(.*)@([^.]+)\..+$ $2\\$1
-#OIDCRemoteUserClaim <claim-name>[@] [<regular-expression>]
+#OIDCRemoteUserClaim <claim-name>[@] [<regular-expression>] [substitution-string]
 
 # Define the way(s) in which the id_token contents are passed to the application according to OIDCPassClaimsAs.
 # Must be one or several of:
@@ -726,10 +760,15 @@
 # "environment": claims/tokens are passed as environment variables
 # "headers": claims/tokens are passed in headers (also useful in reverse proxy scenario's)
 # "both": claims/tokens are passed as both headers as well as environment variables (default)
-# When not defined the default is "both"
+#
+# Since version 2.4.6 one can specify "base64url" as the 2nd argument to apply base64url encoding to
+# all values passed in headers.
+#
+# When not defined the default is "both" and base64url encoding is not applied to the header values.
+#
 # The access token is passed in OIDC_access_token; the access token expiry is passed in OIDC_access_token_expires.
 # The refresh token is only passed in OIDC_refresh_token if enabled for that specific directory/location (see: OIDCPassRefreshToken)
-#OIDCPassClaimsAs [none|headers|environment|both]
+#OIDCPassClaimsAs [none|headers|environment|both] [base64url]
 
 # Specify the HTTP header variable name to set with the name of the authenticated user,
 # i.e. copy what is set in REMOTE_USER and configured in OIDCRemoteUserClaim or OIDCOAuthRemoteUserClaim.
@@ -773,6 +812,7 @@
 # and/or the absence of "Accept" header with any of the values "text/html" "application/xhtml+xml" or "*/*"
 # and returns 401 for such non-browser/non-html clients. See: https://github.com/zmartzone/mod_auth_openidc/wiki/Cookies#tldr
 # 
+# Only for Apache >= 2.4.x:
 # Since verson 2.4.4 a boolean Apache expression as the second parameter to specify which requests
 # need to match to return the configured value in the first parameter to override the default "auth".
 # See also: https://httpd.apache.org/docs/2.4/expr.html.
@@ -802,8 +842,10 @@
 #OIDCUnAutzAction [401|403|auth]
 
 # Indicates whether POST data will be preserved across authentication requests (and discovery in case of multiple OPs).
-# Preservation is done via HTML 5 local storage. Note that this can lead to private data exposure on shared terminals, 
-# that is why the default is "Off". Can be configured on a per Directory/Location basis.
+# This is designed to prevent data loss when a session timeout occurs in a (long) user filled HTML form.
+# It cannot handle arbitrary payloads for security (DOS) reasons, merely form-encoded user data.
+# Preservation is done via HTML 5 local storage: note that this can lead to private data exposure on shared terminals.
+# The default is "Off" (for security reasons). Can be configured on a per Directory/Location basis.
 #OIDCPreservePost [On|Off]
 
 # Indicates whether the refresh token will be passed to the application in a header/environment variable, according
@@ -840,9 +882,12 @@
 #   id_token (object)          : the claims presented in the ID token
 #   userinfo (object)          : the claims resolved from the UserInfo endpoint
 #   refresh_token (string)     : the refresh token (if returned by the OP)
+#   exp (int)                  : the maximum session lifetime (Unix timestamp in seconds)
+#   timeout (int)              : the session inactivity timeout (Unix timestamp in seconds)
+#   remote_user (string)       : the remote user name
 #   session (object)           : (for debugging) mod_auth_openidc specific session data such as "remote user", "session expiry", "session id" and a "state" object
 # When not defined the session hook will not return any data but a HTTP 404
-#OIDCInfoHook [iat|access_token|access_token_expires|id_token|userinfo|refresh_token|session]+
+#OIDCInfoHook [iat|access_token|access_token_expires|id_token|userinfo|refresh_token|exp|timeout|remote_user|session]+
 
 # Specify claims that should be removed from the userinfo and/or id_token before storing them in the session.
 # Note that OIDCBlackListedClaims takes precedence over OIDCWhiteListedClaims
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/AUTHORS libapache2-mod-auth-openidc-2.4.9/AUTHORS
--- libapache2-mod-auth-openidc-2.4.4.1/AUTHORS	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/AUTHORS	2021-07-22 18:31:06.000000000 +0200
@@ -64,3 +64,13 @@
 	Bryan Ingram <https://github/bcingram>
 	Tim Deisser <https://github.com/deisser>
 	Peter Hurtenbach <https://github.com/Peter0x48>
+	Paul Spangler <https://github.com/spanglerco>
+	Chris Pawling <https://github.com/chris468>
+	Matthias Fleschütz <https://github.com/blindzero>
+	Harri Rautila <https://github.com/hrautila>
+	Tatsuhiko Yasumatsu <https://github.com/ty60>
+	Adam Stadler <https://github.com/tzfx>
+	Steffen Greber <https://github.com/codemaker219>
+	Iain Heggie <https://github.com/iainh>
+
+
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/ChangeLog libapache2-mod-auth-openidc-2.4.9/ChangeLog
--- libapache2-mod-auth-openidc-2.4.4.1/ChangeLog	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/ChangeLog	2021-07-22 18:31:06.000000000 +0200
@@ -1,8 +1,186 @@
+07/22/2021
+- use redisvCommand to avoid crash with crafted key when using Redis without encryption; thanks @thomas-chauchefoin-sonarsource
+- replace potentially harmful backslashes with forward slashes when validating redirection URLs; thanks @thomas-chauchefoin-sonarsource
+- release 2.4.9
+
+07/15/2021
+- verify that "alg" is not none in logout_token explicitly
+- make session not found on backchannel logout produce a log warning instead of error
+- don't clear POST params authn on token revocation; thanks @iainh
+- bump to 2.4.9rc0
+
+closes #626
+07/02/2021
+- handle discovery in the content handler
+- return OK in the content handler for calls to the redirect URI and when preserving POST data
+
+06/25/2021
+- avoid XSS vulnerability when using OIDCPreservePost On and supplying URLs that contain single quotes
+  thanks @oss-aimoto
+
+06/21/2021
+- strip A256GCM JWT header from encrypted JWTS used for state cookies, cache encryption and by-value session cookies
+  resulting in smaller cookies and reduced cache content size
+
+06/10/2021
+- use encrypted JWTs for storing encrypted cache contents and avoid using static AAD/IV; thanks @niebardzo
+- bump to 2.4.9-dev
+
+06/04/2021
+- fix a problem where the host and port are calculated incorrectly, when you use literal ipv6 address.
+
+06/02/2021
+- do not send state timeout HTML document when OIDCDefaultURL is set; this can be overridden by using e.g.:
+  SetEnvIfExpr true OIDC_NO_DEFAULT_URL_ON_STATE_TIMEOUT=true 
+- release 2.4.8.4
+
+06/01/2021
+- avoid Apache 2.4 appending 400/302(200/404) HTML document text to state timeout HTML info page
+  see also f5959d767b0eec4856d561cbaa6d2262a52da551 and #484; at least Debian Buster was affected
+- release 2.4.8.3
+
+05/18/2021
+- make error "session corrupted: no issuer found in session" a warning only so a logout call for a
+  non-existing session no longer produces error messages
+
+05/08/2021
+- store timestamps in session in seconds to avoid string conversion problems on some (libapr-1)
+  platform build/run combinations, causing "maximum session duration exceeded" errors
+- bump to 2.4.8.2
+
+05/07/2021
+- add OIDCClientTokenEndpointKeyPassword option to allow the use of an encrypted private key
+- release 2.4.8.1
+
+04/30/2021
+- fix potential crash when Content-Type is not set in POST requests; thanks Tatsuhiko Yasumatsu of JPCERT/CC
+- release 2.4.8
+
+04/21/2021
+- on OAuth 2.0 RS token scope/claim 401 error, add environment variable for usage with mod_headers,
+  instead of adding a header ourselves; see #572; usage, e.g;
+    Header always append WWW-Authenticate %{OIDC_OAUTH_BEARER_SCOPE_ERROR}e "expr=(%{REQUEST_STATUS} == 401) && (-n reqenv('OIDC_OAUTH_BEARER_SCOPE_ERROR'))"
+- bump to 2.4.8-dev
+
+04/13/2021
+- add OIDCRedisCacheConnectTimeout and OIDCRedisCacheTimeout options to configure Redis timeouts
+- bump to 2.4.7.2
+
+04/12/2021
+- fix memory leaks when caching fails
+- bump to 2.4.7.1
+
+04/04/2021
+- improve documentation on OIDCPreservePost
+- release 2.4.7
+
+04/01/2021
+- bump to 2.4.7rc1
+
+02/16/2021
+- remove session from cache before clearing it.
+
+02/12/2021
+- add maximum session lifetime (exp), inactivity timeout (timeout) and remote_user to OIDCInfoHook
+- bump to 2.4.7-dev
+
+02/08/2021
+- return 400 instead of 500 when state cookie matching fails
+- release 2.4.6
+
+02/03/2021
+- avoid displaying the client_secret in debug logs
+
+01/28/2021
+- avoid segmentation fault when hitting an endpoint configured with AuthType openid-connect
+  in an OAuth 2.0 only setup; see #529
+
+01/23/2021
+- fix semaphore cleanup on graceful restarts; see #522
+
+01/12/2021
+- fix inconsistent public/private keys loading order; closes #515
+
+12/17/2020
+- remove support for https://tools.ietf.org/html/draft-bradley-oauth-jwt-encoded-state
+
+12/10/2020
+- add "base64url" option to OIDCPassClaimsAs primitive; closes #417
+
+12/09/2020
+- add Redis database selection option with OIDCRedisCacheDatabase; closes #423
+- optimize Redis AUTH execution once per connection
+
+12/07/2020
+- don't set SameSite=None on cookies when on plain http
+
+12/03/2020
+- add environment variable to control libcURL CURLOPT_SSL_OPTIONS behaviors
+  e.g.: SetEnvIfExpr true CURLOPT_SSL_OPTIONS=CURLSSLOPT_NO_REVOKE
+
+11/23/2020
+- release 2.4.5
+- make sure the module compiles with Apache 2.2 for passphrase exec:
+- bump to 2.4.6-dev
+
+11/19/2020
+- ensure that "sub" is returned from the userinfo endpoint following https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
+  prevents potential ID spoofing; thanks Christian Fries of Ruhr-University Bochum
+- don't printout JSON errors about NULL characters in error log; thanks Christian Fries of Ruhr-University Bochum
+- restrict printout of JSON parsing errors to 4096 bytes; thanks Christian Fries of Ruhr-University Bochum
+- bump to 2.4.5rc6
+
+11/5/2020
+- fix content processing for info and JWKs handler so mod_headers etc. works; closes #497
+- bump to 2.4.5rc5
+
+11/2/2020
+- improve sanity checking on Redis reply
+- bump to 2.4.5rc4
+
+10/30/2020
+- disable caching token introspection results by setting OIDCOAuthTokenIntrospectionInterval to -1; thanks @wadahiro
+- bump to 2.4.5rc3
+
+10/27/2020
+- config check on OIDCCryptoPassphrase in OAuth 2.0 RS setup with cache encryption enabled
+- bump to 2.4.5rc2
+
+10/22/2020
+- hash define expression option to OIDCUnAuthAction so it compiles for Apache 2.2; fixes 1461634
+- bump to 2.4.5rc1
+- add exec support to OIDCCryptoPassphrase
+ 
+10/19/2020
+- delete stale session cookies that aren't in the cache
+- allow OIDCDiscoverURL to be a relative URL
+
+10/08/2020
+- add OIDCCABundlePath for configuring path to curl CA bundle
+
+09/22/2020
+- avoid Apache 2.4 appending 401 HTML document text to step-up authentication HTML refresh page; closes #484
+- bump to 2.4.5rc0
+
+09/21/2020
+- populate AUTH_TYPE when performing authentication; thanks @spanglerco
+
+09/19/2020
+- enable authentication of sub-requests when the main request doesn't require
+  authentication; thanks @spanglerco
+
 09/03/2020
 - add SameSite attribute on cookie clearance / logout; thanks @v0gler
 - bump to 2.4.4.1
 
 09/01/2020
+- forward port Tufin patches
+- always set session cookie same site policy to Lax
+- disable cookie domain check
+- unset host headers for metadata URL retrieval
+- bump to 2.4.4-tufin
+
+09/01/2020
 - avoid GCC 9 compiler warnings
 - release 2.4.4
 
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/configure.ac libapache2-mod-auth-openidc-2.4.9/configure.ac
--- libapache2-mod-auth-openidc-2.4.4.1/configure.ac	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/configure.ac	2021-07-22 18:31:06.000000000 +0200
@@ -1,4 +1,4 @@
-AC_INIT([mod_auth_openidc],[2.4.4.1],[hans.zandbelt@zmartzone.eu])
+AC_INIT([mod_auth_openidc],[2.4.9],[hans.zandbelt@zmartzone.eu])
 
 AC_SUBST(NAMEVER, AC_PACKAGE_TARNAME()-AC_PACKAGE_VERSION())
 
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/.cproject libapache2-mod-auth-openidc-2.4.9/.cproject
--- libapache2-mod-auth-openidc-2.4.4.1/.cproject	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/.cproject	2021-07-22 18:31:06.000000000 +0200
@@ -1,94 +1,180 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
 <?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
-    	
-    <storageModule moduleId="org.eclipse.cdt.core.settings">
-        		
-        <cconfiguration id="org.eclipse.cdt.core.default.config.584843498">
-            			
-            <storageModule buildSystemId="org.eclipse.cdt.core.defaultConfigDataProvider" id="org.eclipse.cdt.core.default.config.584843498" moduleId="org.eclipse.cdt.core.settings" name="Configuration">
-                				
-                <externalSettings/>
-                				
-                <extensions/>
-                			
-            </storageModule>
-            			
-            <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
-            		
-        </cconfiguration>
-        	
-    </storageModule>
-    	
-    <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
-    	
-    <storageModule moduleId="scannerConfiguration"/>
-    	
-    <storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
-        		
-        <buildTargets>
-            			
-            <target name="all" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
-                				
-                <buildCommand>make</buildCommand>
-                				
-                <buildTarget>all</buildTarget>
-                				
-                <stopOnError>false</stopOnError>
-                				
-                <useDefaultCommand>true</useDefaultCommand>
-                				
-                <runAllBuilders>true</runAllBuilders>
-                			
-            </target>
-            			
-            <target name="clean" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
-                				
-                <buildCommand>make</buildCommand>
-                				
-                <buildArguments/>
-                				
-                <buildTarget>clean</buildTarget>
-                				
-                <stopOnError>false</stopOnError>
-                				
-                <useDefaultCommand>true</useDefaultCommand>
-                				
-                <runAllBuilders>true</runAllBuilders>
-                			
-            </target>
-            			
-            <target name="test" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
-                				
-                <buildCommand>make</buildCommand>
-                				
-                <buildArguments/>
-                				
-                <buildTarget>test</buildTarget>
-                				
-                <stopOnError>false</stopOnError>
-                				
-                <useDefaultCommand>true</useDefaultCommand>
-                				
-                <runAllBuilders>true</runAllBuilders>
-                			
-            </target>
-            		
-        </buildTargets>
-        	
-    </storageModule>
-    	
-    <storageModule moduleId="org.eclipse.cdt.core.pathentry">
-        		
-        <pathentry include="/opt/local/include/apache2" kind="inc" path="" system="true"/>
-        		
-        <pathentry include="/opt/local/include/apr-1" kind="inc" path="" system="true"/>
-        		
-        <pathentry include="/opt/local/include" kind="inc" path="" system="true"/>
-        		
-        <pathentry kind="src" path=""/>
-        		
-        <pathentry kind="out" path=""/>
-        	
-    </storageModule>
-    
-</cproject>
+	<storageModule moduleId="org.eclipse.cdt.core.settings">
+		<cconfiguration id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1562680719">
+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1562680719" moduleId="org.eclipse.cdt.core.settings" name="Build (GNU)">
+				<externalSettings/>
+				<extensions>
+					<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.Cygwin_PE" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+				</extensions>
+			</storageModule>
+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+				<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.linuxtools.cdt.autotools.core.buildArtefactType.autotools" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.linuxtools.cdt.autotools.core.buildArtefactType.autotools,org.eclipse.cdt.build.core.buildType=org.eclipse.linuxtools.cdt.autotools.core.buildType.default" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GASErrorParser" id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1562680719" name="Build (GNU)" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.connection=unix:///var/run/docker.sock,org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=" parent="org.eclipse.linuxtools.cdt.autotools.core.configuration.build">
+					<folderInfo id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1562680719." name="/" resourcePath="">
+						<toolChain id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.617277945" name="GNU Autotools Toolchain" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolChain">
+							<targetPlatform id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform.359991688" isAbstract="false" name="GNU Autotools Target Platform" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform"/>
+							<builder id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder.1276700988" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Autotools Makefile Generator" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder">
+								<outputEntries>
+									<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name=""/>
+								</outputEntries>
+							</builder>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure.1747239206" name="configure" superClass="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure">
+								<option id="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name.944169641" name="Name" superClass="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name" value="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1562680719" valueType="string"/>
+							</tool>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen.1984259027" name="autogen.sh" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen"/>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.548331172" name="GCC C Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc">
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.include.paths.811285626" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+									<listOptionValue builtIn="false" value="&quot;${workspace_loc:/liboauth2-docker/src/liboauth2/include}&quot;"/>
+									<listOptionValue builtIn="false" value="/Users/hzandbelt/projects/nginx-1.15.5/src/core"/>
+									<listOptionValue builtIn="false" value="/Users/hzandbelt/projects/nginx-1.15.5/src/http"/>
+									<listOptionValue builtIn="false" value="/usr/local/include"/>
+									<listOptionValue builtIn="false" value="/opt/local/include"/>
+								</option>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.preprocessor.def.symbols.2047978831" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
+									<listOptionValue builtIn="false" value="HAVE_LIBHIREDIS=1"/>
+									<listOptionValue builtIn="false" value="HAVE_LIBMEMCACHE=1"/>
+								</option>
+								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1754841162" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+							</tool>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.1890200658" name="GCC C++ Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp">
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.cpp.compiler.option.preprocessor.def.1849424407" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols">
+									<listOptionValue builtIn="false" value="HAVE_LIBMEMCACHE=1"/>
+									<listOptionValue builtIn="false" value="HAVE_LIBHIREDIS=1"/>
+								</option>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.cpp.compiler.option.include.paths.2024609705" name="Include paths (-I)" superClass="gnu.cpp.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath"/>
+								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1378665586" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+							</tool>
+						</toolChain>
+					</folderInfo>
+					<sourceEntries>
+						<entry excluding="m4|src" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="m4"/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
+					</sourceEntries>
+				</configuration>
+			</storageModule>
+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+		</cconfiguration>
+		<cconfiguration id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.1551656597">
+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.1551656597" moduleId="org.eclipse.cdt.core.settings" name="Debug (GNU)">
+				<externalSettings/>
+				<extensions>
+					<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.Cygwin_PE" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.PE" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GNU_ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+				</extensions>
+			</storageModule>
+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+				<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.linuxtools.cdt.autotools.core.buildArtefactType.autotools" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.linuxtools.cdt.autotools.core.buildArtefactType.autotools,org.eclipse.cdt.build.core.buildType=org.eclipse.linuxtools.cdt.autotools.core.buildType.debug" cleanCommand="rm -rf" description="" errorParsers="org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GASErrorParser" id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.1551656597" name="Debug (GNU)" optionalBuildProperties="org.eclipse.cdt.docker.launcher.containerbuild.property.volumes=,org.eclipse.cdt.docker.launcher.containerbuild.property.connection=unix:///var/run/docker.sock,org.eclipse.cdt.docker.launcher.containerbuild.property.selectedvolumes=" parent="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug">
+					<folderInfo id="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.1551656597." name="/" resourcePath="">
+						<toolChain id="org.eclipse.linuxtools.cdt.autotools.core.toolChain.debug.1772566252" name="GNU Autotools Toolchain" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolChain.debug">
+							<targetPlatform id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform.debug.1422301246" isAbstract="false" name="GNU Autotools Target Platform" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.targetPlatform.debug"/>
+							<builder id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder.debug.1646887820" keepEnvironmentInBuildfile="false" managedBuildOn="true" name="Autotools Makefile Generator" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.builder.debug"/>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure.debug.1645422490" name="configure" superClass="org.eclipse.linuxtools.cdt.autotools.core.gnu.toolchain.tool.configure.debug">
+								<option defaultValue="CFLAGS=-g -O0" id="org.eclipse.linuxtools.cdt.autotools.core.option.configure.user.1759217067" name="User-specified configuration options" superClass="org.eclipse.linuxtools.cdt.autotools.core.option.configure.user" valueType="string"/>
+								<option id="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name.1045624672" name="Name" superClass="org.eclipse.linuxtools.cdt.autotools.core.option.configure.name" value="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.1551656597" valueType="string"/>
+							</tool>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen.debug.831501834" name="autogen.sh" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.autogen.debug"/>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.debug.397973033" name="GCC C Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.debug">
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.include.paths.300127578" name="Include paths (-I)" superClass="gnu.c.compiler.option.include.paths" useByScannerDiscovery="false" valueType="includePath">
+									<listOptionValue builtIn="false" value="/Users/hzandbelt/projects/nginx-1.15.5/src/core"/>
+									<listOptionValue builtIn="false" value="/Users/hzandbelt/projects/nginx-1.15.5/src/http"/>
+								</option>
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="false" id="gnu.c.compiler.option.preprocessor.def.symbols.854310462" name="Defined symbols (-D)" superClass="gnu.c.compiler.option.preprocessor.def.symbols" useByScannerDiscovery="false" valueType="definedSymbols">
+									<listOptionValue builtIn="false" value="HAVE_LIBHIREDIS=1"/>
+									<listOptionValue builtIn="false" value="HAVE_LIBMEMCACHE=1"/>
+								</option>
+								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.814681391" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+							</tool>
+							<tool id="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.debug.1365041530" name="GCC C++ Compiler" superClass="org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gpp.debug">
+								<option IS_BUILTIN_EMPTY="false" IS_VALUE_EMPTY="true" id="gnu.cpp.compiler.option.preprocessor.def.102425444" name="Defined symbols (-D)" superClass="gnu.cpp.compiler.option.preprocessor.def" useByScannerDiscovery="false" valueType="definedSymbols"/>
+								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1683804805" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+							</tool>
+						</toolChain>
+					</folderInfo>
+					<sourceEntries>
+						<entry excluding="m4|src" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="m4"/>
+						<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="src"/>
+					</sourceEntries>
+				</configuration>
+			</storageModule>
+		</cconfiguration>
+	</storageModule>
+	<storageModule moduleId="org.eclipse.cdt.core.pathentry">
+		<pathentry include="/opt/local/include/apache2" kind="inc" path="" system="true"/>
+		<pathentry include="/opt/local/include/apr-1" kind="inc" path="" system="true"/>
+		<pathentry include="/opt/local/include" kind="inc" path="" system="true"/>
+		<pathentry include="/Library/Developer/CommandLineTools/SDKs/MacOSX11.1.sdk/usr/include" kind="inc" path="" system="true"/>
+		<pathentry include="/Library/Developer/CommandLineTools/usr/lib/clang/12.0.0/include" kind="inc" path="" system="true"/>
+		<pathentry kind="mac" name="HAVE_LIBHIREDIS" path="" value=""/>
+		<pathentry kind="mac" name="HAVE_MEMCACHE" path="" value=""/>
+		<pathentry kind="src" path=""/>
+		<pathentry kind="out" path=""/>
+	</storageModule>
+	<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
+		<buildTargets>
+			<target name="test" path=".libs" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+				<buildCommand>make</buildCommand>
+				<buildArguments/>
+				<buildTarget>test</buildTarget>
+				<stopOnError>true</stopOnError>
+				<useDefaultCommand>true</useDefaultCommand>
+				<runAllBuilders>false</runAllBuilders>
+			</target>
+			<target name="clean" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+				<buildCommand>make</buildCommand>
+				<buildArguments/>
+				<buildTarget>clean</buildTarget>
+				<stopOnError>true</stopOnError>
+				<useDefaultCommand>true</useDefaultCommand>
+				<runAllBuilders>false</runAllBuilders>
+			</target>
+			<target name="all" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+				<buildCommand>make</buildCommand>
+				<buildArguments/>
+				<buildTarget>all</buildTarget>
+				<stopOnError>true</stopOnError>
+				<useDefaultCommand>true</useDefaultCommand>
+				<runAllBuilders>false</runAllBuilders>
+			</target>
+			<target name="test" path="" targetID="org.eclipse.cdt.make.MakeTargetBuilder">
+				<buildCommand>make</buildCommand>
+				<buildArguments/>
+				<buildTarget>test</buildTarget>
+				<stopOnError>true</stopOnError>
+				<useDefaultCommand>true</useDefaultCommand>
+				<runAllBuilders>false</runAllBuilders>
+			</target>
+		</buildTargets>
+	</storageModule>
+	<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+		<project id="mod_auth_openidc.org.eclipse.linuxtools.cdt.autotools.core.projectType.1319237749" name="GNU Autotools" projectType="org.eclipse.linuxtools.cdt.autotools.core.projectType"/>
+	</storageModule>
+	<storageModule moduleId="scannerConfiguration">
+		<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+		<scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1562680719;org.eclipse.linuxtools.cdt.autotools.core.configuration.build.1562680719.;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.548331172;cdt.managedbuild.tool.gnu.c.compiler.input.1754841162">
+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+		</scannerConfigBuildInfo>
+		<scannerConfigBuildInfo instanceId="org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.1551656597;org.eclipse.linuxtools.cdt.autotools.core.configuration.build.debug.1551656597.;org.eclipse.linuxtools.cdt.autotools.core.toolchain.tool.gcc.debug.397973033;cdt.managedbuild.tool.gnu.c.compiler.input.814681391">
+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+		</scannerConfigBuildInfo>
+	</storageModule>
+</cproject>
\ Kein Zeilenumbruch am Dateiende.
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/debian/changelog libapache2-mod-auth-openidc-2.4.9/debian/changelog
--- libapache2-mod-auth-openidc-2.4.4.1/debian/changelog	2021-06-07 20:54:00.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/debian/changelog	2021-08-02 11:45:39.000000000 +0200
@@ -1,3 +1,13 @@
+libapache2-mod-auth-openidc (2.4.9-1) unstable; urgency=medium
+
+  * New upstream version 2.4.9
+  * Fix for CVE-2021-32792 (closes: #991580)
+  * Fix for CVE-2021-32791 (closes: #991581)
+  * Fix for CVE-2021-32786 (closes: #991582)
+  * Fix for CVE-2021-32785 (closes: #991583)
+
+ -- Christoph Martin <martin@uni-mainz.de>  Mon, 02 Aug 2021 11:45:39 +0200
+
 libapache2-mod-auth-openidc (2.4.4.1-2) unstable; urgency=medium
 
   * fix CVE-2021-20718 using commit
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/debian/patches/CVE-2021-20718.patch libapache2-mod-auth-openidc-2.4.9/debian/patches/CVE-2021-20718.patch
--- libapache2-mod-auth-openidc-2.4.4.1/debian/patches/CVE-2021-20718.patch	2021-06-07 20:54:00.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/debian/patches/CVE-2021-20718.patch	1970-01-01 01:00:00.000000000 +0100
@@ -1,23 +0,0 @@
---- a/ChangeLog
-+++ b/ChangeLog
-@@ -1,3 +1,7 @@
-+04/30/2021
-+- fix potential crash when Content-Type is not set in POST requests; thanks JPCERT/CC
-+  from release 2.4.8
-+
- 09/03/2020
- - add SameSite attribute on cookie clearance / logout; thanks @v0gler
- - bump to 2.4.4.1
---- a/src/util.c
-+++ b/src/util.c
-@@ -1562,8 +1562,8 @@ apr_byte_t oidc_util_read_post_params(re
- 	const char *content_type = NULL;
- 
- 	content_type = oidc_util_hdr_in_content_type_get(r);
--	if ((r->method_number != M_POST) || (strstr(content_type,
--			OIDC_CONTENT_TYPE_FORM_ENCODED) != content_type)) {
-+	if ((r->method_number != M_POST) || (content_type == NULL) || (strstr(content_type,
-+				OIDC_CONTENT_TYPE_FORM_ENCODED) != content_type)) {
- 		oidc_debug(r, "required content-type %s not found",
- 				OIDC_CONTENT_TYPE_FORM_ENCODED);
- 		goto end;
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/debian/patches/series libapache2-mod-auth-openidc-2.4.9/debian/patches/series
--- libapache2-mod-auth-openidc-2.4.4.1/debian/patches/series	2021-06-07 20:54:00.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/debian/patches/series	2021-08-02 11:45:39.000000000 +0200
@@ -1,2 +1 @@
 fix-parallel-build.patch
-CVE-2021-20718.patch
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/Dockerfile libapache2-mod-auth-openidc-2.4.9/Dockerfile
--- libapache2-mod-auth-openidc-2.4.4.1/Dockerfile	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/Dockerfile	2021-07-22 18:31:06.000000000 +0200
@@ -1,13 +1,14 @@
 FROM ubuntu:bionic
 MAINTAINER hans.zandbelt@zmartzone.eu
 
+ENV DEBIAN_FRONTEND noninteractive
+
 RUN apt-get update && apt-get install -y pkg-config make gcc gdb lcov valgrind vim curl iputils-ping wget
 RUN apt-get update && apt-get install -y autoconf automake libtool
 RUN apt-get update && apt-get install -y libssl-dev libjansson-dev libcurl4-openssl-dev check
-#RUN apt-get update && apt-get install -y libcjose-dev
 RUN apt-get update && apt-get install -y apache2 apache2-dev
-
 RUN apt-get update && apt-get install -y libpcre3-dev zlib1g-dev
+RUN apt-get update && apt-get install -y libapache2-mod-php libhiredis-dev
 
 RUN wget https://mod-auth-openidc.org/download/libcjose0_0.6.1.5-1~bionic+1_amd64.deb
 RUN wget https://mod-auth-openidc.org/download/libcjose-dev_0.6.1.5-1~bionic+1_amd64.deb
@@ -35,4 +36,8 @@
 RUN a2enconf openidc
 RUN /usr/sbin/apache2ctl start
 
-# docker run -p 443:443 -it 749d1204d189 /bin/bash -c "source /etc/apache2/envvars && valgrind --leak-check=full /usr/sbin/apache2 -X"
+RUN mkdir -p /var/www/html/protected
+RUN echo "<html><body><h1>Hello, <?php echo($_SERVER['REMOTE_USER']) ?></h1><pre><?php print_r(array_map(\"htmlentities\", apache_request_headers())); ?></pre><a href=\"/protected/?logout=https%3A%2F%2Flocalhost.zmartzone.eu%2Floggedout.html\">Logout</a></body></html>" >  /var/www/html/protected/index.php
+RUN mkdir -p /var/www/html/api && cp /var/www/html/protected/index.php /var/www/html/api
+
+# docker run -p 443:443 -it mod_auth_openidc /bin/bash -c "source /etc/apache2/envvars && valgrind --leak-check=full /usr/sbin/apache2 -X"
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/.dockerignore libapache2-mod-auth-openidc-2.4.9/.dockerignore
--- libapache2-mod-auth-openidc-2.4.4.1/.dockerignore	1970-01-01 01:00:00.000000000 +0100
+++ libapache2-mod-auth-openidc-2.4.9/.dockerignore	2021-07-22 18:31:06.000000000 +0200
@@ -0,0 +1 @@
+/Dockerfile*
\ Kein Zeilenumbruch am Dateiende.
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/.github/issue_template.md libapache2-mod-auth-openidc-2.4.9/.github/issue_template.md
--- libapache2-mod-auth-openidc-2.4.4.1/.github/issue_template.md	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/.github/issue_template.md	2021-07-22 18:31:06.000000000 +0200
@@ -1,3 +1,4 @@
-THIS ISSUER TRACKER IS NO LONGER USED!
-DON'T CREATE ANY NEW ISSUES AS THEY WILL BE CLOSED IMMEDIATELY AS INVALID!
-QUESTIONS SHOULD GO TO THE MAILINGLIST AT: mod_auth_openidc@googlegroups.com
+This issue tracker is not for end users!
+Don't create any new issues as they will immediately be closed as invalid.
+Questions and discussions are at: https://github.com/zmartzone/mod_auth_openidc/discussions
+There's also a user mailing list at: mod_auth_openidc@googlegroups.com
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/.gitignore libapache2-mod-auth-openidc-2.4.9/.gitignore
--- libapache2-mod-auth-openidc-2.4.4.1/.gitignore	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/.gitignore	2021-07-22 18:31:06.000000000 +0200
@@ -19,4 +19,10 @@
 /missing
 /.settings/
 /.autotools
-/.vscode/
\ Kein Zeilenumbruch am Dateiende.
+/.vscode/
+/configure~
+/Dockerfile-*
+/*.rpm
+
+/config.guess~
+/config.sub~
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/openidc.conf libapache2-mod-auth-openidc-2.4.9/openidc.conf
--- libapache2-mod-auth-openidc-2.4.4.1/openidc.conf	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/openidc.conf	2021-07-22 18:31:06.000000000 +0200
@@ -24,6 +24,11 @@
 
 OIDCInfoHook iat access_token access_token_expires id_token userinfo refresh_token session
 
+OIDCScope "openid email profile"
+
+OIDCCacheType redis
+OIDCRedisCacheServer host.docker.internal
+
 <Location /protected>
   AuthType openid-connect
   Require valid-user
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/.project libapache2-mod-auth-openidc-2.4.9/.project
--- libapache2-mod-auth-openidc-2.4.4.1/.project	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/.project	2021-07-22 18:31:06.000000000 +0200
@@ -6,89 +6,21 @@
 	</projects>
 	<buildSpec>
 		<buildCommand>
-			<name>org.eclipse.cdt.make.core.makeBuilder</name>
-			<triggers>clean,full,incremental,</triggers>
-			<arguments>
-				<dictionary>
-					<key>org.eclipse.cdt.core.errorOutputParser</key>
-					<value>org.eclipse.cdt.autotools.core.ErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GmakeErrorParser;org.eclipse.cdt.core.VCErrorParser;org.eclipse.cdt.core.CWDLocator;org.eclipse.cdt.core.MakeErrorParser;</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.append_environment</key>
-					<value>true</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.build.arguments</key>
-					<value></value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.build.command</key>
-					<value>make</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.build.target.auto</key>
-					<value>all</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.build.target.clean</key>
-					<value>clean</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.build.target.inc</key>
-					<value>all</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
-					<value>false</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
-					<value>true</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.enableFullBuild</key>
-					<value>true</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>
-					<value>true</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.environment</key>
-					<value></value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.stopOnError</key>
-					<value>false</value>
-				</dictionary>
-				<dictionary>
-					<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
-					<value>true</value>
-				</dictionary>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
-			<name>org.eclipse.cdt.autotools.core.genmakebuilderV2</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-		<buildCommand>
 			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+			<triggers>clean,full,incremental,</triggers>
 			<arguments>
 			</arguments>
 		</buildCommand>
 		<buildCommand>
-			<name>org.eclipse.cdt.make.core.ScannerConfigBuilder</name>
+			<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+			<triggers>full,incremental,</triggers>
 			<arguments>
 			</arguments>
 		</buildCommand>
 	</buildSpec>
 	<natures>
-		<nature>org.eclipse.cdt.core.ccnature</nature>
 		<nature>org.eclipse.cdt.core.cnature</nature>
 		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
-		<nature>org.eclipse.cdt.make.core.ScannerConfigNature</nature>
-		<nature>org.eclipse.cdt.make.core.makeNature</nature>
-		<nature>org.eclipse.cdt.autotools.core.autotoolsNatureV2</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
 	</natures>
 </projectDescription>
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/README.md libapache2-mod-auth-openidc-2.4.9/README.md
--- libapache2-mod-auth-openidc-2.4.4.1/README.md	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/README.md	2021-07-22 18:31:06.000000000 +0200
@@ -38,9 +38,6 @@
 - [OpenID Connect Session Management 1.0](http://openid.net/specs/openid-connect-session-1_0.html) *(implementers draft; see the [Wiki](https://github.com/zmartzone/mod_auth_openidc/wiki/OpenID-Connect-Session-Management) for information on how to configure it)*
 - [OpenID Connect Front-Channel Logout 1.0](http://openid.net/specs/openid-connect-frontchannel-1_0.html) *(implementers draft)*
 - [OpenID Connect Back-Channel Logout 1.0](https://openid.net/specs/openid-connect-backchannel-1_0.html) *(implementers draft)*
-- [Encoding claims in the OAuth 2 state parameter using a JWT](https://tools.ietf.org/html/draft-bradley-oauth-jwt-encoded-state-08) *(draft spec)*
-- [OpenID Connect Token Bound Authentication](https://openid.net/specs/openid-connect-token-bound-authentication-1_0.html) *(draft spec; when combined with [mod_token_binding](https://github.com/zmartzone/mod_token_binding))*
-- [OAuth 2.0 Token Binding for Authorization Codes for Web Server Clients](https://tools.ietf.org/html/draft-ietf-oauth-token-binding-07#section-5.2) *(draft spec)*
 
 For an exhaustive description of all configuration options, see the file `auth_openidc.conf`
 in this directory. This file can also serve as an include file for `httpd.conf`.
@@ -49,13 +46,13 @@
 -------
 
 #### Community Support
-For generic questions, see the Wiki pages with Frequently Asked Questions at:  
+For documentation, see the Wiki pages (including Frequently Asked Questions) at:  
   [https://github.com/zmartzone/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:  
+For generic questions there is a Github Discussions forum at:  
+  [https://github.com/zmartzone/mod_auth_openidc/discussions](https://github.com/zmartzone/mod_auth_openidc/discussions)
+There is a (now deprecated) Google Group/mailing list archive at:  
   [https://groups.google.com/forum/#!forum/mod_auth_openidc](https://groups.google.com/forum/#!forum/mod_auth_openidc)  
-Any questions/issues should go to the mailing list.
+Any questions/issues should go to the Discussions forum.
 
 #### Commercial Services
 For commercial Support contracts, Professional Services, Training and use-case specific support you can contact:  
@@ -132,7 +129,7 @@
 See the [Wiki](https://github.com/zmartzone/mod_auth_openidc/wiki) for configuration docs for other OpenID Connect Providers:
 - [GLUU Server](https://github.com/zmartzone/mod_auth_openidc/wiki/Gluu-Server)
 - [Keycloak](https://github.com/zmartzone/mod_auth_openidc/wiki/Keycloak)
-- [Azure AD](https://github.com/zmartzone/mod_auth_openidc/wiki/Azure-OAuth2.0-and-OpenID)
+- [Azure AD](https://github.com/zmartzone/mod_auth_openidc/wiki/Azure-Active-Directory-Authentication)
 - [Sign in with Apple](https://github.com/zmartzone/mod_auth_openidc/wiki/Sign-in-with-Apple)
 - [Curity Identity Server](https://github.com/zmartzone/mod_auth_openidc/wiki/Curity-Identity-Server)
 - [LemonLDAP::NG](https://github.com/zmartzone/mod_auth_openidc/wiki/LemonLDAP::NG)
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/SECURITY.md libapache2-mod-auth-openidc-2.4.9/SECURITY.md
--- libapache2-mod-auth-openidc-2.4.4.1/SECURITY.md	1970-01-01 01:00:00.000000000 +0100
+++ libapache2-mod-auth-openidc-2.4.9/SECURITY.md	2021-07-22 18:31:06.000000000 +0200
@@ -0,0 +1,28 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 2.4.x   | :white_check_mark: |
+| < 2.4.0   | :x:               |
+
+## Reporting a Vulnerability
+
+Please send an e-mail to support@zmartzone.eu with a description of:
+
+- a brief description of the vulnerability
+- how the vulnerability can be observed
+- optionally the type of vulnerability and any related OWASP category
+- non-destructive exploitation details
+
+## Followup
+After submitting your vulnerability report, you will receive an acknowledgement reply usually within 24 working hours of your report being received.
+
+The team will triage the reported vulnerability, and respond as soon as possible to let you know whether further information is required, whether the vulnerability is in or out of scope, or is a duplicate report. Priority for bug fixes or mitigations is assessed by looking at the impact severity and exploit complexity. 
+
+When the reported vulnerability is resolved, or remediation work is scheduled, the Support team will notify you, and invite you to confirm that the solution covers the vulnerability adequately.
+
+You are particularly invited to give us feedback on the disclosure handling process, the clarity and quality of the communication relationship, and of course the effectiveness of the vulnerability resolution. This feedback will be used in strict confidence to help us improve our processes for handling reports, developing services, and resolving vulnerabilities.
+
+Where a report qualifies, we will offer to include you on our thanks and acknowledgement page. We will ask you to confirm the details you want included before they are published.
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/authz.c libapache2-mod-auth-openidc-2.4.9/src/authz.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/authz.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/authz.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/cache/cache.h libapache2-mod-auth-openidc-2.4.9/src/cache/cache.h
--- libapache2-mod-auth-openidc-2.4.4.1/src/cache/cache.h	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/cache/cache.h	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/cache/common.c libapache2-mod-auth-openidc-2.4.9/src/cache/common.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/cache/common.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/cache/common.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -102,6 +94,8 @@
 	apr_status_t rv = APR_SUCCESS;
 	const char *dir;
 
+	// oidc_sdebug(s, "enter: %d (m=%pp,s=%pp, p=%d)", (m && m->sema) ? *m->sema : -1, m->mutex ? m->mutex : 0, s, m->is_parent);
+
 	/* construct the mutex filename */
 	apr_temp_dir_get(&dir, s->process->pool);
 	m->mutex_filename = apr_psprintf(s->process->pool,
@@ -155,6 +149,11 @@
 apr_status_t oidc_cache_mutex_child_init(apr_pool_t *p, server_rec *s,
 		oidc_cache_mutex_t *m) {
 
+	// oidc_sdebug(s, "enter: %d (m=%pp,s=%pp, p=%d)", (m && m->sema) ? *m->sema : -1, m->mutex ? m->mutex : 0, s, m->is_parent);
+
+	if (m->is_parent == FALSE)
+		return APR_SUCCESS;
+
 	/* initialize the lock for the child process */
 	apr_status_t rv = apr_global_mutex_child_init(&m->mutex,
 			(const char *) m->mutex_filename, p);
@@ -211,13 +210,17 @@
 
 	apr_status_t rv = APR_SUCCESS;
 
+	// oidc_sdebug(s, "enter: %d (m=%pp,s=%pp, p=%d)", (m && m->sema) ? *m->sema : -1, m->mutex ? m->mutex : 0, s, m->is_parent);
+
 	if (m->mutex != NULL) {
 
 		apr_global_mutex_lock(m->mutex);
 		(*m->sema)--;
 		//oidc_sdebug(s, "semaphore: %d (m=%pp,s=%pp)", *m->sema, m->mutex, s);
 
-		if ((m->shm != NULL) && (*m->sema == 0) && (m->is_parent == TRUE)) {
+		// oidc_sdebug(s, "processing: %d (m=%pp,s=%pp, p=%d)", (m && m->sema) ? *m->sema : -1, m->mutex ? m->mutex : 0, s, m->is_parent);
+
+		if ((m->shm != NULL) && (*m->sema == 0)) {
 
 			rv = apr_shm_destroy(m->shm);
 			oidc_sdebug(s, "apr_shm_destroy for semaphore returned: %d", rv);
@@ -241,325 +244,59 @@
 	return rv;
 }
 
-#define oidc_cache_crypto_openssl_error(r, fmt, ...) \
-		oidc_error(r, "%s: %s", apr_psprintf(r->pool, fmt, ##__VA_ARGS__), ERR_error_string(ERR_get_error(), NULL))
-
-#define OIDC_CACHE_CIPHER							EVP_aes_256_gcm()
-#define OIDC_CACHE_TAG_LEN							16
-
-#if (OPENSSL_VERSION_NUMBER >= 0x10100005L && !defined(LIBRESSL_VERSION_NUMBER))
-#define OIDC_CACHE_CRYPTO_GET_TAG					EVP_CTRL_AEAD_GET_TAG
-#define OIDC_CACHE_CRYPTO_SET_TAG					EVP_CTRL_AEAD_SET_TAG
-#define OIDC_CACHE_CRYPTO_SET_IVLEN					EVP_CTRL_AEAD_SET_IVLEN
-#else
-#define OIDC_CACHE_CRYPTO_GET_TAG					EVP_CTRL_GCM_GET_TAG
-#define OIDC_CACHE_CRYPTO_SET_TAG					EVP_CTRL_GCM_SET_TAG
-#define OIDC_CACHE_CRYPTO_SET_IVLEN					EVP_CTRL_GCM_SET_IVLEN
-#endif
-
-/*
- * AES GCM encrypt
- */
-static int oidc_cache_crypto_encrypt_impl(request_rec *r,
-		unsigned char *plaintext, int plaintext_len, const unsigned char *aad,
-		int aad_len, unsigned char *key, const unsigned char *iv, int iv_len,
-		unsigned char *ciphertext, const unsigned char *tag, int tag_len) {
-	EVP_CIPHER_CTX *ctx;
-
-	int len;
-
-	int ciphertext_len;
-
-	/* create and initialize the context */
-	if (!(ctx = EVP_CIPHER_CTX_new())) {
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
-		return -1;
-	}
-
-	/* initialize the encryption cipher */
-	if (!EVP_EncryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
-		return -1;
-	}
-
-	/* set IV length */
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
-		return -1;
-	}
-
-	/* initialize key and IV */
-	if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
-		return -1;
-	}
-
-	/* provide AAD data */
-	if (!EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
-				aad_len);
-		return -1;
-	}
-
-	/* provide the message to be encrypted and obtain the encrypted output */
-	if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptUpdate ciphertext");
-		return -1;
-	}
-	ciphertext_len = len;
-
-	/*
-	 * finalize the encryption; normally ciphertext bytes may be written at
-	 * this stage, but this does not occur in GCM mode
-	 */
-	if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_EncryptFinal_ex");
-		return -1;
-	}
-	ciphertext_len += len;
-
-	/* get the tag */
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_GET_TAG, tag_len,
-			(void *) tag)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
-		return -1;
-	}
-
-	/* clean up */
-	EVP_CIPHER_CTX_free(ctx);
-
-	return ciphertext_len;
-}
-
-/*
- * AES GCM decrypt
- */
-static int oidc_cache_crypto_decrypt_impl(request_rec *r,
-		unsigned char *ciphertext, int ciphertext_len, const unsigned char *aad,
-		int aad_len, const unsigned char *tag, int tag_len, unsigned char *key,
-		const unsigned char *iv, int iv_len, unsigned char *plaintext) {
-	EVP_CIPHER_CTX *ctx;
-	int len;
-	int plaintext_len;
-	int ret;
-
-	/* create and initialize the context */
-	if (!(ctx = EVP_CIPHER_CTX_new())) {
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
-		return -1;
-	}
-
-	/* initialize the decryption cipher */
-	if (!EVP_DecryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
-		return -1;
-	}
-
-	/* set IV length */
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
-		return -1;
-	}
-
-	/* initialize key and IV */
-	if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
-		return -1;
-	}
-
-	/* provide AAD data */
-	if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
-				aad_len);
-		return -1;
-	}
-
-	/* provide the message to be decrypted and obtain the plaintext output */
-	if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate ciphertext");
-		return -1;
-	}
-	plaintext_len = len;
-
-	/* set expected tag value; works in OpenSSL 1.0.1d and later */
-	if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_TAG, tag_len,
-			(void *) tag)) {
-		oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
-		return -1;
-	}
-
-	/*
-	 * finalize the decryption; a positive return value indicates success,
-	 * anything else is a failure - the plaintext is not trustworthy
-	 */
-	ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
-
-	/* clean up */
-	EVP_CIPHER_CTX_free(ctx);
-
-	if (ret > 0) {
-		/* success */
-		plaintext_len += len;
-		return plaintext_len;
-	} else {
-		/* verify failed */
-		oidc_cache_crypto_openssl_error(r, "EVP_DecryptFinal_ex");
-		return -1;
-	}
-}
-
-/*
- * static AAD value for encryption/decryption
- */
-static const unsigned char OIDC_CACHE_CRYPTO_GCM_AAD[] = { 0x4d, 0x23, 0xc3,
-		0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec, 0x78,
-		0xde };
+#define OIDC_CACHE_CRYPTO_JSON_KEY "c"
 
 /*
- * static IV value for encryption/decryption
+ * AES GCM encrypt using the crypto passphrase as symmetric key
  */
-static const unsigned char OIDC_CACHE_CRYPTO_GCM_IV[] = { 0x00, 0x01, 0x02,
-		0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
-		0x0f };
+static apr_byte_t oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext, const char *key,
+		char **result) {
+	apr_byte_t rv = FALSE;
+	json_t *json = NULL;
 
-/*
- * AES GCM encrypt using the static AAD and IV
- */
-static int oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext,
-		unsigned char *key, char **result) {
-	char *encoded = NULL, *p = NULL, *e_tag = NULL;
-	unsigned char *ciphertext = NULL;
-	int plaintext_len, ciphertext_len, encoded_len, e_tag_len;
-	unsigned char tag[OIDC_CACHE_TAG_LEN];
-
-	/* allocate space for the ciphertext */
-	plaintext_len = strlen(plaintext) + 1;
-	ciphertext = apr_pcalloc(r->pool,
-			(plaintext_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER)));
-
-	ciphertext_len = oidc_cache_crypto_encrypt_impl(r,
-			(unsigned char *) plaintext, plaintext_len,
-			OIDC_CACHE_CRYPTO_GCM_AAD, sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), key,
-			OIDC_CACHE_CRYPTO_GCM_IV, sizeof(OIDC_CACHE_CRYPTO_GCM_IV),
-			ciphertext, tag, sizeof(tag));
-
-	/* base64url encode the resulting ciphertext */
-	encoded_len = oidc_base64url_encode(r, &encoded, (const char *) ciphertext,
-			ciphertext_len, 1);
-	if (encoded_len > 0) {
-		p = encoded;
-
-		/* base64url encode the tag */
-		e_tag_len = oidc_base64url_encode(r, &e_tag, (const char *) tag,
-				OIDC_CACHE_TAG_LEN, 1);
-
-		/* now allocated space for the concatenated base64url encoded ciphertext and tag */
-		encoded = apr_pcalloc(r->pool, encoded_len + 1 + e_tag_len + 1);
-		memcpy(encoded, p, encoded_len);
-		p = encoded + encoded_len;
-		*p = OIDC_CHAR_DOT;
-		p++;
-
-		/* append the tag in the buffer */
-		memcpy(p, e_tag, e_tag_len);
-		encoded_len += e_tag_len + 1;
+	json = json_object();
+	json_object_set_new(json, OIDC_CACHE_CRYPTO_JSON_KEY, json_string(plaintext));
 
-		/* make sure the result is \0 terminated */
-		encoded[encoded_len] = '\0';
+	rv = oidc_util_jwt_create(r, (const char*) key, json, result, TRUE);
 
-		*result = encoded;
-	}
+	if (json)
+		json_decref(json);
 
-	return encoded_len;
+	return rv;
 }
 
 /*
- * AES GCM decrypt using the static AAD and IV
+ * AES GCM decrypt using the crypto passphrase as symmetric key
  */
-static int oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
-		unsigned char *key, unsigned char **plaintext) {
-
-	int len = -1;
-
-	/* grab the base64url-encoded tag after the "." */
-	char *encoded_tag = strstr(cache_value, ".");
-	if (encoded_tag == NULL) {
-		oidc_error(r,
-				"corrupted cache value: no tag separator found in encrypted value");
-		return FALSE;
-	}
+static apr_byte_t oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
+		const char *key, char **plaintext) {
 
-	/* make sure we don't modify the original string since it may be just a pointer into the cache (shm) */
-	cache_value = apr_pstrmemdup(r->pool, cache_value,
-			strlen(cache_value) - strlen(encoded_tag));
-	encoded_tag++;
-
-	/* base64url decode the ciphertext */
-	char *d_bytes = NULL;
-	int d_len = oidc_base64url_decode(r->pool, &d_bytes, cache_value);
-
-	/* base64url decode the tag */
-	char *t_bytes = NULL;
-	int t_len = oidc_base64url_decode(r->pool, &t_bytes, encoded_tag);
-
-	/* see if we're still good to go */
-	if ((d_len > 0) && (t_len > 0)) {
-
-		/* allocated space for the plaintext */
-		*plaintext = apr_pcalloc(r->pool,
-				(d_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER) - 1));
-
-		/* decrypt the ciphertext providing the tag value */
-
-		len = oidc_cache_crypto_decrypt_impl(r, (unsigned char *) d_bytes,
-				d_len, OIDC_CACHE_CRYPTO_GCM_AAD,
-				sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), (unsigned char *) t_bytes,
-				t_len, key, OIDC_CACHE_CRYPTO_GCM_IV,
-				sizeof(OIDC_CACHE_CRYPTO_GCM_IV), *plaintext);
-
-		/* check the result and make sure it is \0 terminated */
-		if (len > -1) {
-			(*plaintext)[len] = '\0';
-		} else {
-			*plaintext = NULL;
-		}
+	apr_byte_t rv = FALSE;
+	json_t *json = NULL;
 
-	}
+	rv = oidc_util_jwt_verify(r, (const char*) key, cache_value, &json, TRUE);
+	if (rv == FALSE)
+		goto end;
 
-	return len;
-}
+	rv = oidc_json_object_get_string(r->pool, json, OIDC_CACHE_CRYPTO_JSON_KEY, plaintext, NULL);
 
-/*
- * hash the crypto passhphrase so it has enough key length for AES GCM 256
- */
-static unsigned char *oidc_cache_hash_passphrase(request_rec *r,
-		const char *passphrase) {
+end:
 
-	unsigned char *key = NULL;
-	unsigned int key_len = 0;
-	oidc_jose_error_t err;
-
-	if (oidc_jose_hash_bytes(r->pool, OIDC_JOSE_ALG_SHA256,
-			(const unsigned char *) passphrase, strlen(passphrase), &key,
-			&key_len, &err) == FALSE) {
-		oidc_error(r, "oidc_jose_hash_bytes returned an error: %s", err.text);
-		return NULL;
-	}
+	if (json)
+		json_decref(json);
 
-	return key;
+	return rv;
 }
 
 /*
  * hash a cache key and a crypto passphrase so the result is suitable as an randomized cache key
  */
-static char *oidc_cache_get_hashed_key(request_rec *r, const char *passphrase,
-		const char *key) {
+static char* oidc_cache_get_hashed_key(request_rec *r, const char *passphrase, const char *key) {
 	char *input = apr_psprintf(r->pool, "%s:%s", passphrase, key);
 	char *output = NULL;
-	if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256,
-			input, &output) == FALSE) {
-		oidc_error(r,
-				"oidc_util_hash_string_and_base64url_encode returned an error");
+	if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256, input, &output)
+			== FALSE) {
+		oidc_error(r, "oidc_util_hash_string_and_base64url_encode returned an error");
 		return NULL;
 	}
 	return output;
@@ -601,9 +338,7 @@
 		goto out;
 	}
 
-	rc = (oidc_cache_crypto_decrypt(r, cache_value,
-			oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
-			(unsigned char **) value) > 0);
+	rc = oidc_cache_crypto_decrypt(r, cache_value, cfg->crypto_passphrase, value);
 
 out:
 	/* log the result */
@@ -647,9 +382,7 @@
 			goto out;
 
 		if (value != NULL) {
-			if (oidc_cache_crypto_encrypt(r, value,
-					oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
-					&encoded) <= 0)
+			if (oidc_cache_crypto_encrypt(r, value, cfg->crypto_passphrase, &encoded) == FALSE)
 				goto out;
 			value = encoded;
 		}
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/cache/file.c libapache2-mod-auth-openidc-2.4.9/src/cache/file.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/cache/file.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/cache/file.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/cache/memcache.c libapache2-mod-auth-openidc-2.4.9/src/cache/memcache.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/cache/memcache.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/cache/memcache.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/cache/redis.c libapache2-mod-auth-openidc-2.4.9/src/cache/redis.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/cache/redis.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/cache/redis.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -71,9 +63,15 @@
 	char *host_str;
 	apr_port_t port;
 	char *passwd;
+	int database;
+	struct timeval connect_timeout;
+	struct timeval timeout;
 	redisContext *ctx;
 } oidc_cache_cfg_redis_t;
 
+#define REDIS_CONNECT_TIMEOUT_DEFAULT 5
+#define REDIS_TIMEOUT_DEFAULT 5
+
 /* create the cache context */
 static void *oidc_cache_redis_cfg_create(apr_pool_t *pool) {
 	oidc_cache_cfg_redis_t *context = apr_pcalloc(pool,
@@ -81,6 +79,11 @@
 	context->mutex = oidc_cache_mutex_create(pool);
 	context->host_str = NULL;
 	context->passwd = NULL;
+	context->database = -1;
+	context->connect_timeout.tv_sec = REDIS_CONNECT_TIMEOUT_DEFAULT;
+	context->connect_timeout.tv_usec = 0;
+	context->timeout.tv_sec = REDIS_TIMEOUT_DEFAULT;
+	context->timeout.tv_usec = 0;
 	context->ctx = NULL;
 	return context;
 }
@@ -131,6 +134,15 @@
 				cfg->cache_redis_password);
 	}
 
+	if (cfg->cache_redis_database != -1)
+		context->database = cfg->cache_redis_database;
+
+	if (cfg->cache_redis_connect_timeout != -1)
+		context->connect_timeout.tv_sec = cfg->cache_redis_connect_timeout;
+
+	if (cfg->cache_redis_timeout != -1)
+		context->timeout.tv_sec = cfg->cache_redis_timeout;
+
 	if (oidc_cache_mutex_post_config(s, context->mutex, "redis") == FALSE)
 		return HTTP_INTERNAL_SERVER_ERROR;
 
@@ -169,15 +181,28 @@
 }
 
 /*
+ * free and nullify a reply object
+ */
+static void oidc_cache_redis_reply_free(redisReply **reply) {
+	if (*reply != NULL) {
+		freeReplyObject(*reply);
+		*reply = NULL;
+	}
+}
+
+/*
  * connect to Redis server
  */
 static apr_status_t oidc_cache_redis_connect(request_rec *r,
 		oidc_cache_cfg_redis_t *context) {
 
+	redisReply *reply = NULL;
+
 	if (context->ctx == NULL) {
 
 		/* no connection, connect to the configured Redis server */
-		context->ctx = redisConnect(context->host_str, context->port);
+		oidc_debug(r, "calling redisConnectWithTimeout");
+		context->ctx = redisConnectWithTimeout(context->host_str, context->port, context->connect_timeout);
 
 		/* check for errors */
 		if ((context->ctx == NULL) || (context->ctx->err != 0)) {
@@ -189,32 +214,63 @@
 			/* log the connection */
 			oidc_debug(r, "successfully connected to Redis server (%s:%d)",
 					context->host_str, context->port);
+
+			/* see if we need to authenticate to the Redis server */
+			if (context->passwd != NULL) {
+				reply = redisCommand(context->ctx, "AUTH %s", context->passwd);
+				if ((reply == NULL) || (reply->type == REDIS_REPLY_ERROR))
+					oidc_error(r,
+							"Redis AUTH command (%s:%d) failed: '%s' [%s]",
+							context->host_str, context->port,
+							context->ctx->errstr, reply ? reply->str : "<n/a>");
+				else
+					oidc_debug(r,
+							"successfully authenticated to the Redis server: %s",
+							reply ? reply->str : "<n/a>");
+
+				/* free the auth answer */
+				oidc_cache_redis_reply_free(&reply);
+			}
+
+			/* see if we need to set the database */
+			if (context->database != -1) {
+				reply = redisCommand(context->ctx, "SELECT %d",
+						context->database);
+				if ((reply == NULL) || (reply->type == REDIS_REPLY_ERROR))
+					oidc_error(r,
+							"Redis SELECT command (%s:%d) failed: '%s' [%s]",
+							context->host_str, context->port,
+							context->ctx->errstr, reply ? reply->str : "<n/a>");
+				else
+					oidc_debug(r,
+							"successfully selected database %d on the Redis server: %s",
+							context->database, reply ? reply->str : "<n/a>");
+
+				/* free the database answer */
+				oidc_cache_redis_reply_free(&reply);
+			}
+
+			if (redisSetTimeout(context->ctx, context->timeout) != REDIS_OK)
+				oidc_error(r, "redisSetTimeout failed: %s", context->ctx->errstr);
+
 		}
 	}
 
 	return (context->ctx != NULL) ? APR_SUCCESS : APR_EGENERAL;
 }
 
-/*
- * free and nullify a reply object
- */
-static void oidc_cache_redis_reply_free(redisReply **reply) {
-	if (*reply != NULL) {
-		freeReplyObject(*reply);
-		*reply = NULL;
-	}
-}
-
 #define OIDC_REDIS_MAX_TRIES 2
 
 /*
  * execute Redis command and deal with return value
  */
 static redisReply* oidc_cache_redis_command(request_rec *r,
-		oidc_cache_cfg_redis_t *context, const char *command) {
+		oidc_cache_cfg_redis_t *context, const char *format, ...) {
 
 	redisReply *reply = NULL;
 	int i = 0;
+	va_list ap;
+	va_start(ap, format);
 
 	/* try to execute a command at max 2 times while reconnecting */
 	for (i = 0; i < OIDC_REDIS_MAX_TRIES; i++) {
@@ -223,21 +279,8 @@
 		if (oidc_cache_redis_connect(r, context) != APR_SUCCESS)
 			break;
 
-		/* see if we need to authenticate to the Redis server */
-		if (context->passwd != NULL) {
-			reply = redisCommand(context->ctx, "AUTH %s", context->passwd);
-			if ((reply == NULL) || (reply->type == REDIS_REPLY_ERROR))
-				oidc_error(r,
-						"Redis AUTH command (attempt=%d to %s:%d) failed: '%s' [%s]",
-						i, context->host_str, context->port,
-						context->ctx->errstr, reply ? reply->str : "<n/a>");
-
-			/* free the auth answer */
-			oidc_cache_redis_reply_free(&reply);
-		}
-
 		/* execute the actual command */
-		reply = redisCommand(context->ctx, command);
+		reply = redisvCommand(context->ctx, format, ap);
 
 		/* check for errors, need to return error replies for cache miss case REDIS_REPLY_NIL */
 		if ((reply != NULL) && (reply->type != REDIS_REPLY_ERROR))
@@ -257,6 +300,8 @@
 		oidc_cache_redis_free(context);
 	}
 
+	va_end(ap);
+
 	return reply;
 }
 
@@ -277,9 +322,8 @@
 		return FALSE;
 
 	/* get */
-	reply = oidc_cache_redis_command(r, context,
-			apr_psprintf(r->pool, "GET %s",
-					oidc_cache_redis_get_key(r->pool, section, key)));
+	reply =
+			oidc_cache_redis_command(r, context, "GET %s", oidc_cache_redis_get_key(r->pool, section, key));
 
 	if (reply == NULL)
 		goto end;
@@ -291,10 +335,17 @@
 		goto end;
 	}
 
+	if (reply->type != REDIS_REPLY_STRING) {
+		oidc_error(r, "redisCommand reply type is not string: %d", reply->type);
+		goto end;
+	}
+
 	/* do a sanity check on the returned value */
-	if (reply->len != strlen(reply->str)) {
-		oidc_error(r, "redisCommand reply->len != strlen(reply->str): '%s'",
-				reply->str);
+	if ((reply->str == NULL)
+			|| (reply->len != strlen(reply->str))) {
+		oidc_error(r,
+				"redisCommand reply->len (%d) != strlen(reply->str): '%s'",
+				(int )reply->len, reply->str);
 		goto end;
 	}
 
@@ -336,9 +387,8 @@
 	if (value == NULL) {
 
 		/* delete it */
-		reply = oidc_cache_redis_command(r, context,
-				apr_psprintf(r->pool, "DEL %s",
-						oidc_cache_redis_get_key(r->pool, section, key)));
+		reply =
+				oidc_cache_redis_command(r, context, "DEL %s", oidc_cache_redis_get_key(r->pool, section, key));
 
 	} else {
 
@@ -346,10 +396,8 @@
 		timeout = apr_time_sec(expiry - apr_time_now());
 
 		/* store it */
-		reply = oidc_cache_redis_command(r, context,
-				apr_psprintf(r->pool, "SETEX %s %d %s",
-						oidc_cache_redis_get_key(r->pool, section, key),
-						timeout, value));
+		reply =
+				oidc_cache_redis_command(r, context, "SETEX %s %d %s", oidc_cache_redis_get_key(r->pool, section, key), timeout, value);
 
 	}
 
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/cache/shm.c libapache2-mod-auth-openidc-2.4.9/src/cache/shm.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/cache/shm.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/cache/shm.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/config.c libapache2-mod-auth-openidc-2.4.9/src/config.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/config.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/config.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -152,6 +144,8 @@
 #define OIDC_DEFAULT_PASS_APP_INFO_IN_HEADERS 1
 /* default for passing app info in environment variables */
 #define OIDC_DEFAULT_PASS_APP_INFO_IN_ENVVARS 1
+/* default for passing app info in base64 encoded format */
+#define OIDC_DEFAULT_PASS_APP_INFO_BASE64URL 0
 /* default value for the token introspection interval (0 = disabled, no expiry of claims) */
 #define OIDC_DEFAULT_TOKEN_INTROSPECTION_INTERVAL 0
 /* default action to take on an incoming unauthenticated request */
@@ -216,6 +210,7 @@
 #define OIDCClientSecret                       "OIDCClientSecret"
 #define OIDCClientTokenEndpointCert            "OIDCClientTokenEndpointCert"
 #define OIDCClientTokenEndpointKey             "OIDCClientTokenEndpointKey"
+#define OIDCClientTokenEndpointKeyPassword     "OIDCClientTokenEndpointKeyPassword"
 #define OIDCDefaultLoggedOutURL                "OIDCDefaultLoggedOutURL"
 #define OIDCCookieHTTPOnly                     "OIDCCookieHTTPOnly"
 #define OIDCCookieSameSite                     "OIDCCookieSameSite"
@@ -253,6 +248,9 @@
 #define OIDCCacheDir                           "OIDCCacheDir"
 #define OIDCCacheFileCleanInterval             "OIDCCacheFileCleanInterval"
 #define OIDCRedisCachePassword                 "OIDCRedisCachePassword"
+#define OIDCRedisCacheDatabase                 "OIDCRedisCacheDatabase"
+#define OIDCRedisCacheConnectTimeout           "OIDCRedisCacheConnectTimeout"
+#define OIDCRedisCacheTimeout                  "OIDCRedisCacheTimeout"
 #define OIDCHTMLErrorTemplate                  "OIDCHTMLErrorTemplate"
 #define OIDCDiscoverURL                        "OIDCDiscoverURL"
 #define OIDCPassCookies                        "OIDCPassCookies"
@@ -277,6 +275,7 @@
 #define OIDCStateInputHeaders                  "OIDCStateInputHeaders"
 #define OIDCRedirectURLsAllowed                "OIDCRedirectURLsAllowed"
 #define OIDCStateCookiePrefix                  "OIDCStateCookiePrefix"
+#define OIDCCABundlePath                       "OIDCCABundlePath"
 
 extern module AP_MODULE_DECLARE_DATA auth_openidc_module;
 
@@ -289,12 +288,15 @@
 	char *cookie;
 	char *authn_header;
 	int unauth_action;
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
 	ap_expr_info_t *unauth_expression;
+#endif
 	int unautz_action;
 	apr_array_header_t *pass_cookies;
 	apr_array_header_t *strip_cookies;
 	int pass_info_in_headers;
 	int pass_info_in_env_vars;
+	int pass_info_base64url;
 	int oauth_accept_token_in;
 	apr_hash_t *oauth_accept_token_options;
 	int oauth_token_introspect_interval;
@@ -312,36 +314,36 @@
 /*
  * set a boolean value in the server config
  */
-static const char *oidc_set_flag_slot(cmd_parms *cmd, void *struct_ptr, int arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+static const char* oidc_set_flag_slot(cmd_parms *cmd, void *struct_ptr, int arg) {
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	return ap_set_flag_slot(cmd, cfg, arg);
 }
 
 /*
  * set a string value in the server config
  */
-static const char *oidc_set_string_slot(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_string_slot(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	return ap_set_string_slot(cmd, cfg, arg);
 }
 
 /*
  * set an integer value in the server config
  */
-static const char *oidc_set_int_slot(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_int_slot(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	return ap_set_int_slot(cmd, cfg, arg);
 }
 
 /*
  * set a URL value in a config record
  */
-static const char *oidc_set_url_slot_type(cmd_parms *cmd, void *ptr,
+static const char* oidc_set_url_slot_type(cmd_parms *cmd, void *ptr,
 		const char *arg, const char *type) {
 	const char *rv =
 			type != NULL ?
@@ -355,29 +357,27 @@
 /*
  * set a HTTPS value in the server config
  */
-static const char *oidc_set_https_slot(cmd_parms *cmd, void *ptr,
+static const char* oidc_set_https_slot(cmd_parms *cmd, void *ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	return oidc_set_url_slot_type(cmd, cfg, arg, "https");
 }
 
 /*
  * set a HTTPS/HTTP value in the server config
  */
-static const char *oidc_set_url_slot(cmd_parms *cmd, void *ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+static const char* oidc_set_url_slot(cmd_parms *cmd, void *ptr, const char *arg) {
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	return oidc_set_url_slot_type(cmd, cfg, arg, NULL);
 }
 
 /*
- * set a relative or absolute URL value in the server config
+ * set a relative or absolute URL value in a config rec
  */
-static const char *oidc_set_relative_or_absolute_url_slot(cmd_parms *cmd,
-		void *ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+static const char* oidc_set_relative_or_absolute_url_slot_dir_cfg(
+		cmd_parms *cmd, void *ptr, const char *arg) {
 	if (arg[0] == OIDC_CHAR_FORWARD_SLASH) {
 		// relative uri
 		apr_uri_t uri;
@@ -386,29 +386,31 @@
 					"cannot parse '%s' as relative URI", arg);
 			return OIDC_CONFIG_DIR_RV(cmd, rv);
 		} else {
-			return ap_set_string_slot(cmd, cfg, arg);
+			return ap_set_string_slot(cmd, ptr, arg);
 		}
 	} else {
 		// absolute uri
-		return oidc_set_url_slot_type(cmd, cfg, arg, NULL);
+		return oidc_set_url_slot_type(cmd, ptr, arg, NULL);
 	}
 }
 
 /*
- * set a HTTPS/HTTP value in the directory config
+ * set a relative or absolute URL value in the server config
  */
-static const char *oidc_set_url_slot_dir_cfg(cmd_parms *cmd, void *ptr,
-		const char *arg) {
-	return oidc_set_url_slot_type(cmd, ptr, arg, NULL);
+static const char* oidc_set_relative_or_absolute_url_slot(cmd_parms *cmd,
+		void *ptr, const char *arg) {
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
+	return oidc_set_relative_or_absolute_url_slot_dir_cfg(cmd, cfg, arg);
 }
 
 /*
  * set a directory value in the server config
  */
 // TODO: it's not really a syntax error... (could be fixed at runtime but then we'd have to restart the server)
-static const char *oidc_set_dir_slot(cmd_parms *cmd, void *ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+static const char* oidc_set_dir_slot(cmd_parms *cmd, void *ptr, const char *arg) {
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_valid_dir(cmd->pool, arg);
 	if (rv == NULL)
 		rv = ap_set_string_slot(cmd, cfg, arg);
@@ -416,12 +418,63 @@
 }
 
 /*
+ * set a path value in the server config, converting to absolute if necessary
+ */
+static const char* oidc_set_path_slot(cmd_parms *cmd, void *ptr,
+		const char *arg) {
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
+	const char *full_path = oidc_util_get_full_path(cmd->pool, arg);
+	return ap_set_string_slot(cmd, cfg, full_path);
+}
+
+/*
+ * set a string value in the server config with exec support
+ */
+static const char* oidc_set_passphrase_slot(cmd_parms *cmd, void *struct_ptr,
+		const char *arg) {
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
+	const char *passphrase = NULL;
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
+	int arglen = strlen(arg);
+	char **argv = NULL;
+	char *result = NULL;
+	/* Based on code from mod_session_crypto. */
+	if (arglen > 5 && strncmp(arg, "exec:", 5) == 0) {
+		if (apr_tokenize_to_argv(arg + 5, &argv, cmd->temp_pool) != APR_SUCCESS) {
+			return apr_pstrcat(cmd->pool,
+					"Unable to parse exec arguments from ", arg + 5, NULL);
+		}
+		argv[0] = ap_server_root_relative(cmd->temp_pool, argv[0]);
+		if (!argv[0]) {
+			return apr_pstrcat(cmd->pool, "Invalid ", cmd->cmd->name,
+					" exec location:", arg + 5, NULL);
+		}
+		result = ap_get_exec_line(cmd->pool, argv[0],
+				(const char* const*) argv);
+		if (!result) {
+			return apr_pstrcat(cmd->pool,
+					"Unable to get passphrase from exec of ", arg + 5, NULL);
+		}
+		passphrase = result;
+	} else {
+		passphrase = arg;
+	}
+#else
+	passphrase = arg;
+#endif
+
+	return ap_set_string_slot(cmd, cfg, passphrase);
+}
+
+/*
  * set the cookie domain in the server config and check it syntactically
  */
-static const char *oidc_set_cookie_domain(cmd_parms *cmd, void *ptr,
+static const char* oidc_set_cookie_domain(cmd_parms *cmd, void *ptr,
 		const char *value) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_valid_cookie_domain(cmd->pool, value);
 	if (rv == NULL)
 		cfg->cookie_domain = apr_pstrdup(cmd->pool, value);
@@ -431,10 +484,10 @@
 /*
  * set the session storage type
  */
-static const char *oidc_set_session_type(cmd_parms *cmd, void *ptr,
+static const char* oidc_set_session_type(cmd_parms *cmd, void *ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_session_type(cmd->pool, arg, &cfg->session_type,
 			&cfg->persistent_session_cookie);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -443,10 +496,10 @@
 /*
  * set the maximum size of a shared memory cache entry and enforces a minimum
  */
-static const char *oidc_set_cache_shm_entry_size_max(cmd_parms *cmd, void *ptr,
+static const char* oidc_set_cache_shm_entry_size_max(cmd_parms *cmd, void *ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_cache_shm_entry_size_max(cmd->pool, arg,
 			&cfg->cache_shm_entry_size_max);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -455,10 +508,10 @@
 /*
  * set the cache type
  */
-static const char *oidc_set_cache_type(cmd_parms *cmd, void *ptr,
+static const char* oidc_set_cache_type(cmd_parms *cmd, void *ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_cache_type(cmd->pool, arg, &cfg->cache);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
@@ -466,10 +519,10 @@
 /*
  * set SSL validation slot
  */
-static const char *oidc_set_ssl_validate_slot(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_ssl_validate_slot(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	int b = 0;
 	const char *rv = oidc_parse_boolean(cmd->pool, arg, &b);
 	if (rv == NULL)
@@ -480,10 +533,10 @@
 /*
  * set validate issuer slot
  */
-static const char *oidc_set_validate_issuer_slot(cmd_parms *cmd,
+static const char* oidc_set_validate_issuer_slot(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	int b = 0;
 	const char *rv = oidc_parse_boolean(cmd->pool, arg, &b);
 	if (rv == NULL)
@@ -503,10 +556,10 @@
 /*
  * set an authentication method for an endpoint and check it is one that we support
  */
-static const char *oidc_set_endpoint_auth_slot(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_endpoint_auth_slot(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_cfg_get_valid_endpoint_auth_function(cfg)(cmd->pool,
 			arg);
 	if (rv == NULL)
@@ -517,10 +570,10 @@
 /*
  * set the response type used
  */
-static const char *oidc_set_response_type(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_response_type(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 
 	const char *rv = oidc_valid_response_type(cmd->pool, arg);
 	if (rv == NULL)
@@ -528,7 +581,7 @@
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
 
-const char *oidc_parse_pkce_type(apr_pool_t *pool, const char *arg,
+const char* oidc_parse_pkce_type(apr_pool_t *pool, const char *arg,
 		oidc_proto_pkce_t **type) {
 	const char *rv = oidc_valid_pkce_method(pool, arg);
 	if (rv != NULL)
@@ -548,10 +601,10 @@
 /*
  * define the PCKE method to use
  */
-static const char *oidc_set_pkce_method(cmd_parms *cmd, void *ptr,
+static const char* oidc_set_pkce_method(cmd_parms *cmd, void *ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_pkce_type(cmd->pool, arg, &cfg->provider.pkce);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
@@ -559,10 +612,10 @@
 /*
  * set the response mode used
  */
-static const char *oidc_set_response_mode(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_response_mode(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 
 	const char *rv = oidc_valid_response_mode(cmd->pool, arg);
 	if (rv == NULL)
@@ -573,10 +626,10 @@
 /*
  * set the signing algorithm to be used by the OP (id_token/user_info)
  */
-static const char *oidc_set_signed_response_alg(cmd_parms *cmd,
+static const char* oidc_set_signed_response_alg(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_valid_signed_response_alg(cmd->pool, arg);
 	if (rv == NULL)
 		rv = ap_set_string_slot(cmd, cfg, arg);
@@ -586,10 +639,10 @@
 /*
  * set the Content Encryption Key encryption algorithm to be used by the OP (id_token/user_info)
  */
-static const char *oidc_set_encrypted_response_alg(cmd_parms *cmd,
+static const char* oidc_set_encrypted_response_alg(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_valid_encrypted_response_alg(cmd->pool, arg);
 	if (rv == NULL)
 		rv = ap_set_string_slot(cmd, cfg, arg);
@@ -599,10 +652,10 @@
 /*
  * set the content encryption algorithm to be used by the OP (id_token/user_info)
  */
-static const char *oidc_set_encrypted_response_enc(cmd_parms *cmd,
+static const char* oidc_set_encrypted_response_enc(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_valid_encrypted_response_enc(cmd->pool, arg);
 	if (rv == NULL)
 		rv = ap_set_string_slot(cmd, cfg, arg);
@@ -612,10 +665,10 @@
 /*
  * set the userinfo endpoint token presentation method
  */
-static const char *oidc_set_userinfo_token_method(cmd_parms *cmd,
+static const char* oidc_set_userinfo_token_method(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_userinfo_token_method(cmd->pool, arg,
 			&cfg->provider.userinfo_token_method);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -624,10 +677,10 @@
 /*
  * set the session inactivity timeout
  */
-static const char *oidc_set_session_inactivity_timeout(cmd_parms *cmd,
+static const char* oidc_set_session_inactivity_timeout(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_session_inactivity_timeout(cmd->pool, arg,
 			&cfg->session_inactivity_timeout);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -636,28 +689,20 @@
 /*
  * set the maximum session duration; 0 means take it from the ID token expiry time
  */
-static const char *oidc_set_session_max_duration(cmd_parms *cmd,
+static const char* oidc_set_session_max_duration(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_session_max_duration(cmd->pool, arg,
 			&cfg->provider.session_max_duration);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
 
-typedef struct oidc_cleanup_keys_ctx {
-	apr_pool_t *pool;
-	apr_hash_t *keys;
-} oidc_cleanup_keys_ctx;
-
 static apr_status_t oidc_cleanup_keys(void *data) {
-	oidc_cleanup_keys_ctx *ctx = (oidc_cleanup_keys_ctx *) data;
-	oidc_jwk_t *jwk = NULL;
-	apr_hash_index_t *hi;
-	for (hi = apr_hash_first(ctx->pool, ctx->keys); hi;
-			hi = apr_hash_next(hi)) {
-		apr_hash_this(hi, NULL, NULL, (void **) &jwk);
-		oidc_jwk_destroy(jwk);
+	apr_array_header_t *keys_list = (apr_array_header_t*) data;
+	oidc_jwk_t **jwk = NULL;
+	while ((jwk = apr_array_pop(keys_list))) {
+		oidc_jwk_destroy(*jwk);
 	}
 	return APR_SUCCESS;
 }
@@ -665,16 +710,17 @@
 /*
  * add a public key from an X.509 file to our list of JWKs with public keys
  */
-static const char *oidc_set_public_key_files(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_public_key_files(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
 	oidc_jwk_t *jwk = NULL;
 	oidc_jose_error_t err;
 
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 
 	int offset = (int) (long) cmd->info;
-	apr_hash_t **public_keys = (apr_hash_t **) ((char *) cfg + offset);
+	apr_array_header_t **public_keys = (apr_array_header_t**) ((char*) cfg
+			+ offset);
 
 	char *kid = NULL, *fname = NULL;
 	int fname_len;
@@ -693,15 +739,12 @@
 	}
 
 	if (*public_keys == NULL) {
-		*public_keys = apr_hash_make(cmd->pool);
-		oidc_cleanup_keys_ctx *ctx = apr_pcalloc(cmd->pool,
-				sizeof(oidc_cleanup_keys_ctx));
-		ctx->pool = cmd->pool;
-		ctx->keys = *public_keys;
-		apr_pool_cleanup_register(cmd->pool, ctx, oidc_cleanup_keys,
+		*public_keys = apr_array_make(cmd->pool, 4, sizeof(const oidc_jwk_t*));
+		apr_pool_cleanup_register(cmd->pool, *public_keys, oidc_cleanup_keys,
 				oidc_cleanup_keys);
 	}
-	apr_hash_set(*public_keys, jwk->kid, APR_HASH_KEY_STRING, jwk);
+
+	*(const oidc_jwk_t**) apr_array_push(*public_keys) = jwk;
 
 	return NULL;
 }
@@ -709,15 +752,15 @@
 /*
  * add a shared key to a list of JWKs with shared keys
  */
-static const char *oidc_set_shared_keys(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_shared_keys(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
 	oidc_jose_error_t err;
 	oidc_jwk_t *jwk = NULL;
 
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	int offset = (int) (long) cmd->info;
-	apr_hash_t **shared_keys = (apr_hash_t **) ((char *) cfg + offset);
+	apr_hash_t **shared_keys = (apr_hash_t**) ((char*) cfg + offset);
 
 	char *kid = NULL, *secret = NULL;
 	int key_len = 0;
@@ -727,7 +770,7 @@
 		return rv;
 
 	jwk = oidc_jwk_create_symmetric_key(cmd->pool, kid,
-			(const unsigned char *) secret, key_len, TRUE, &err);
+			(const unsigned char*) secret, key_len, TRUE, &err);
 	if (jwk == NULL) {
 		return apr_psprintf(cmd->pool,
 				"oidc_jwk_create_symmetric_key failed for (kid=%s) \"%s\": %s",
@@ -745,10 +788,10 @@
 /*
  * add a private key from an RSA private key file to our list of JWKs with private keys
  */
-static const char *oidc_set_private_key_files_enc(cmd_parms *cmd, void *dummy,
+static const char* oidc_set_private_key_files_enc(cmd_parms *cmd, void *dummy,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	oidc_jwk_t *jwk = NULL;
 	oidc_jose_error_t err;
 
@@ -769,16 +812,13 @@
 	}
 
 	if (cfg->private_keys == NULL) {
-		cfg->private_keys = apr_hash_make(cmd->pool);
-		oidc_cleanup_keys_ctx *ctx = apr_pcalloc(cmd->pool,
-				sizeof(oidc_cleanup_keys_ctx));
-		ctx->pool = cmd->pool;
-		ctx->keys = cfg->private_keys;
-		apr_pool_cleanup_register(cmd->pool, ctx, oidc_cleanup_keys,
-				oidc_cleanup_keys);
+		cfg->private_keys = apr_array_make(cmd->pool, 4,
+				sizeof(const oidc_jwk_t*));
+		apr_pool_cleanup_register(cmd->pool, cfg->private_keys,
+				oidc_cleanup_keys, oidc_cleanup_keys);
 	}
 
-	apr_hash_set(cfg->private_keys, jwk->kid, APR_HASH_KEY_STRING, jwk);
+	*(const oidc_jwk_t**) apr_array_push(cfg->private_keys) = jwk;
 
 	return NULL;
 }
@@ -786,10 +826,10 @@
 /*
  * define how to pass the id_token/claims in HTTP headers
  */
-static const char * oidc_set_pass_idtoken_as(cmd_parms *cmd, void *dummy,
+static const char* oidc_set_pass_idtoken_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);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_pass_idtoken_as(cmd->pool, v1, v2, v3,
 			&cfg->pass_idtoken_as);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -798,10 +838,10 @@
 /*
  * define how to pass the userinfo/claims in HTTP headers
  */
-static const char * oidc_set_pass_userinfo_as(cmd_parms *cmd, void *dummy,
+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);
+	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);
@@ -810,9 +850,9 @@
 /*
  * define which method of pass an OAuth Bearer token is accepted
  */
-static const char * oidc_set_accept_oauth_token_in(cmd_parms *cmd, void *m,
+static const char* oidc_set_accept_oauth_token_in(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg *) m;
+	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg*) m;
 	const char *rv = oidc_parse_accept_oauth_token_in(cmd->pool, arg,
 			&dir_cfg->oauth_accept_token_in,
 			dir_cfg->oauth_accept_token_options);
@@ -822,11 +862,11 @@
 /*
  * set the syntax of the token expiry claim in the introspection response
  */
-static const char * oidc_set_token_expiry_claim(cmd_parms *cmd, void *dummy,
+static const char* oidc_set_token_expiry_claim(cmd_parms *cmd, void *dummy,
 		const char *claim_name, const char *claim_format,
 		const char *claim_required) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 
 	const char *rv = NULL;
 
@@ -851,14 +891,14 @@
 /*
  * specify cookies names to pass/strip
  */
-static const char * oidc_set_cookie_names(cmd_parms *cmd, void *m,
+static const char* oidc_set_cookie_names(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg *) m;
+	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg*) m;
 	int offset = (int) (long) cmd->info;
-	apr_array_header_t **cookie_names =
-			(apr_array_header_t **) ((char *) dir_cfg + offset);
+	apr_array_header_t **cookie_names = (apr_array_header_t**) ((char*) dir_cfg
+			+ offset);
 	if (*cookie_names == NULL)
-		*cookie_names = apr_array_make(cmd->pool, 3, sizeof(const char *));
+		*cookie_names = apr_array_make(cmd->pool, 3, sizeof(const char*));
 	*(const char**) apr_array_push((*cookie_names)) = arg;
 	return NULL;
 }
@@ -866,10 +906,10 @@
 /*
  * set the HTTP method to use in an OAuth 2.0 token introspection/validation call
  */
-static const char * oidc_set_introspection_method(cmd_parms *cmd, void *m,
+static const char* oidc_set_introspection_method(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_valid_introspection_method(cmd->pool, arg);
 	if (rv == NULL)
 		rv = ap_set_string_slot(cmd, cfg, arg);
@@ -879,9 +919,9 @@
 /*
  * set POST preservation behavior
  */
-static const char *oidc_set_preserve_post(cmd_parms *cmd, void *m,
+static const char* oidc_set_preserve_post(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg *) m;
+	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg*) m;
 	int b = 0;
 	const char *rv = oidc_parse_boolean(cmd->pool, arg, &b);
 	if (rv == NULL)
@@ -892,14 +932,14 @@
 /*
  * set the remote user name claims, optionally plus the regular expression applied to it
  */
-static const char *oidc_set_remote_user_claim(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_remote_user_claim(cmd_parms *cmd, void *struct_ptr,
 		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);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 
 	int offset = (int) (long) cmd->info;
 	oidc_remote_user_claim_t *remote_user_claim =
-			(oidc_remote_user_claim_t *) ((char *) cfg + offset);
+			(oidc_remote_user_claim_t*) ((char*) cfg + offset);
 
 	remote_user_claim->claim_name = v1;
 	if (v2)
@@ -913,23 +953,34 @@
 /*
  * define how to pass claims information to the application: in headers and/or environment variables
  */
-static const char * oidc_set_pass_claims_as(cmd_parms *cmd, void *m,
-		const char *arg) {
-	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg *) m;
-	const char *rv = oidc_parse_set_claims_as(cmd->pool, arg,
+static const char* oidc_set_pass_claims_as(cmd_parms *cmd, void *m,
+		const char *arg1, const char *arg2) {
+	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg*) m;
+	const char *rv = oidc_parse_set_claims_as(cmd->pool, arg1,
 			&dir_cfg->pass_info_in_headers, &dir_cfg->pass_info_in_env_vars);
+	if (rv == NULL) {
+		if (arg2 != NULL) {
+			if (apr_strnatcmp(arg2, "base64url") == 0) {
+				dir_cfg->pass_info_base64url = 1;
+			} else {
+				rv = apr_pstrcat(cmd->temp_pool, "unknown encoding option \"",
+						arg2, "\", only \"base64url\" is supported", NULL);
+			}
+		}
+	}
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
 
 /*
  * define how to act on unauthenticated requests
  */
-static const char * oidc_set_unauth_action(cmd_parms *cmd, void *m,
+static const char* oidc_set_unauth_action(cmd_parms *cmd, void *m,
 		const char *arg1, const char *arg2) {
-	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg *) m;
-	const char *expr_err = NULL;
+	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg*) m;
 	const char *rv = oidc_parse_unauth_action(cmd->pool, arg1,
 			&dir_cfg->unauth_action);
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
+	const char *expr_err = NULL;
 	if ((rv == NULL) && (arg2 != NULL)) {
 		dir_cfg->unauth_expression = ap_expr_parse_cmd(cmd, arg2,
 				AP_EXPR_FLAG_DONT_VARY & AP_EXPR_FLAG_RESTRICTED, &expr_err,
@@ -939,15 +990,16 @@
 					expr_err, NULL);
 		}
 	}
+#endif
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
 
 /*
  * define how to act on unauthorized requests
  */
-static const char * oidc_set_unautz_action(cmd_parms *cmd, void *m,
+static const char* oidc_set_unautz_action(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg *) m;
+	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg*) m;
 	const char *rv = oidc_parse_unautz_action(cmd->pool, arg,
 			&dir_cfg->unautz_action);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -956,10 +1008,10 @@
 /*
  * set the JWKS refresh interval
  */
-static const char *oidc_set_jwks_refresh_interval(cmd_parms *cmd,
+static const char* oidc_set_jwks_refresh_interval(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_jwks_refresh_interval(cmd->pool, arg,
 			&cfg->provider.jwks_refresh_interval);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -968,10 +1020,10 @@
 /*
  * set the ID token "iat" slack
  */
-static const char *oidc_set_idtoken_iat_slack(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_set_idtoken_iat_slack(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_idtoken_iat_slack(cmd->pool, arg,
 			&cfg->provider.idtoken_iat_slack);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -980,10 +1032,10 @@
 /*
  * set the userinfo refresh interval
  */
-static const char *oidc_set_userinfo_refresh_interval(cmd_parms *cmd,
+static const char* oidc_set_userinfo_refresh_interval(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_userinfo_refresh_interval(cmd->pool, arg,
 			&cfg->provider.userinfo_refresh_interval);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -992,21 +1044,21 @@
 /*
  * define which data will be returned from the info hook
  */
-static const char * oidc_set_info_hook_data(cmd_parms *cmd, void *m,
+static const char* oidc_set_info_hook_data(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_info_hook_data(cmd->pool, arg,
 			&cfg->info_hook_data);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
 
-static const char * oidc_set_filtered_claims(cmd_parms *cmd, void *m,
+static const char* oidc_set_filtered_claims(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	int offset = (int) (long) cmd->info;
-	apr_hash_t **list = (apr_hash_t **) ((char *) cfg + offset);
+	apr_hash_t **list = (apr_hash_t**) ((char*) cfg + offset);
 	if (*list == NULL)
 		*list = apr_hash_make(cmd->pool);
 	apr_hash_set(*list, arg, APR_HASH_KEY_STRING, arg);
@@ -1016,12 +1068,12 @@
 /*
  * set the token binding policy
  */
-static const char *oidc_set_token_binding_policy(cmd_parms *cmd,
+static const char* oidc_set_token_binding_policy(cmd_parms *cmd,
 		void *struct_ptr, const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	int offset = (int) (long) cmd->info;
-	int *token_binding_policy = (int *) ((char *) cfg + offset);
+	int *token_binding_policy = (int*) ((char*) cfg + offset);
 	const char *rv = oidc_parse_token_binding_policy(cmd->pool, arg,
 			token_binding_policy);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -1030,10 +1082,10 @@
 /*
  * set the claim prefix
  */
-static const char *oidc_cfg_set_claim_prefix(cmd_parms *cmd, void *struct_ptr,
+static const char* oidc_cfg_set_claim_prefix(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);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	char *w = ap_getword_conf(cmd->pool, &args);
 	if (*w == '\0' || *args != 0)
 		cfg->claim_prefix = "";
@@ -1045,7 +1097,7 @@
 /*
  * get the claim prefix
  */
-const char *oidc_cfg_claim_prefix(request_rec *r) {
+const char* oidc_cfg_claim_prefix(request_rec *r) {
 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
 			&auth_openidc_module);
 	if (cfg->claim_prefix == NULL)
@@ -1056,10 +1108,10 @@
 /*
  * set the HTTP method used to send the authentication request to the provider
  */
-const char *oidc_set_auth_request_method(cmd_parms *cmd, void *struct_ptr,
+const char* oidc_set_auth_request_method(cmd_parms *cmd, void *struct_ptr,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_auth_request_method(cmd->pool, arg,
 			&cfg->provider.auth_request_method);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
@@ -1068,10 +1120,10 @@
 /*
  * set the introspection authorization static bearer token
  */
-static const char *oidc_set_client_auth_bearer_token(cmd_parms *cmd,
+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);
+	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;
@@ -1079,12 +1131,12 @@
 }
 
 /*
- * set the maximun number of parallel state cookies
+ * set the maximum number of parallel state cookies
  */
-static const char *oidc_set_max_number_of_state_cookies(cmd_parms *cmd,
+static const char* oidc_set_max_number_of_state_cookies(cmd_parms *cmd,
 		void *struct_ptr, const char *arg1, const char *arg2) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_max_number_of_state_cookies(cmd->pool, arg1,
 			arg2, &cfg->max_number_of_state_cookies,
 			&cfg->delete_oldest_state_cookies);
@@ -1092,7 +1144,7 @@
 }
 
 /*
- * return the maximun number of parallel state cookies
+ * return the maximum number of parallel state cookies
  */
 int oidc_cfg_max_number_of_state_cookies(oidc_cfg *cfg) {
 	if (cfg->max_number_of_state_cookies == OIDC_CONFIG_POS_INT_UNSET)
@@ -1112,9 +1164,9 @@
 /*
  * set the time in seconds that the access token needs to be valid for
  */
-static const char * oidc_set_refresh_access_token_before_expiry(cmd_parms *cmd,
+static const char* oidc_set_refresh_access_token_before_expiry(cmd_parms *cmd,
 		void *m, const char *arg1, const char *arg2) {
-	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg *) m;
+	oidc_dir_cfg *dir_cfg = (oidc_dir_cfg*) m;
 	const char *rv1 = oidc_parse_refresh_access_token_before_expiry(cmd->pool,
 			arg1, &dir_cfg->refresh_access_token_before_expiry);
 	if (rv1 != NULL)
@@ -1133,19 +1185,19 @@
 /*
  * define which header we use for calculating the fingerprint of the state during authentication
  */
-static const char * oidc_set_state_input_headers_as(cmd_parms *cmd, void *m,
+static const char* oidc_set_state_input_headers_as(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	const char *rv = oidc_parse_set_state_input_headers_as(cmd->pool, arg,
 			&cfg->state_input_headers);
 	return OIDC_CONFIG_DIR_RV(cmd, rv);
 }
 
-static const char * oidc_set_redirect_urls_allowed(cmd_parms *cmd, void *m,
+static const char* oidc_set_redirect_urls_allowed(cmd_parms *cmd, void *m,
 		const char *arg) {
-	oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(
-			cmd->server->module_config, &auth_openidc_module);
+	oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(cmd->server->module_config,
+			&auth_openidc_module);
 	if (cfg->redirect_urls_allowed == NULL)
 		cfg->redirect_urls_allowed = apr_hash_make(cmd->pool);
 	apr_hash_set(cfg->redirect_urls_allowed, arg, APR_HASH_KEY_STRING, arg);
@@ -1168,15 +1220,15 @@
 	return dir_cfg->logout_on_error_refresh;
 }
 
-char *oidc_cfg_dir_state_cookie_prefix(request_rec *r) {
-    oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
-                                                 &auth_openidc_module);
-    if ((dir_cfg->state_cookie_prefix == NULL)
-        || ((dir_cfg->state_cookie_prefix != NULL)
-            && (apr_strnatcmp(dir_cfg->state_cookie_prefix, OIDC_CONFIG_STRING_UNSET)
-                == 0)))
-        return OIDC_DEFAULT_STATE_COOKIE_PREFIX;
-    return dir_cfg->state_cookie_prefix;
+char* oidc_cfg_dir_state_cookie_prefix(request_rec *r) {
+	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
+			&auth_openidc_module);
+	if ((dir_cfg->state_cookie_prefix == NULL)
+			|| ((dir_cfg->state_cookie_prefix != NULL)
+					&& (apr_strnatcmp(dir_cfg->state_cookie_prefix,
+							OIDC_CONFIG_STRING_UNSET) == 0)))
+		return OIDC_DEFAULT_STATE_COOKIE_PREFIX;
+	return dir_cfg->state_cookie_prefix;
 }
 
 void oidc_cfg_provider_init(oidc_provider_t *provider) {
@@ -1192,6 +1244,7 @@
 	provider->client_secret = NULL;
 	provider->token_endpoint_tls_client_cert = NULL;
 	provider->token_endpoint_tls_client_key = NULL;
+	provider->token_endpoint_tls_client_key_pwd = NULL;
 	provider->registration_endpoint_url = NULL;
 	provider->registration_endpoint_json = NULL;
 	provider->check_session_iframe = NULL;
@@ -1230,7 +1283,7 @@
 /*
  * create a new server config record with defaults
  */
-void *oidc_create_server_config(apr_pool_t *pool, server_rec *svr) {
+void* oidc_create_server_config(apr_pool_t *pool, server_rec *svr) {
 	oidc_cfg *c = apr_pcalloc(pool, sizeof(oidc_cfg));
 
 	c->merged = FALSE;
@@ -1290,6 +1343,9 @@
 #ifdef USE_LIBHIREDIS
 	c->cache_redis_server = NULL;
 	c->cache_redis_password = NULL;
+	c->cache_redis_database = -1;
+	c->cache_redis_connect_timeout = -1;
+	c->cache_redis_timeout = -1;
 #endif
 
 	c->metadata_dir = NULL;
@@ -1343,13 +1399,15 @@
 
 	c->redirect_urls_allowed = NULL;
 
+	c->ca_bundle_path = NULL;
+
 	return c;
 }
 
 /*
  * merge a new server config with a base one
  */
-void *oidc_merge_server_config(apr_pool_t *pool, void *BASE, void *ADD) {
+void* oidc_merge_server_config(apr_pool_t *pool, void *BASE, void *ADD) {
 	oidc_cfg *c = apr_pcalloc(pool, sizeof(oidc_cfg));
 	oidc_cfg *base = BASE;
 	oidc_cfg *add = ADD;
@@ -1413,6 +1471,10 @@
 			add->provider.token_endpoint_tls_client_key != NULL ?
 					add->provider.token_endpoint_tls_client_key :
 					base->provider.token_endpoint_tls_client_key;
+	c->provider.token_endpoint_tls_client_key_pwd =
+		add->provider.token_endpoint_tls_client_key_pwd != NULL ?
+				add->provider.token_endpoint_tls_client_key_pwd :
+				base->provider.token_endpoint_tls_client_key_pwd;
 	c->provider.token_endpoint_tls_client_cert =
 			add->provider.token_endpoint_tls_client_cert != NULL ?
 					add->provider.token_endpoint_tls_client_cert :
@@ -1705,6 +1767,15 @@
 	c->cache_redis_password =
 			add->cache_redis_password != NULL ?
 					add->cache_redis_password : base->cache_redis_password;
+	c->cache_redis_database =
+			add->cache_redis_database != -1 ?
+					add->cache_redis_database : base->cache_redis_database;
+	c->cache_redis_connect_timeout =
+			add->cache_redis_connect_timeout != -1 ?
+					add->cache_redis_connect_timeout : base->cache_redis_connect_timeout;
+	c->cache_redis_timeout =
+			add->cache_redis_timeout != -1 ?
+					add->cache_redis_timeout : base->cache_redis_timeout;
 #endif
 
 	c->metadata_dir =
@@ -1818,6 +1889,10 @@
 			add->redirect_urls_allowed != NULL ?
 					add->redirect_urls_allowed : base->redirect_urls_allowed;
 
+	c->ca_bundle_path =
+			add->ca_bundle_path != NULL ?
+					add->ca_bundle_path : base->ca_bundle_path;
+
 	return c;
 }
 
@@ -1840,22 +1915,25 @@
 /*
  * create a new directory config record with defaults
  */
-void *oidc_create_dir_config(apr_pool_t *pool, char *path) {
+void* oidc_create_dir_config(apr_pool_t *pool, char *path) {
 	oidc_dir_cfg *c = apr_pcalloc(pool, sizeof(oidc_dir_cfg));
 	c->discover_url = OIDC_CONFIG_STRING_UNSET;
 	c->cookie = OIDC_CONFIG_STRING_UNSET;
 	c->cookie_path = OIDC_CONFIG_STRING_UNSET;
 	c->authn_header = OIDC_CONFIG_STRING_UNSET;
 	c->unauth_action = OIDC_CONFIG_POS_INT_UNSET;
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
 	c->unauth_expression = NULL;
+#endif
 	c->unautz_action = OIDC_CONFIG_POS_INT_UNSET;
 	c->pass_cookies = NULL;
 	c->strip_cookies = NULL;
 	c->pass_info_in_headers = OIDC_CONFIG_POS_INT_UNSET;
 	c->pass_info_in_env_vars = OIDC_CONFIG_POS_INT_UNSET;
+	c->pass_info_base64url = OIDC_CONFIG_POS_INT_UNSET;
 	c->oauth_accept_token_in = OIDC_CONFIG_POS_INT_UNSET;
 	c->oauth_accept_token_options = apr_hash_make(pool);
-	c->oauth_token_introspect_interval = OIDC_CONFIG_POS_INT_UNSET;
+	c->oauth_token_introspect_interval = -2;
 	c->preserve_post = OIDC_CONFIG_POS_INT_UNSET;
 	c->pass_refresh_token = OIDC_CONFIG_POS_INT_UNSET;
 	c->path_auth_request_params = NULL;
@@ -1866,7 +1944,7 @@
 	return (c);
 }
 
-char *oidc_cfg_dir_discover_url(request_rec *r) {
+char* oidc_cfg_dir_discover_url(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	if ((dir_cfg->discover_url != NULL) && (apr_strnatcmp(dir_cfg->discover_url,
@@ -1875,7 +1953,7 @@
 	return dir_cfg->discover_url;
 }
 
-char *oidc_cfg_dir_cookie(request_rec *r) {
+char* oidc_cfg_dir_cookie(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	if ((dir_cfg->cookie == NULL)
@@ -1886,7 +1964,7 @@
 	return dir_cfg->cookie;
 }
 
-char *oidc_cfg_dir_cookie_path(request_rec *r) {
+char* oidc_cfg_dir_cookie_path(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	if ((dir_cfg->cookie_path == NULL)
@@ -1897,7 +1975,7 @@
 	return dir_cfg->cookie_path;
 }
 
-char *oidc_cfg_dir_authn_header(request_rec *r) {
+char* oidc_cfg_dir_authn_header(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	if ((dir_cfg->authn_header == NULL)
@@ -1924,6 +2002,14 @@
 	return dir_cfg->pass_info_in_env_vars;
 }
 
+apr_byte_t oidc_cfg_dir_pass_info_base64url(request_rec *r) {
+	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
+			&auth_openidc_module);
+	if (dir_cfg->pass_info_base64url == OIDC_CONFIG_POS_INT_UNSET)
+		return OIDC_DEFAULT_PASS_APP_INFO_BASE64URL;
+	return dir_cfg->pass_info_base64url;
+}
+
 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);
@@ -1940,7 +2026,7 @@
 	return dir_cfg->oauth_accept_token_in;
 }
 
-char *oidc_cfg_dir_accept_token_in_option(request_rec *r, const char *key) {
+char* oidc_cfg_dir_accept_token_in_option(request_rec *r, const char *key) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	return apr_hash_get(dir_cfg->oauth_accept_token_options, key,
@@ -1950,7 +2036,7 @@
 int oidc_cfg_token_introspection_interval(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
-	if (dir_cfg->oauth_token_introspect_interval == OIDC_CONFIG_POS_INT_UNSET)
+	if (dir_cfg->oauth_token_introspect_interval <= -2)
 		return OIDC_DEFAULT_TOKEN_INTROSPECTION_INTERVAL;
 	return dir_cfg->oauth_token_introspect_interval;
 }
@@ -1963,13 +2049,13 @@
 	return dir_cfg->preserve_post;
 }
 
-apr_array_header_t *oidc_dir_cfg_pass_cookies(request_rec *r) {
+apr_array_header_t* oidc_dir_cfg_pass_cookies(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	return dir_cfg->pass_cookies;
 }
 
-apr_array_header_t *oidc_dir_cfg_strip_cookies(request_rec *r) {
+apr_array_header_t* oidc_dir_cfg_strip_cookies(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	return dir_cfg->strip_cookies;
@@ -1979,11 +2065,12 @@
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 
-	int rc = 0;
-	const char *err_str = NULL;
 	if (dir_cfg->unauth_action == OIDC_CONFIG_POS_INT_UNSET)
 		return OIDC_DEFAULT_UNAUTH_ACTION;
 
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
+	int rc = 0;
+	const char *err_str = NULL;
 	if (dir_cfg->unauth_expression == NULL)
 		return dir_cfg->unauth_action;
 
@@ -1995,12 +2082,19 @@
 	}
 
 	return (rc > 0) ? dir_cfg->unauth_action : OIDC_DEFAULT_UNAUTH_ACTION;
+#else
+	return dir_cfg->unauth_action;
+#endif
 }
 
 apr_byte_t oidc_dir_cfg_unauth_expr_is_set(request_rec *r) {
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	return (dir_cfg->unauth_expression != NULL) ? TRUE : FALSE;
+#else
+	return FALSE;
+#endif
 }
 
 int oidc_dir_cfg_unautz_action(request_rec *r) {
@@ -2011,13 +2105,13 @@
 	return dir_cfg->unautz_action;
 }
 
-char *oidc_dir_cfg_path_auth_request_params(request_rec *r) {
+char* oidc_dir_cfg_path_auth_request_params(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	return dir_cfg->path_auth_request_params;
 }
 
-char *oidc_dir_cfg_path_scope(request_rec *r) {
+char* oidc_dir_cfg_path_scope(request_rec *r) {
 	oidc_dir_cfg *dir_cfg = ap_get_module_config(r->per_dir_config,
 			&auth_openidc_module);
 	return dir_cfg->path_scope;
@@ -2026,7 +2120,7 @@
 /*
  * merge a new directory config with a base one
  */
-void *oidc_merge_dir_config(apr_pool_t *pool, void *BASE, void *ADD) {
+void* oidc_merge_dir_config(apr_pool_t *pool, void *BASE, void *ADD) {
 	oidc_dir_cfg *c = apr_pcalloc(pool, sizeof(oidc_dir_cfg));
 	oidc_dir_cfg *base = BASE;
 	oidc_dir_cfg *add = ADD;
@@ -2045,9 +2139,11 @@
 	c->unauth_action =
 			add->unauth_action != OIDC_CONFIG_POS_INT_UNSET ?
 					add->unauth_action : base->unauth_action;
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
 	c->unauth_expression =
 			add->unauth_expression != NULL ?
 					add->unauth_expression : base->unauth_expression;
+#endif
 	c->unautz_action =
 			add->unautz_action != OIDC_CONFIG_POS_INT_UNSET ?
 					add->unautz_action : base->unautz_action;
@@ -2064,6 +2160,9 @@
 	c->pass_info_in_env_vars =
 			add->pass_info_in_env_vars != OIDC_CONFIG_POS_INT_UNSET ?
 					add->pass_info_in_env_vars : base->pass_info_in_env_vars;
+	c->pass_info_base64url =
+			add->pass_info_base64url != OIDC_CONFIG_POS_INT_UNSET ?
+					add->pass_info_base64url : base->pass_info_base64url;
 	c->oauth_accept_token_in =
 			add->oauth_accept_token_in != OIDC_CONFIG_POS_INT_UNSET ?
 					add->oauth_accept_token_in : base->oauth_accept_token_in;
@@ -2072,7 +2171,7 @@
 					add->oauth_accept_token_options :
 					base->oauth_accept_token_options;
 	c->oauth_token_introspect_interval =
-			add->oauth_token_introspect_interval != OIDC_CONFIG_POS_INT_UNSET ?
+			add->oauth_token_introspect_interval >= -1 ?
 					add->oauth_token_introspect_interval :
 					base->oauth_token_introspect_interval;
 	c->preserve_post =
@@ -2098,9 +2197,10 @@
 					add->logout_on_error_refresh :
 					base->logout_on_error_refresh;
 
-    c->state_cookie_prefix =
-            (apr_strnatcmp(add->state_cookie_prefix, OIDC_CONFIG_STRING_UNSET) != 0) ?
-            add->state_cookie_prefix : base->state_cookie_prefix;
+	c->state_cookie_prefix =
+			(apr_strnatcmp(add->state_cookie_prefix, OIDC_CONFIG_STRING_UNSET)
+					!= 0) ?
+							add->state_cookie_prefix : base->state_cookie_prefix;
 
 	return (c);
 }
@@ -2226,6 +2326,9 @@
 
 	}
 
+	if ((c->cache_encrypt == 1) && (c->crypto_passphrase == NULL))
+		return oidc_check_config_error(s, OIDCCryptoPassphrase);
+
 	return OK;
 }
 
@@ -2320,9 +2423,9 @@
 #endif /* defined(OPENSSL_THREADS) && APR_HAS_THREADS */
 
 static apr_status_t oidc_cleanup_child(void *data) {
-	server_rec *sp = (server_rec *) data;
+	server_rec *sp = (server_rec*) data;
 	while (sp != NULL) {
-		oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(sp->module_config,
+		oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(sp->module_config,
 				&auth_openidc_module);
 		if (cfg->cache->destroy != NULL) {
 			if (cfg->cache->destroy(sp) != APR_SUCCESS) {
@@ -2332,8 +2435,10 @@
 
 		// can do this even though we haven't got a deep copy
 		// since references within the object will be set to NULL
-		oidc_jwk_list_destroy(sp->process->pool, cfg->oauth.verify_public_keys);
-		oidc_jwk_list_destroy(sp->process->pool, cfg->oauth.verify_shared_keys);
+		oidc_jwk_list_destroy_hash(sp->process->pool,
+				cfg->oauth.verify_public_keys);
+		oidc_jwk_list_destroy_hash(sp->process->pool,
+				cfg->oauth.verify_shared_keys);
 		oidc_jwk_list_destroy(sp->process->pool, cfg->public_keys);
 		oidc_jwk_list_destroy(sp->process->pool, cfg->private_keys);
 
@@ -2363,7 +2468,7 @@
 	EVP_cleanup();
 	curl_global_cleanup();
 
-	ap_log_error(APLOG_MARK, APLOG_INFO, 0, (server_rec * ) data,
+	ap_log_error(APLOG_MARK, APLOG_INFO, 0, (server_rec* ) data,
 			"%s - shutdown", NAMEVERSION);
 
 	return APR_SUCCESS;
@@ -2384,7 +2489,7 @@
 	 */
 	apr_pool_userdata_get(&data, userdata_key, s->process->pool);
 	if (data == NULL) {
-		apr_pool_userdata_set((const void *) 1, userdata_key,
+		apr_pool_userdata_set((const void*) 1, userdata_key,
 				apr_pool_cleanup_null, s->process->pool);
 		return OK;
 	}
@@ -2446,7 +2551,7 @@
 
 	server_rec *sp = s;
 	while (sp != NULL) {
-		oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(sp->module_config,
+		oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(sp->module_config,
 				&auth_openidc_module);
 		if (cfg->cache->post_config != NULL) {
 			if (cfg->cache->post_config(sp) != OK)
@@ -2476,7 +2581,7 @@
 }
 
 #if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
-static const char *oidc_parse_config(cmd_parms *cmd, const char *require_line,
+static const char* oidc_parse_config(cmd_parms *cmd, const char *require_line,
 		const void **parsed_require_line) {
 	const char *expr_err = NULL;
 	ap_expr_info_t *expr;
@@ -2509,7 +2614,7 @@
 static void oidc_child_init(apr_pool_t *p, server_rec *s) {
 	server_rec *sp = s;
 	while (sp != NULL) {
-		oidc_cfg *cfg = (oidc_cfg *) ap_get_module_config(sp->module_config,
+		oidc_cfg *cfg = (oidc_cfg*) ap_get_module_config(sp->module_config,
 				&auth_openidc_module);
 		if (cfg->cache->child_init != NULL) {
 			if (cfg->cache->child_init(p, sp) != APR_SUCCESS) {
@@ -2532,7 +2637,7 @@
 		return;
 
 	apr_table_t *userdata_post_params = NULL;
-	apr_pool_userdata_get((void **) &userdata_post_params,
+	apr_pool_userdata_get((void**) &userdata_post_params,
 			OIDC_USERDATA_POST_PARAMS_KEY, r->pool);
 	if (userdata_post_params == NULL)
 		return;
@@ -2576,7 +2681,7 @@
 
 			APR_BUCKET_REMOVE(b_in);
 
-			apr_pool_userdata_get((void **) &userdata_post_params,
+			apr_pool_userdata_get((void**) &userdata_post_params,
 					OIDC_USERDATA_POST_PARAMS_KEY, f->r->pool);
 
 			if (userdata_post_params != NULL) {
@@ -2618,7 +2723,7 @@
 void oidc_register_hooks(apr_pool_t *pool) {
 	ap_hook_post_config(oidc_post_config, NULL, NULL, APR_HOOK_LAST);
 	ap_hook_child_init(oidc_child_init, NULL, NULL, APR_HOOK_MIDDLE);
-	ap_hook_handler(oidc_content_handler, NULL, NULL, APR_HOOK_MIDDLE);
+	ap_hook_handler(oidc_content_handler, NULL, NULL, APR_HOOK_FIRST);
 	ap_hook_insert_filter(oidc_filter_in_insert_filter, NULL, NULL,
 			APR_HOOK_MIDDLE);
 	ap_register_input_filter(oidcFilterName, oidc_filter_in_filter, NULL,
@@ -2726,7 +2831,7 @@
 				oidc_set_public_key_files,
 				(void *)APR_OFFSETOF(oidc_cfg, public_keys),
 				RSRC_CONF,
-				"The fully qualified names of the files that contain the X.509 certificates that contains the RSA public keys that can be used for encryption by the OP."),
+				"The fully qualified names of the files that contain the RSA public keys or X.509 certificates that contains the RSA public keys that can be used for signature validation or encryption by the OP."),
 		AP_INIT_ITERATE(OIDCPrivateKeyFiles,
 				oidc_set_private_key_files_enc,
 				NULL,
@@ -2861,7 +2966,11 @@
 				(void*)APR_OFFSETOF(oidc_cfg, provider.token_endpoint_tls_client_key),
 				RSRC_CONF,
 				"TLS client certificate private key used for calls to OpenID Connect OP token endpoint."),
-
+		AP_INIT_TAKE1(OIDCClientTokenEndpointKeyPassword,
+				oidc_set_passphrase_slot,
+				(void*)APR_OFFSETOF(oidc_cfg, provider.token_endpoint_tls_client_key_pwd),
+				RSRC_CONF,
+				"TLS client certificate private key password used for calls to OpenID Connect OP token endpoint."),
 		AP_INIT_TAKE1(OIDCRedirectURI,
 				oidc_set_relative_or_absolute_url_slot,
 				(void *)APR_OFFSETOF(oidc_cfg, redirect_uri),
@@ -2898,7 +3007,7 @@
 				RSRC_CONF,
 				"Specify an outgoing proxy for your network (<host>[:<port>]."),
 		AP_INIT_TAKE1(OIDCCryptoPassphrase,
-				oidc_set_string_slot,
+				oidc_set_passphrase_slot,
 				(void*)APR_OFFSETOF(oidc_cfg, crypto_passphrase),
 				RSRC_CONF,
 				"Passphrase used for AES crypto on cookies and state."),
@@ -3106,6 +3215,21 @@
 				(void*)APR_OFFSETOF(oidc_cfg, cache_redis_password),
 				RSRC_CONF,
 				"Password for authentication to the Redis servers."),
+		AP_INIT_TAKE1(OIDCRedisCacheDatabase,
+				oidc_set_int_slot,
+				(void*)APR_OFFSETOF(oidc_cfg, cache_redis_database),
+				RSRC_CONF,
+				"Database for the Redis servers."),
+		AP_INIT_TAKE1(OIDCRedisCacheConnectTimeout,
+				oidc_set_int_slot,
+				(void*)APR_OFFSETOF(oidc_cfg, cache_redis_connect_timeout),
+				RSRC_CONF,
+				"Timeout for connecting to the Redis servers."),
+		AP_INIT_TAKE1(OIDCRedisCacheTimeout,
+				oidc_set_int_slot,
+				(void*)APR_OFFSETOF(oidc_cfg, cache_redis_timeout),
+				RSRC_CONF,
+				"Timeout waiting for a response of the Redis servers."),
 #endif
 		AP_INIT_TAKE1(OIDCHTMLErrorTemplate,
 				oidc_set_string_slot,
@@ -3114,7 +3238,7 @@
 				"Name of a HTML error template: needs to contain two \"%s\" characters, one for the error message, one for the description."),
 
 		AP_INIT_TAKE1(OIDCDiscoverURL,
-				oidc_set_url_slot_dir_cfg,
+				oidc_set_relative_or_absolute_url_slot_dir_cfg,
 				(void *)APR_OFFSETOF(oidc_dir_cfg, discover_url),
 				RSRC_CONF|ACCESS_CONF|OR_AUTHCFG,
 				"Defines an external IDP Discovery page"),
@@ -3153,7 +3277,7 @@
 				(void *) APR_OFFSETOF(oidc_dir_cfg, unautz_action),
 				RSRC_CONF|ACCESS_CONF|OR_AUTHCFG,
 				"Sets the action taken when an unauthorized request occurs: must be one of \"401\" (default), \"403\" or \"auth\"."),
-		AP_INIT_TAKE1(OIDCPassClaimsAs,
+		AP_INIT_TAKE12(OIDCPassClaimsAs,
 				oidc_set_pass_claims_as, NULL,
 				RSRC_CONF|ACCESS_CONF|OR_AUTHCFG,
 				"Specify how claims are passed to the application(s); must be one of \"none\", \"headers\", \"environment\" or \"both\" (default)."),
@@ -3247,5 +3371,11 @@
 				RSRC_CONF|ACCESS_CONF|OR_AUTHCFG,
 				"Define the cookie prefix for the state cookie."),
 
+		AP_INIT_TAKE1(OIDCCABundlePath,
+				oidc_set_path_slot,
+				(void *) APR_OFFSETOF(oidc_cfg, ca_bundle_path),
+				RSRC_CONF,
+				"Sets the path to the CA bundle to be used by cURL."),
+
 		{ NULL }
 };
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/jose.c libapache2-mod-auth-openidc-2.4.9/src/jose.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/jose.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/jose.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -68,13 +60,13 @@
 
 /* to extract a b64 encoded certificate representation as a single string */
 static int oidc_jose_util_get_b64encoded_certificate_data(apr_pool_t *p,
-		X509 *x509_cert, unsigned char** b64_encoded_certificate,
+		X509 *x509_cert, unsigned char **b64_encoded_certificate,
 		oidc_jose_error_t *err) {
 	int rc = 0;
 	char *name = NULL, *header = NULL;
 	long len = 0, b64_len = 0;
 	BIO *bio = NULL;
-	unsigned char* data = NULL;
+	unsigned char *data = NULL;
 
 	if ((bio = BIO_new(BIO_s_mem())) == NULL) {
 		oidc_jose_error_openssl(err, "BIO_new");
@@ -93,7 +85,7 @@
 	/* "For every 3 bytes of input provided 4 bytes of output data will be produced." */
 	b64_len = (((len + 2) / 3) * 4) + 1;
 
-	*b64_encoded_certificate = (unsigned char *) apr_pcalloc(p, b64_len);
+	*b64_encoded_certificate = (unsigned char*) apr_pcalloc(p, b64_len);
 	if (!*b64_encoded_certificate) {
 		oidc_jose_error_openssl(err, "apr_pcalloc");
 		goto end;
@@ -119,8 +111,8 @@
 }
 
 /* definition follows */
-static char *internal_cjose_jwk_to_json(apr_pool_t *pool, oidc_jwk_t *oidc_jwk,
-		oidc_jose_error_t *oidc_err);
+static char* internal_cjose_jwk_to_json(apr_pool_t *pool,
+		const oidc_jwk_t *oidc_jwk, oidc_jose_error_t *oidc_err);
 
 /*
  * assemble an error report
@@ -149,7 +141,7 @@
 /*
  * create a new JWT
  */
-oidc_jwt_t *oidc_jwt_new(apr_pool_t *pool, int create_header,
+oidc_jwt_t* oidc_jwt_new(apr_pool_t *pool, int create_header,
 		int create_payload) {
 	oidc_jwt_t *jwt = apr_pcalloc(pool, sizeof(oidc_jwt_t));
 	if (create_header) {
@@ -165,7 +157,7 @@
 /*
  * get a header value from a JWT
  */
-const char *oidc_jwt_hdr_get(oidc_jwt_t *jwt, const char *key) {
+const char* oidc_jwt_hdr_get(oidc_jwt_t *jwt, const char *key) {
 	cjose_err cjose_err;
 	cjose_header_t *hdr = cjose_jws_get_protected(jwt->cjose_jws);
 	return hdr ? cjose_header_get(hdr, key, &cjose_err) : NULL;
@@ -179,7 +171,7 @@
 /*
  * perform compact serialization on a JWT and return the resulting string
  */
-char *oidc_jwt_serialize(apr_pool_t *pool, oidc_jwt_t *jwt,
+char* oidc_jwt_serialize(apr_pool_t *pool, oidc_jwt_t *jwt,
 		oidc_jose_error_t *err) {
 	cjose_err cjose_err;
 	const char *cser = NULL;
@@ -196,7 +188,7 @@
 
 		char *out = NULL;
 		size_t out_len;
-		if (cjose_base64url_encode((const uint8_t *) s_payload,
+		if (cjose_base64url_encode((const uint8_t*) s_payload,
 				strlen(s_payload), &out, &out_len, &cjose_err) == FALSE)
 			return NULL;
 		cser = apr_pstrmemdup(pool, out, out_len);
@@ -276,7 +268,7 @@
 /*
  * create a new JWK
  */
-static oidc_jwk_t *oidc_jwk_new(apr_pool_t *pool) {
+static oidc_jwk_t* oidc_jwk_new(apr_pool_t *pool) {
 	oidc_jwk_t *jwk = apr_pcalloc(pool, sizeof(oidc_jwk_t));
 	return jwk;
 }
@@ -291,7 +283,7 @@
 /*
  * parse a JSON object with an RSA "x5c" JWK representation in to a cjose JWK object
  */
-static cjose_jwk_t *oidc_jwk_parse_rsa_x5c_spec(apr_pool_t *pool,
+static cjose_jwk_t* oidc_jwk_parse_rsa_x5c_spec(apr_pool_t *pool,
 		const char *s_json, oidc_jose_error_t *err) {
 
 	cjose_jwk_t *cjose_jwk = NULL;
@@ -337,7 +329,7 @@
 /*
  * create a JWK struct from a cjose_jwk object
  */
-static oidc_jwk_t *oidc_jwk_from_cjose(apr_pool_t *pool, cjose_jwk_t *cjose_jwk) {
+static oidc_jwk_t* oidc_jwk_from_cjose(apr_pool_t *pool, cjose_jwk_t *cjose_jwk) {
 	cjose_err cjose_err;
 	oidc_jwk_t *jwk = oidc_jwk_new(pool);
 	jwk->cjose_jwk = cjose_jwk;
@@ -349,7 +341,7 @@
 /*
  * parse a JSON string to a JWK struct
  */
-oidc_jwk_t *oidc_jwk_parse(apr_pool_t *pool, const char *s_json,
+oidc_jwk_t* oidc_jwk_parse(apr_pool_t *pool, const char *s_json,
 		oidc_jose_error_t *err) {
 	cjose_err cjose_err;
 	cjose_jwk_t *cjose_jwk = cjose_jwk_import(s_json, strlen(s_json),
@@ -383,17 +375,26 @@
 /*
  * destroy a list of JWKs structs
  */
-void oidc_jwk_list_destroy(apr_pool_t *pool, apr_hash_t *keys) {
+void oidc_jwk_list_destroy_hash(apr_pool_t *pool, apr_hash_t *keys) {
 	apr_hash_index_t *hi = NULL;
 	if (keys == NULL)
 		return;
 	for (hi = apr_hash_first(pool, keys); hi; hi = apr_hash_next(hi)) {
 		oidc_jwk_t *jwk = NULL;
-		apr_hash_this(hi, NULL, NULL, (void **) &jwk);
+		apr_hash_this(hi, NULL, NULL, (void**) &jwk);
 		oidc_jwk_destroy(jwk);
 	}
 }
 
+void oidc_jwk_list_destroy(apr_pool_t *pool, apr_array_header_t *keys_list) {
+	if (keys_list == NULL)
+		return;
+	oidc_jwk_t **jwk = NULL;
+	while ((jwk = apr_array_pop(keys_list))) {
+		oidc_jwk_destroy(*jwk);
+	}
+}
+
 /*
  * parse a JSON object in to a JWK struct
  */
@@ -408,8 +409,8 @@
 /*
  * convert a JWK struct to a JSON string
  */
-apr_byte_t oidc_jwk_to_json(apr_pool_t *pool, oidc_jwk_t *jwk, char **s_json,
-		oidc_jose_error_t *err) {
+apr_byte_t oidc_jwk_to_json(apr_pool_t *pool, const oidc_jwk_t *jwk,
+		char **s_json, oidc_jose_error_t *err) {
 	char *s = internal_cjose_jwk_to_json(pool, jwk, err);
 	if (s == NULL) {
 		oidc_jose_error(err, "internal_cjose_jwk_to_json failed");
@@ -430,7 +431,7 @@
 	unsigned char *hashed = NULL;
 	unsigned int hashed_len = 0;
 	if (oidc_jose_hash_bytes(pool, openssl_hash_algo,
-			(const unsigned char *) input, input_len, &hashed, &hashed_len,
+			(const unsigned char*) input, input_len, &hashed, &hashed_len,
 			&err) == FALSE) {
 		return FALSE;
 	}
@@ -479,7 +480,7 @@
 /*
  * create an "oct" symmetric JWK
  */
-oidc_jwk_t *oidc_jwk_create_symmetric_key(apr_pool_t *pool, const char *skid,
+oidc_jwk_t* oidc_jwk_create_symmetric_key(apr_pool_t *pool, const char *skid,
 		const unsigned char *key, unsigned int key_len, apr_byte_t set_kid,
 		oidc_jose_error_t *err) {
 
@@ -494,7 +495,7 @@
 
 	if (set_kid == TRUE) {
 		if (oidc_jwk_set_or_generate_kid(pool, cjose_jwk, skid,
-				(const char *) key, key_len, err) == FALSE) {
+				(const char*) key, key_len, err) == FALSE) {
 			cjose_jwk_release(cjose_jwk);
 			return NULL;
 		}
@@ -524,7 +525,7 @@
 /*
  * return all supported signing algorithms
  */
-apr_array_header_t *oidc_jose_jws_supported_algorithms(apr_pool_t *pool) {
+apr_array_header_t* oidc_jose_jws_supported_algorithms(apr_pool_t *pool) {
 	apr_array_header_t *result = apr_array_make(pool, 12, sizeof(const char*));
 	*(const char**) apr_array_push(result) = CJOSE_HDR_ALG_RS256;
 	*(const char**) apr_array_push(result) = CJOSE_HDR_ALG_RS384;
@@ -556,7 +557,7 @@
 /*
  * return all supported content encryption key algorithms
  */
-apr_array_header_t *oidc_jose_jwe_supported_algorithms(apr_pool_t *pool) {
+apr_array_header_t* oidc_jose_jwe_supported_algorithms(apr_pool_t *pool) {
 	apr_array_header_t *result = apr_array_make(pool, 4, sizeof(const char*));
 	*(const char**) apr_array_push(result) = CJOSE_HDR_ALG_RSA1_5;
 	*(const char**) apr_array_push(result) = CJOSE_HDR_ALG_A128KW;
@@ -578,7 +579,7 @@
 /*
  * return all supported encryption algorithms
  */
-apr_array_header_t *oidc_jose_jwe_supported_encryptions(apr_pool_t *pool) {
+apr_array_header_t* oidc_jose_jwe_supported_encryptions(apr_pool_t *pool) {
 	apr_array_header_t *result = apr_array_make(pool, 5, sizeof(const char*));
 	*(const char**) apr_array_push(result) = CJOSE_HDR_ENC_A128CBC_HS256;
 	*(const char**) apr_array_push(result) = CJOSE_HDR_ENC_A192CBC_HS384;
@@ -703,7 +704,7 @@
 /*
  * decrypt a JWT and return the plaintext
  */
-static uint8_t *oidc_jwe_decrypt_impl(apr_pool_t *pool, cjose_jwe_t *jwe,
+static uint8_t* oidc_jwe_decrypt_impl(apr_pool_t *pool, cjose_jwe_t *jwe,
 		apr_hash_t *keys, size_t *content_len, oidc_jose_error_t *err) {
 
 	uint8_t *decrypted = NULL;
@@ -737,7 +738,7 @@
 	} else {
 
 		for (hi = apr_hash_first(pool, keys); hi; hi = apr_hash_next(hi)) {
-			apr_hash_this(hi, NULL, NULL, (void **) &jwk);
+			apr_hash_this(hi, NULL, NULL, (void**) &jwk);
 
 			if (jwk->kty == oidc_alg2kty(alg)) {
 				decrypted = cjose_jwe_decrypt(jwe, jwk->cjose_jwk, content_len,
@@ -813,7 +814,7 @@
 	}
 
 	cjose_header_t *hdr = cjose_jws_get_protected(jwt->cjose_jws);
-	jwt->header.value.json = json_deep_copy((json_t *) hdr);
+	jwt->header.value.json = json_deep_copy((json_t*) hdr);
 	char *str = json_dumps(jwt->header.value.json,
 			JSON_PRESERVE_ORDER | JSON_COMPACT);
 	jwt->header.value.str = apr_pstrdup(pool, str);
@@ -835,7 +836,7 @@
 		return FALSE;
 	}
 
-	if (oidc_jose_parse_payload(pool, (const char *) plaintext, plaintext_len,
+	if (oidc_jose_parse_payload(pool, (const char*) plaintext, plaintext_len,
 			&jwt->payload, err) == FALSE) {
 		oidc_jwt_destroy(jwt);
 		*j_jwt = NULL;
@@ -871,7 +872,7 @@
 apr_byte_t oidc_jwt_sign(apr_pool_t *pool, oidc_jwt_t *jwt, oidc_jwk_t *jwk,
 		oidc_jose_error_t *err) {
 
-	cjose_header_t *hdr = (cjose_header_t *) jwt->header.value.json;
+	cjose_header_t *hdr = (cjose_header_t*) jwt->header.value.json;
 
 	if (jwt->header.alg)
 		oidc_jwt_hdr_set(jwt, CJOSE_HDR_ALG, jwt->header.alg);
@@ -888,7 +889,7 @@
 			JSON_PRESERVE_ORDER | JSON_COMPACT);
 	jwt->payload.value.str = apr_pstrdup(pool, s_payload);
 	jwt->cjose_jws = cjose_jws_sign(jwk->cjose_jwk, hdr,
-			(const uint8_t *) s_payload, strlen(s_payload), &cjose_err);
+			(const uint8_t*) s_payload, strlen(s_payload), &cjose_err);
 	free(s_payload);
 
 	if (jwt->cjose_jws == NULL) {
@@ -916,7 +917,7 @@
 apr_byte_t oidc_jwt_encrypt(apr_pool_t *pool, oidc_jwt_t *jwe, oidc_jwk_t *jwk,
 		const char *payload, char **serialized, oidc_jose_error_t *err) {
 
-	cjose_header_t *hdr = (cjose_header_t *) jwe->header.value.json;
+	cjose_header_t *hdr = (cjose_header_t*) jwe->header.value.json;
 
 	if (jwe->header.alg)
 		oidc_jwt_hdr_set(jwe, CJOSE_HDR_ALG, jwe->header.alg);
@@ -927,7 +928,7 @@
 
 	cjose_err cjose_err;
 	cjose_jwe_t *cjose_jwe = cjose_jwe_encrypt(jwk->cjose_jwk, hdr,
-			(const uint8_t *) payload, strlen(payload), &cjose_err);
+			(const uint8_t*) payload, strlen(payload), &cjose_err);
 	if (cjose_jwe == NULL) {
 		oidc_jose_error(err, "cjose_jwe_encrypt failed: %s",
 				oidc_cjose_e2s(pool, cjose_err));
@@ -990,7 +991,7 @@
 	} else {
 
 		for (hi = apr_hash_first(pool, keys); hi; hi = apr_hash_next(hi)) {
-			apr_hash_this(hi, NULL, NULL, (void **) &jwk);
+			apr_hash_this(hi, NULL, NULL, (void**) &jwk);
 			if (jwk->kty == oidc_jwt_alg2kty(jwt)) {
 				rc = cjose_jws_verify(jwt->cjose_jws, jwk->cjose_jwk,
 						&cjose_err);
@@ -1063,7 +1064,7 @@
 /*
  * return the OpenSSL hash algorithm associated with a specified JWT algorithm
  */
-static char *oidc_jose_alg_to_openssl_digest(const char *alg) {
+static char* oidc_jose_alg_to_openssl_digest(const char *alg) {
 	if ((strcmp(alg, CJOSE_HDR_ALG_RS256) == 0)
 			|| (strcmp(alg, CJOSE_HDR_ALG_PS256) == 0)
 			|| (strcmp(alg, CJOSE_HDR_ALG_HS256) == 0)
@@ -1100,8 +1101,8 @@
 		return FALSE;
 	}
 
-	return oidc_jose_hash_bytes(pool, s_digest, (const unsigned char *) msg,
-			strlen(msg), (unsigned char **) hash, hash_len, err);
+	return oidc_jose_hash_bytes(pool, s_digest, (const unsigned char*) msg,
+			strlen(msg), (unsigned char**) hash, hash_len, err);
 }
 
 /*
@@ -1183,7 +1184,7 @@
 						"oidc_jose_util_get_b64encoded_certificate");
 				goto end;
 			}
-			(*oidc_jwk)->x5c[0] = (unsigned char *) apr_pmemdup(pool,
+			(*oidc_jwk)->x5c[0] = (unsigned char*) apr_pmemdup(pool,
 					x509_pem_encoded_certificate, b64_len + 1);
 			(*oidc_jwk)->x5c_count = 1;
 			/* populate thumbprints entries */
@@ -1203,14 +1204,14 @@
 			}
 			/* populate x5t */
 			if (oidc_jose_hash_and_base64url_encode(pool, OIDC_JOSE_ALG_SHA1,
-					(const char *) x509_bytes, x509_cert_length,
+					(const char*) x509_bytes, x509_cert_length,
 					&(*oidc_jwk)->x5t) == FALSE) {
 				oidc_jose_error(err,
 						"oidc_jose_hash_and_base64urlencode failed");
 			}
 			/* populate x5t_S256 */
 			if (oidc_jose_hash_and_base64url_encode(pool, OIDC_JOSE_ALG_SHA256,
-					(const char *) x509_bytes, x509_cert_length,
+					(const char*) x509_bytes, x509_cert_length,
 					&(*oidc_jwk)->x5t_S256) == FALSE) {
 				oidc_jose_error(err,
 						"oidc_jose_hash_and_base64urlencode failed");
@@ -1236,7 +1237,7 @@
 					goto end;
 				}
 				(*oidc_jwk)->x5c[(*oidc_jwk)->x5c_count] =
-						(unsigned char *) apr_pmemdup(pool,
+						(unsigned char*) apr_pmemdup(pool,
 								x509_pem_encoded_certificate, b64_len + 1);
 				(*oidc_jwk)->x5c_count += 1;
 				X509_free(x509);
@@ -1442,8 +1443,8 @@
 /*
  * produce the string jwk representation from an oidc_jwk_t structure
  */
-static char *internal_cjose_jwk_to_json(apr_pool_t *pool, oidc_jwk_t *oidc_jwk,
-		oidc_jose_error_t *oidc_err) {
+static char* internal_cjose_jwk_to_json(apr_pool_t *pool,
+		const oidc_jwk_t *oidc_jwk, oidc_jose_error_t *oidc_err) {
 	char *result = NULL, *cjose_jwk_json;
 	cjose_err err;
 	json_t *json = NULL, *tempArray = NULL;
@@ -1480,7 +1481,7 @@
 		}
 		for (i = 0; i < oidc_jwk->x5c_count; i++) {
 			if (json_array_append_new(tempArray,
-					json_string((char *) oidc_jwk->x5c[i])) == -1) {
+					json_string((char*) oidc_jwk->x5c[i])) == -1) {
 				oidc_jose_error(oidc_err, "json_array_append failed");
 				goto to_json_cleanup;
 			}
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/jose.h libapache2-mod-auth-openidc-2.4.9/src/jose.h
--- libapache2-mod-auth-openidc-2.4.4.1/src/jose.h	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/jose.h	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -95,8 +87,8 @@
 /*
  * error handling functions
  */
-void _oidc_jose_error_set(oidc_jose_error_t *, const char *, const int,
-		const char *, const char *msg, ...);
+void _oidc_jose_error_set(oidc_jose_error_t*, const char*, const int,
+		const char*, const char *msg, ...);
 #define oidc_jose_error(err, msg, ...) _oidc_jose_error_set(err, __FILE__, __LINE__, __FUNCTION__, msg, ##__VA_ARGS__)
 #define oidc_jose_error_openssl(err, msg, ...) _oidc_jose_error_set(err, __FILE__, __LINE__, __FUNCTION__, "%s() failed: %s", msg, ERR_error_string(ERR_get_error(), NULL), ##__VA_ARGS__)
 #define oidc_jose_e2s(pool, err) apr_psprintf(pool, "[%s:%d: %s]: %s", err.source, err.line, err.function, err.text)
@@ -107,13 +99,13 @@
  */
 
 /* helpers to find out about the supported ala/enc algorithms */
-apr_array_header_t *oidc_jose_jws_supported_algorithms(apr_pool_t *pool);
+apr_array_header_t* oidc_jose_jws_supported_algorithms(apr_pool_t *pool);
 apr_byte_t oidc_jose_jws_algorithm_is_supported(apr_pool_t *pool,
 		const char *alg);
-apr_array_header_t *oidc_jose_jwe_supported_algorithms(apr_pool_t *pool);
+apr_array_header_t* oidc_jose_jwe_supported_algorithms(apr_pool_t *pool);
 apr_byte_t oidc_jose_jwe_algorithm_is_supported(apr_pool_t *pool,
 		const char *alg);
-apr_array_header_t *oidc_jose_jwe_supported_encryptions(apr_pool_t *pool);
+apr_array_header_t* oidc_jose_jwe_supported_encryptions(apr_pool_t *pool);
 apr_byte_t oidc_jose_jwe_encryption_is_supported(apr_pool_t *pool,
 		const char *enc);
 
@@ -170,20 +162,21 @@
 		apr_hash_t *keys, char **s_json, oidc_jose_error_t *err,
 		apr_byte_t import_must_succeed);
 /* parse a JSON string to a JWK struct */
-oidc_jwk_t *oidc_jwk_parse(apr_pool_t *pool, const char *s_json,
+oidc_jwk_t* oidc_jwk_parse(apr_pool_t *pool, const char *s_json,
 		oidc_jose_error_t *err);
 /* parse a JSON object in to a JWK struct */
 apr_byte_t oidc_jwk_parse_json(apr_pool_t *pool, json_t *json, oidc_jwk_t **jwk,
 		oidc_jose_error_t *err);
 /* convert a JWK struct to a JSON string */
-apr_byte_t oidc_jwk_to_json(apr_pool_t *pool, oidc_jwk_t *jwk, char **s_json,
-		oidc_jose_error_t *err);
+apr_byte_t oidc_jwk_to_json(apr_pool_t *pool, const oidc_jwk_t *jwk,
+		char **s_json, oidc_jose_error_t *err);
 /* destroy resources allocated for a JWK struct */
 void oidc_jwk_destroy(oidc_jwk_t *jwk);
 /* destroy a list of JWKs structs */
-void oidc_jwk_list_destroy(apr_pool_t *pool, apr_hash_t *key);
+void oidc_jwk_list_destroy_hash(apr_pool_t *pool, apr_hash_t *key);
+void oidc_jwk_list_destroy(apr_pool_t *pool, apr_array_header_t *keys_list);
 /* create an "oct" symmetric JWK */
-oidc_jwk_t *oidc_jwk_create_symmetric_key(apr_pool_t *pool, const char *kid,
+oidc_jwk_t* oidc_jwk_create_symmetric_key(apr_pool_t *pool, const char *kid,
 		const unsigned char *key, unsigned int key_len, apr_byte_t set_kid,
 		oidc_jose_error_t *err);
 
@@ -247,20 +240,20 @@
 apr_byte_t oidc_jwt_verify(apr_pool_t *pool, oidc_jwt_t *jwt, apr_hash_t *keys,
 		oidc_jose_error_t *err);
 /* perform compact serialization on a JWT and return the resulting string */
-char *oidc_jwt_serialize(apr_pool_t *pool, oidc_jwt_t *jwt,
+char* oidc_jwt_serialize(apr_pool_t *pool, oidc_jwt_t *jwt,
 		oidc_jose_error_t *err);
 /* encrypt JWT */
 apr_byte_t oidc_jwt_encrypt(apr_pool_t *pool, oidc_jwt_t *jwe, oidc_jwk_t *jwk,
 		const char *payload, char **serialized, oidc_jose_error_t *err);
 
 /* create a new JWT */
-oidc_jwt_t *oidc_jwt_new(apr_pool_t *pool, int create_header,
+oidc_jwt_t* oidc_jwt_new(apr_pool_t *pool, int create_header,
 		int create_payload);
 /* destroy resources allocated for JWT */
-void oidc_jwt_destroy(oidc_jwt_t *);
+void oidc_jwt_destroy(oidc_jwt_t*);
 
 /* get a header value from a JWT */
-const char *oidc_jwt_hdr_get(oidc_jwt_t *jwt, const char *key);
+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 */
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/metadata.c libapache2-mod-auth-openidc-2.4.9/src/metadata.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/metadata.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/metadata.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -127,6 +119,7 @@
 #define OIDC_METADATA_USERINFO_REFRESH_INTERVAL                    "userinfo_refresh_interval"
 #define OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_CERT               "token_endpoint_tls_client_cert"
 #define OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_KEY                "token_endpoint_tls_client_key"
+#define OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_KEY_PWD            "token_endpoint_tls_client_key_pwd"
 #define OIDC_METADATA_REQUEST_OBJECT                               "request_object"
 #define OIDC_METADATA_USERINFO_TOKEN_METHOD                        "userinfo_token_method"
 #define OIDC_METADATA_TOKEN_BINDING_POLICY                         "token_binding_policy"
@@ -136,7 +129,7 @@
 /*
  * get the metadata filename for a specified issuer (cq. urlencode it)
  */
-static const char *oidc_metadata_issuer_to_filename(request_rec *r,
+static const char* oidc_metadata_issuer_to_filename(request_rec *r,
 		const char *issuer) {
 
 	/* strip leading https:// */
@@ -163,7 +156,7 @@
 /*
  * get the issuer from a metadata filename (cq. urldecode it)
  */
-static const char *oidc_metadata_filename_to_issuer(request_rec *r,
+static const char* oidc_metadata_filename_to_issuer(request_rec *r,
 		const char *filename) {
 	char *result = apr_pstrdup(r->pool, filename);
 	char *p = strrchr(result, OIDC_CHAR_DOT);
@@ -175,7 +168,7 @@
 /*
  * get the full path to the metadata file for a specified issuer and directory
  */
-static const char *oidc_metadata_file_path(request_rec *r, oidc_cfg *cfg,
+static const char* oidc_metadata_file_path(request_rec *r, oidc_cfg *cfg,
 		const char *issuer, const char *type) {
 	return apr_psprintf(r->pool, "%s/%s.%s", cfg->metadata_dir,
 			oidc_metadata_issuer_to_filename(r, issuer), type);
@@ -184,7 +177,7 @@
 /*
  * get the full path to the provider metadata file for a specified issuer
  */
-static const char *oidc_metadata_provider_file_path(request_rec *r,
+static const char* oidc_metadata_provider_file_path(request_rec *r,
 		const char *issuer) {
 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
 			&auth_openidc_module);
@@ -195,7 +188,7 @@
 /*
  * get the full path to the client metadata file for a specified issuer
  */
-static const char *oidc_metadata_client_file_path(request_rec *r,
+static const char* oidc_metadata_client_file_path(request_rec *r,
 		const char *issuer) {
 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
 			&auth_openidc_module);
@@ -205,7 +198,7 @@
 /*
  * get the full path to the custom config file for a specified issuer
  */
-static const char *oidc_metadata_conf_path(request_rec *r, const char *issuer) {
+static const char* oidc_metadata_conf_path(request_rec *r, const char *issuer) {
 	oidc_cfg *cfg = ap_get_module_config(r->server->module_config,
 			&auth_openidc_module);
 	return oidc_metadata_file_path(r, cfg, issuer, OIDC_METADATA_SUFFIX_CONF);
@@ -214,7 +207,7 @@
 /*
  * get cache key for the JWKs file for a specified URI
  */
-static const char *oidc_metadata_jwks_cache_key(request_rec *r,
+static const char* oidc_metadata_jwks_cache_key(request_rec *r,
 		const char *jwks_uri) {
 	return jwks_uri;
 }
@@ -601,7 +594,7 @@
 			NULL, provider->registration_token, provider->ssl_validate_server, response,
 			cfg->http_timeout_short, cfg->outgoing_proxy,
 			oidc_dir_cfg_pass_cookies(r),
-			NULL, NULL) == FALSE) {
+			NULL, NULL, NULL) == FALSE) {
 		json_decref(data);
 		return FALSE;
 	}
@@ -629,7 +622,7 @@
 	if (oidc_util_http_get(r, jwks_uri->url, NULL, NULL,
 			NULL, jwks_uri->ssl_validate_server, &response, cfg->http_timeout_long,
 			cfg->outgoing_proxy, oidc_dir_cfg_pass_cookies(r), NULL,
-			NULL) == FALSE)
+			NULL, NULL) == FALSE)
 		return FALSE;
 
 	/* decode and see if it is not an error response somehow */
@@ -700,7 +693,7 @@
 			cfg->provider.ssl_validate_server, response,
 			cfg->http_timeout_short, cfg->outgoing_proxy,
 			oidc_dir_cfg_pass_cookies(r),
-			NULL, NULL) == FALSE)
+			NULL, NULL, NULL) == FALSE)
 		return FALSE;
 
 	/* decode and see if it is not an error response somehow */
@@ -1145,8 +1138,8 @@
 	*int_value = v;
 }
 
-void oidc_metadata_get_jwks(request_rec *r, json_t *json, const char *s_use,
-		apr_hash_t **jwk_list) {
+static void oidc_metadata_get_jwks(request_rec *r, json_t *json,
+		const char *s_use, apr_array_header_t **jwk_list) {
 	json_t *keys = NULL;
 	int i = 0;
 	oidc_jose_error_t err;
@@ -1184,8 +1177,8 @@
 		}
 
 		if (*jwk_list == NULL)
-			*jwk_list = apr_hash_make(r->pool);
-		apr_hash_set(*jwk_list, jwk->kid, APR_HASH_KEY_STRING, jwk);
+			*jwk_list = apr_array_make(r->pool, 4, sizeof(const oidc_jwk_t*));
+		*(const oidc_jwk_t**) apr_array_push(*jwk_list) = jwk;
 	}
 }
 
@@ -1333,6 +1326,10 @@
 			OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_KEY,
 			&provider->token_endpoint_tls_client_key,
 			cfg->provider.token_endpoint_tls_client_key);
+	oidc_json_object_get_string(r->pool, j_conf,
+			OIDC_METADATA_TOKEN_ENDPOINT_TLS_CLIENT_KEY_PWD,
+			&provider->token_endpoint_tls_client_key_pwd,
+			cfg->provider.token_endpoint_tls_client_key_pwd);
 
 	oidc_json_object_get_string(r->pool, j_conf, OIDC_METADATA_REQUEST_OBJECT,
 			&provider->request_object, cfg->provider.request_object);
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/mod_auth_openidc.c libapache2-mod-auth-openidc-2.4.9/src/mod_auth_openidc.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/mod_auth_openidc.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/mod_auth_openidc.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -97,16 +89,16 @@
 	const int prefix_len = claim_prefix ? strlen(claim_prefix) : 0;
 
 	/* get an array representation of the incoming HTTP headers */
-	const apr_array_header_t * const h = apr_table_elts(r->headers_in);
+	const apr_array_header_t *const h = apr_table_elts(r->headers_in);
 
 	/* table to keep the non-suspicious headers */
 	apr_table_t *clean_headers = apr_table_make(r->pool, h->nelts);
 
 	/* loop over the incoming HTTP headers */
-	const apr_table_entry_t * const e = (const apr_table_entry_t *) h->elts;
+	const apr_table_entry_t *const e = (const apr_table_entry_t*) h->elts;
 	int i;
 	for (i = 0; i < h->nelts; i++) {
-		const char * const k = e[i].key;
+		const char *const k = e[i].key;
 
 		/* is this header's name equivalent to a header that needs scrubbing? */
 		const char *hdr =
@@ -227,7 +219,7 @@
 /*
  * calculates a hash value based on request fingerprint plus a provided nonce string.
  */
-static char *oidc_get_browser_state_hash(request_rec *r, oidc_cfg *c,
+static char* oidc_get_browser_state_hash(request_rec *r, oidc_cfg *c,
 		const char *nonce) {
 
 	oidc_debug(r, "enter");
@@ -258,11 +250,11 @@
 
 	/* get the remote client IP address or host name */
 	/*
-	int remotehost_is_ip;
-	value = ap_get_remote_host(r->connection, r->per_dir_config,
-			REMOTE_NOLOOKUP, &remotehost_is_ip);
-	apr_sha1_update(&sha1, value, strlen(value));
-	*/
+	 int remotehost_is_ip;
+	 value = ap_get_remote_host(r->connection, r->per_dir_config,
+	 REMOTE_NOLOOKUP, &remotehost_is_ip);
+	 apr_sha1_update(&sha1, value, strlen(value));
+	 */
 
 	/* concat the nonce parameter to the hash input */
 	apr_sha1_update(&sha1, nonce, strlen(nonce));
@@ -281,14 +273,14 @@
 
 	/* base64url-encode the resulting hash and return it */
 	char *result = NULL;
-	oidc_base64url_encode(r, &result, (const char *) hash, OIDC_SHA1_LEN, TRUE);
+	oidc_base64url_encode(r, &result, (const char*) hash, OIDC_SHA1_LEN, TRUE);
 	return result;
 }
 
 /*
  * return the name for the state cookie
  */
-static char *oidc_get_state_cookie_name(request_rec *r, const char *state) {
+static char* oidc_get_state_cookie_name(request_rec *r, const char *state) {
 	return apr_psprintf(r->pool, "%s%s", oidc_cfg_dir_state_cookie_prefix(r),
 			state);
 }
@@ -354,7 +346,7 @@
 /*
  * return the oidc_provider_t struct for the specified issuer
  */
-static oidc_provider_t *oidc_get_provider_for_issuer(request_rec *r,
+static oidc_provider_t* oidc_get_provider_for_issuer(request_rec *r,
 		oidc_cfg *c, const char *issuer, apr_byte_t allow_discovery) {
 
 	/* by default we'll assume that we're dealing with a single statically configured OP */
@@ -395,7 +387,7 @@
 /*
  * return the HTTP method being called: only for POST data persistence purposes
  */
-static const char *oidc_original_request_method(request_rec *r, oidc_cfg *cfg,
+static const char* oidc_original_request_method(request_rec *r, oidc_cfg *cfg,
 		apr_byte_t handle_discovery_response) {
 	const char *method = OIDC_METHOD_GET;
 
@@ -476,7 +468,7 @@
 					"    </script>\n", jmethod, json,
 					location ?
 							apr_psprintf(r->pool, "window.location='%s';\n",
-									location) :
+									oidc_util_javascript_escape(r->pool, location)) :
 									"");
 	if (location == NULL) {
 		if (javascript_method)
@@ -521,10 +513,10 @@
 					"          input.type = \"hidden\";\n"
 					"          document.forms[0].appendChild(input);\n"
 					"        }\n"
-					"        document.forms[0].action = '%s';\n"
+					"        document.forms[0].action = \"%s\";\n"
 					"        document.forms[0].submit();\n"
 					"      }\n"
-					"    </script>\n", method, original_url);
+					"    </script>\n", method, oidc_util_javascript_escape(r->pool, original_url));
 
 	const char *body = "    <p>Restoring...</p>\n"
 			"    <form method=\"post\"></form>\n";
@@ -533,166 +525,6 @@
 			OK);
 }
 
-/*
- * parse state that was sent to us by the issuer
- */
-static apr_byte_t oidc_unsolicited_proto_state(request_rec *r, oidc_cfg *c,
-		const char *state, oidc_proto_state_t **proto_state) {
-
-	char *alg = NULL;
-	oidc_debug(r, "enter: state header=%s",
-			oidc_proto_peek_jwt_header(r, state, &alg));
-
-	oidc_jose_error_t err;
-	oidc_jwk_t *jwk = NULL;
-	if (oidc_util_create_symmetric_key(r, c->provider.client_secret,
-			oidc_alg2keysize(alg), OIDC_JOSE_ALG_SHA256, TRUE, &jwk) == FALSE)
-		return FALSE;
-
-	oidc_jwt_t *jwt = NULL;
-	if (oidc_jwt_parse(r->pool, state, &jwt,
-			oidc_util_merge_symmetric_key(r->pool, c->private_keys, jwk), &err)
-			== FALSE) {
-		oidc_error(r,
-				"could not parse JWT from state: invalid unsolicited response: %s",
-				oidc_jose_e2s(r->pool, err));
-		return FALSE;
-	}
-
-	oidc_jwk_destroy(jwk);
-	oidc_debug(r, "successfully parsed JWT from state");
-
-	if (jwt->payload.iss == NULL) {
-		oidc_error(r, "no \"%s\" could be retrieved from JWT state, aborting",
-				OIDC_CLAIM_ISS);
-		oidc_jwt_destroy(jwt);
-		return FALSE;
-	}
-
-	oidc_provider_t *provider = oidc_get_provider_for_issuer(r, c,
-			jwt->payload.iss, FALSE);
-	if (provider == NULL) {
-		oidc_jwt_destroy(jwt);
-		return FALSE;
-	}
-
-	/* validate the state JWT, validating optional exp + iat */
-	if (oidc_proto_validate_jwt(r, jwt,
-			provider->validate_issuer ? provider->issuer : NULL, FALSE, FALSE,
-					provider->idtoken_iat_slack,
-					OIDC_TOKEN_BINDING_POLICY_DISABLED) == FALSE) {
-		oidc_jwt_destroy(jwt);
-		return FALSE;
-	}
-
-	char *rfp = NULL;
-	if (oidc_jose_get_string(r->pool, jwt->payload.value.json, OIDC_CLAIM_RFP,
-			TRUE, &rfp, &err) == FALSE) {
-		oidc_error(r,
-				"no \"%s\" claim could be retrieved from JWT state, aborting: %s",
-				OIDC_CLAIM_RFP, oidc_jose_e2s(r->pool, err));
-		oidc_jwt_destroy(jwt);
-		return FALSE;
-	}
-
-	if (apr_strnatcmp(rfp, OIDC_PROTO_ISS) != 0) {
-		oidc_error(r, "\"%s\" (%s) does not match \"%s\", aborting",
-				OIDC_CLAIM_RFP, rfp, OIDC_PROTO_ISS);
-		oidc_jwt_destroy(jwt);
-		return FALSE;
-	}
-
-	char *target_link_uri = NULL;
-	oidc_jose_get_string(r->pool, jwt->payload.value.json,
-			OIDC_CLAIM_TARGET_LINK_URI, FALSE, &target_link_uri, NULL);
-	if (target_link_uri == NULL) {
-		if (c->default_sso_url == NULL) {
-			oidc_error(r,
-					"no \"%s\" claim could be retrieved from JWT state and no " OIDCDefaultURL " is set, aborting",
-					OIDC_CLAIM_TARGET_LINK_URI);
-			oidc_jwt_destroy(jwt);
-			return FALSE;
-		}
-		target_link_uri = c->default_sso_url;
-	}
-
-	if (c->metadata_dir != NULL) {
-		if ((oidc_metadata_get(r, c, jwt->payload.iss, &provider, FALSE)
-				== FALSE) || (provider == NULL)) {
-			oidc_error(r, "no provider metadata found for provider \"%s\"",
-					jwt->payload.iss);
-			oidc_jwt_destroy(jwt);
-			return FALSE;
-		}
-	}
-
-	char *jti = NULL;
-	oidc_jose_get_string(r->pool, jwt->payload.value.json, OIDC_CLAIM_JTI,
-			FALSE, &jti,
-			NULL);
-	if (jti == NULL) {
-		char *cser = oidc_jwt_serialize(r->pool, jwt, &err);
-		if (cser == NULL)
-			return FALSE;
-		if (oidc_util_hash_string_and_base64url_encode(r, OIDC_JOSE_ALG_SHA256,
-				cser, &jti) == FALSE) {
-			oidc_error(r,
-					"oidc_util_hash_string_and_base64url_encode returned an error");
-			return FALSE;
-		}
-	}
-
-	char *replay = NULL;
-	oidc_cache_get_jti(r, jti, &replay);
-	if (replay != NULL) {
-		oidc_error(r,
-				"the \"%s\" value (%s) passed in the browser state was found in the cache already; possible replay attack!?",
-				OIDC_CLAIM_JTI, jti);
-		oidc_jwt_destroy(jwt);
-		return FALSE;
-	}
-
-	/* jti cache duration is the configured replay prevention window for token issuance plus 10 seconds for safety */
-	apr_time_t jti_cache_duration = apr_time_from_sec(
-			provider->idtoken_iat_slack * 2 + 10);
-
-	/* store it in the cache for the calculated duration */
-	oidc_cache_set_jti(r, jti, jti, apr_time_now() + jti_cache_duration);
-
-	oidc_debug(r,
-			"jti \"%s\" validated successfully and is now cached for %" APR_TIME_T_FMT " seconds",
-			jti, apr_time_sec(jti_cache_duration));
-
-	jwk = NULL;
-	if (oidc_util_create_symmetric_key(r, c->provider.client_secret, 0,
-			NULL, TRUE, &jwk) == FALSE)
-		return FALSE;
-
-	oidc_jwks_uri_t jwks_uri = { provider->jwks_uri,
-			provider->jwks_refresh_interval, provider->ssl_validate_server };
-	if (oidc_proto_jwt_verify(r, c, jwt, &jwks_uri,
-			oidc_util_merge_symmetric_key(r->pool, NULL, jwk), NULL) == FALSE) {
-		oidc_error(r, "state JWT could not be validated, aborting");
-		oidc_jwt_destroy(jwt);
-		return FALSE;
-	}
-
-	oidc_jwk_destroy(jwk);
-	oidc_debug(r, "successfully verified state JWT");
-
-	*proto_state = oidc_proto_state_new();
-	oidc_proto_state_set_issuer(*proto_state, jwt->payload.iss);
-	oidc_proto_state_set_original_url(*proto_state, target_link_uri);
-	oidc_proto_state_set_original_method(*proto_state, OIDC_METHOD_GET);
-	oidc_proto_state_set_response_mode(*proto_state, provider->response_mode);
-	oidc_proto_state_set_response_type(*proto_state, provider->response_type);
-	oidc_proto_state_set_timestamp_now(*proto_state);
-
-	oidc_jwt_destroy(jwt);
-
-	return TRUE;
-}
-
 typedef struct oidc_state_cookies_t {
 	char *name;
 	apr_time_t timestamp;
@@ -721,7 +553,7 @@
 				"deleting oldest state cookie: %s (time until expiry %" APR_TIME_T_FMT " seconds)",
 				oldest->name, apr_time_sec(oldest->timestamp - apr_time_now()));
 		oidc_util_set_cookie(r, oldest->name, "", 0,
-				OIDC_COOKIE_EXT_SAME_SITE_NONE);
+				OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 		if (prev_oldest)
 			prev_oldest->next = oldest->next;
 		else
@@ -769,7 +601,7 @@
 										oidc_proto_state_get_original_url(
 												proto_state));
 								oidc_util_set_cookie(r, cookieName, "", 0,
-										OIDC_COOKIE_EXT_SAME_SITE_NONE);
+										OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 							} else {
 								if (first == NULL) {
 									first = apr_pcalloc(r->pool,
@@ -791,7 +623,7 @@
 									"state cookie could not be retrieved/decoded, deleting: %s",
 									cookieName);
 							oidc_util_set_cookie(r, cookieName, "", 0,
-									OIDC_COOKIE_EXT_SAME_SITE_NONE);
+									OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 						}
 					}
 				}
@@ -824,12 +656,15 @@
 	/* get the state cookie value first */
 	char *cookieValue = oidc_util_get_cookie(r, cookieName);
 	if (cookieValue == NULL) {
-		oidc_error(r, "no \"%s\" state cookie found", cookieName);
-		return oidc_unsolicited_proto_state(r, c, state, proto_state);
+		oidc_error(r,
+				"no \"%s\" state cookie found: check domain and samesite cookie settings",
+				cookieName);
+		return FALSE;
 	}
 
 	/* clear state cookie because we don't need it anymore */
-	oidc_util_set_cookie(r, cookieName, "", 0, OIDC_COOKIE_EXT_SAME_SITE_NONE);
+	oidc_util_set_cookie(r, cookieName, "", 0,
+			OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 
 	*proto_state = oidc_proto_state_from_cookie(r, c, cookieValue);
 	if (*proto_state == NULL)
@@ -853,16 +688,16 @@
 	/* check that the timestamp is not beyond the valid interval */
 	if (apr_time_now() > ts + apr_time_from_sec(c->state_timeout)) {
 		oidc_error(r, "state has expired");
-		/*
-		 * note that this overrides redirection to the OIDCDefaultURL as done later...
-		 * see: https://groups.google.com/forum/?utm_medium=email&utm_source=footer#!msg/mod_auth_openidc/L4JFBw-XCNU/BWi2Fmk2AwAJ
-		 */
-		oidc_util_html_send_error(r, c->error_template,
-				"Invalid Authentication Response",
-				apr_psprintf(r->pool,
-						"This is due to a timeout; please restart your authentication session by re-entering the URL/bookmark you originally wanted to access: %s",
-						oidc_proto_state_get_original_url(*proto_state)),
-						OK);
+		if ((c->default_sso_url == NULL)
+				|| (apr_table_get(r->subprocess_env, "OIDC_NO_DEFAULT_URL_ON_STATE_TIMEOUT") != NULL)) {
+			oidc_util_html_send_error(r, c->error_template, "Invalid Authentication Response", apr_psprintf(r->pool, "This is due to a timeout; please restart your authentication session by re-entering the URL/bookmark you originally wanted to access: %s", oidc_proto_state_get_original_url(*proto_state)),
+									  OK);
+			/*
+			 * a hack for Apache 2.4 to prevent it from writing its own 500/400/302 HTML document
+			 * text by making ap_send_error_response in http_protocol.c return early...
+			 */
+			r->header_only = 1;
+		}
 		oidc_proto_state_destroy(*proto_state);
 		return FALSE;
 	}
@@ -917,13 +752,13 @@
 		 * into a 200 so we'll avoid that for now: the user will see Apache specific
 		 * readable text anyway
 		 *
-		return oidc_util_html_send_error(r, c->error_template,
-				"Too Many Outstanding Requests",
-				apr_psprintf(r->pool,
-						"No authentication request could be generated since there are too many outstanding authentication requests already; you may have to wait up to %d seconds to be able to create a new request",
-						c->state_timeout),
-						HTTP_SERVICE_UNAVAILABLE);
-		*/
+		 return oidc_util_html_send_error(r, c->error_template,
+		 "Too Many Outstanding Requests",
+		 apr_psprintf(r->pool,
+		 "No authentication request could be generated since there are too many outstanding authentication requests already; you may have to wait up to %d seconds to be able to create a new request",
+		 c->state_timeout),
+		 HTTP_SERVICE_UNAVAILABLE);
+		 */
 
 		return HTTP_SERVICE_UNAVAILABLE;
 	}
@@ -933,23 +768,23 @@
 
 	/* set it as a cookie */
 	oidc_util_set_cookie(r, cookieName, cookieValue, -1,
-			OIDC_COOKIE_SAMESITE_LAX(c));
+			OIDC_COOKIE_SAMESITE_LAX(c, r));
 
-	return HTTP_OK;
+	return OK;
 }
 
 /*
  * get the mod_auth_openidc related context from the (userdata in the) request
  * (used for passing state between various Apache request processing stages and hook callbacks)
  */
-static apr_table_t *oidc_request_state(request_rec *rr) {
+static apr_table_t* oidc_request_state(request_rec *rr) {
 
 	/* our state is always stored in the main request */
 	request_rec *r = (rr->main != NULL) ? rr->main : rr;
 
 	/* our state is a table, get it */
 	apr_table_t *state = NULL;
-	apr_pool_userdata_get((void **) &state, OIDC_USERDATA_KEY, r->pool);
+	apr_pool_userdata_get((void**) &state, OIDC_USERDATA_KEY, r->pool);
 
 	/* if it does not exist, we'll create a new table */
 	if (state == NULL) {
@@ -978,7 +813,7 @@
  * get a name/value pair from the mod_auth_openidc-specific request context
  * (used for passing state between various Apache request processing stages and hook callbacks)
  */
-const char*oidc_request_state_get(request_rec *r, const char *key) {
+const char* oidc_request_state_get(request_rec *r, const char *key) {
 
 	/* get a handle to the global state, which is a table */
 	apr_table_t *state = oidc_request_state(r);
@@ -991,9 +826,8 @@
  * set the claims from a JSON object (c.q. id_token or user_info response) stored
  * in the session in to HTTP headers passed on to the application
  */
-static apr_byte_t oidc_set_app_claims(request_rec *r,
-		const oidc_cfg * const cfg, oidc_session_t *session,
-		const char *s_claims) {
+static apr_byte_t oidc_set_app_claims(request_rec *r, const oidc_cfg *const cfg,
+		oidc_session_t *session, const char *s_claims) {
 
 	json_t *j_claims = NULL;
 
@@ -1007,7 +841,8 @@
 	if (j_claims != NULL) {
 		oidc_util_set_app_infos(r, j_claims, oidc_cfg_claim_prefix(r),
 				cfg->claim_delimiter, oidc_cfg_dir_pass_info_in_headers(r),
-				oidc_cfg_dir_pass_info_in_envvars(r));
+				oidc_cfg_dir_pass_info_in_envvars(r),
+				oidc_cfg_dir_pass_info_base64url(r));
 
 		/* release resources */
 		json_decref(j_claims);
@@ -1157,7 +992,7 @@
 	/* get the issuer value from the session state */
 	const char *issuer = oidc_session_get_issuer(r, session);
 	if (issuer == NULL) {
-		oidc_error(r, "session corrupted: no issuer found in session");
+		oidc_warn(r, "empty or invalid session: no issuer found");
 		return FALSE;
 	}
 
@@ -1264,7 +1099,7 @@
 /*
  * retrieve claims from the userinfo endpoint and return the stringified response
  */
-static const char *oidc_retrieve_claims_from_userinfo_endpoint(request_rec *r,
+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, char **userinfo_jwt) {
 
@@ -1431,13 +1266,14 @@
 
 	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);
+	apr_byte_t pass_base64url = oidc_cfg_dir_pass_info_base64url(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);
 	if ((oidc_cfg_dir_pass_refresh_token(r) != 0) && (refresh_token != NULL)) {
 		/* pass it to the app in a header or environment variable */
 		oidc_util_set_app_info(r, OIDC_APP_INFO_REFRESH_TOKEN, refresh_token,
-				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
+				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars, pass_base64url);
 	}
 
 	/* set the access_token in the app headers/variables */
@@ -1445,7 +1281,7 @@
 	if (access_token != NULL) {
 		/* pass it to the app in a header or environment variable */
 		oidc_util_set_app_info(r, OIDC_APP_INFO_ACCESS_TOKEN, access_token,
-				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
+				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars, pass_base64url);
 	}
 
 	/* set the expiry timestamp in the app headers/variables */
@@ -1455,7 +1291,8 @@
 		/* pass it to the app in a header or environment variable */
 		oidc_util_set_app_info(r, OIDC_APP_INFO_ACCESS_TOKEN_EXP,
 				access_token_expires,
-				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
+				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars,
+				pass_base64url);
 	}
 
 	/*
@@ -1561,6 +1398,7 @@
 	char *authn_header = oidc_cfg_dir_authn_header(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);
+	apr_byte_t pass_base64url = oidc_cfg_dir_pass_info_base64url(r);
 
 	/* verify current cookie domain against issued cookie domain */
 	if (oidc_check_cookie_domain(r, cfg, session) == FALSE)
@@ -1612,7 +1450,7 @@
 	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);
+				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars, pass_base64url);
 	}
 
 	if ((cfg->pass_userinfo_as & OIDC_PASS_USERINFO_AS_JWT)) {
@@ -1624,7 +1462,8 @@
 				/* 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);
+						OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars,
+						pass_base64url);
 			} 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)");
@@ -1644,7 +1483,7 @@
 	if ((cfg->pass_idtoken_as & OIDC_PASS_IDTOKEN_AS_PAYLOAD)) {
 		/* pass the id_token JSON object to the app in a header or environment variable */
 		oidc_util_set_app_info(r, OIDC_APP_INFO_ID_TOKEN_PAYLOAD, s_id_token,
-				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
+				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars, pass_base64url);
 	}
 
 	if ((cfg->pass_idtoken_as & OIDC_PASS_IDTOKEN_AS_SERIALIZED)) {
@@ -1653,7 +1492,8 @@
 			const char *s_id_token = oidc_session_get_idtoken(r, session);
 			/* pass the compact serialized JWT to the app in a header or environment variable */
 			oidc_util_set_app_info(r, OIDC_APP_INFO_ID_TOKEN, s_id_token,
-					OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
+					OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars,
+					pass_base64url);
 		} else {
 			oidc_error(r,
 					"session type \"client-cookie\" does not allow storing/passing the id_token; use \"" OIDCSessionType " server-cache\" for that");
@@ -1693,7 +1533,13 @@
 	*provider = oidc_get_provider_for_issuer(r, c,
 			oidc_proto_state_get_issuer(*proto_state), FALSE);
 
-	return (*provider != NULL);
+	if (*provider == NULL) {
+		oidc_proto_state_destroy(*proto_state);
+		*proto_state = NULL;
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
 /*
@@ -1707,7 +1553,7 @@
 	char *java_script = apr_psprintf(r->pool,
 			"    <script type=\"text/javascript\">\n"
 			"      window.top.location.href = '%s?session=logout';\n"
-			"    </script>\n", oidc_get_redirect_uri(r, c));
+			"    </script>\n", oidc_util_javascript_escape(r->pool, oidc_get_redirect_uri(r, c)));
 
 	return oidc_util_html_send(r, "Redirecting...", java_script, NULL, NULL,
 			OK);
@@ -1834,7 +1680,7 @@
 	return TRUE;
 }
 
-static char *oidc_make_sid_iss_unique(request_rec *r, const char *sid,
+static char* oidc_make_sid_iss_unique(request_rec *r, const char *sid,
 		const char *issuer) {
 	return apr_psprintf(r->pool, "%s@%s", sid, issuer);
 }
@@ -2066,6 +1912,7 @@
 					"invalid authorization response state; a default SSO URL is set, sending the user there: %s",
 					c->default_sso_url);
 			oidc_util_hdr_out_location_set(r, c->default_sso_url);
+			//oidc_util_hdr_err_out_add(r, "Location", c->default_sso_url));
 			return HTTP_MOVED_TEMPORARILY;
 		}
 		oidc_error(r,
@@ -2073,7 +1920,8 @@
 		// if content was already returned via html/http send then don't return 500
 		// but send 200 to avoid extraneous internal error document text to be sent
 		return ((r->user) && (strncmp(r->user, "", 1) == 0)) ?
-				OK : HTTP_INTERNAL_SERVER_ERROR;
+				OK :
+				HTTP_BAD_REQUEST;
 	}
 
 	/* see if the response is an error response */
@@ -2142,11 +1990,15 @@
 				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,
-				userinfo_jwt) == FALSE)
+				userinfo_jwt) == FALSE) {
+			oidc_proto_state_destroy(proto_state);
+			oidc_jwt_destroy(jwt);
 			return HTTP_INTERNAL_SERVER_ERROR;
+		}
 
 	} else {
 		oidc_error(r, "remote user could not be set");
+		oidc_jwt_destroy(jwt);
 		return oidc_authorization_response_error(r, c, proto_state,
 				"Remote user could not be set: contact the website administrator",
 				NULL);
@@ -2209,7 +2061,7 @@
 	}
 
 	/* get the parameters */
-	response_mode = (char *) apr_table_get(params, OIDC_PROTO_RESPONSE_MODE);
+	response_mode = (char*) apr_table_get(params, OIDC_PROTO_RESPONSE_MODE);
 
 	/* do the actual implicit work */
 	return oidc_handle_authorization_response(r, c, session, params,
@@ -2280,7 +2132,7 @@
 
 		/* set CSRF cookie */
 		oidc_util_set_cookie(r, OIDC_CSRF_NAME, csrf, -1,
-				OIDC_COOKIE_SAMESITE_STRICT(cfg));
+				OIDC_COOKIE_SAMESITE_STRICT(cfg, r));
 
 		/* see if we need to preserve POST parameters through Javascript/HTML5 storage */
 		if (oidc_post_preserve_javascript(r, url, NULL, NULL) == TRUE)
@@ -2373,7 +2225,7 @@
 	s = apr_psprintf(r->pool, "%s</form>\n", s);
 
 	oidc_util_set_cookie(r, OIDC_CSRF_NAME, csrf, -1,
-			OIDC_COOKIE_SAMESITE_STRICT(cfg));
+			OIDC_COOKIE_SAMESITE_STRICT(cfg, r));
 
 	char *javascript = NULL, *javascript_method = NULL;
 	char *html_head =
@@ -2400,8 +2252,17 @@
 	if (provider == NULL) {
 
 		// TODO: should we use an explicit redirect to the discovery endpoint (maybe a "discovery" param to the redirect_uri)?
-		if (c->metadata_dir != NULL)
-			return oidc_discovery(r, c);
+		if (c->metadata_dir != NULL) {
+			/*
+			 * Will be handled in the content handler; avoid:
+			 * No authentication done but request not allowed without authentication
+			 * by setting r->user
+			 */
+			oidc_debug(r, "defer discovery to the content handler");
+			oidc_request_state_set(r, OIDC_REQUEST_STATE_KEY_DISCOVERY, "");
+			r->user = "";
+			return OK;
+		}
 
 		/* we're not using multiple OP's configured in a metadata directory, pick the statically configured OP */
 		if (oidc_provider_static_config(r, c, &provider) == FALSE)
@@ -2453,7 +2314,7 @@
 	 * and cryptographically bind it to the browser
 	 */
 	int rc = oidc_authorization_request_set_cookie(r, c, state, proto_state);
-	if (rc != HTTP_OK) {
+	if (rc != OK) {
 		oidc_proto_state_destroy(proto_state);
 		return rc;
 	}
@@ -2595,7 +2456,7 @@
 
 		/* clean CSRF cookie */
 		oidc_util_set_cookie(r, OIDC_CSRF_NAME, "", 0,
-				OIDC_COOKIE_EXT_SAME_SITE_NONE);
+				OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 
 		/* compare CSRF cookie value with query parameter value */
 		if ((csrf_query == NULL)
@@ -2769,29 +2630,30 @@
 	// TODO: use oauth.ssl_validate_server ...
 	token = oidc_session_get_refresh_token(r, session);
 	if (token != NULL) {
-		apr_table_addn(params, "token_type_hint", "refresh_token");
-		apr_table_addn(params, "token", token);
+		apr_table_setn(params, OIDC_PROTO_TOKEN_TYPE_HINT, OIDC_PROTO_REFRESH_TOKEN);
+		apr_table_setn(params, OIDC_PROTO_TOKEN, token);
 
 		if (oidc_util_http_post_form(r, provider->revocation_endpoint_url,
 				params, basic_auth, bearer_auth, c->oauth.ssl_validate_server,
 				&response, c->http_timeout_long, c->outgoing_proxy,
 				oidc_dir_cfg_pass_cookies(r), NULL,
-				NULL) == FALSE) {
+				NULL, NULL) == FALSE) {
 			oidc_warn(r, "revoking refresh token failed");
 		}
-		apr_table_clear(params);
+		apr_table_unset(params, OIDC_PROTO_TOKEN_TYPE_HINT);
+		apr_table_unset(params, OIDC_PROTO_TOKEN);
 	}
 
 	token = oidc_session_get_access_token(r, session);
 	if (token != NULL) {
-		apr_table_addn(params, "token_type_hint", "access_token");
-		apr_table_addn(params, "token", token);
+		apr_table_setn(params, OIDC_PROTO_TOKEN_TYPE_HINT, OIDC_PROTO_ACCESS_TOKEN);
+		apr_table_setn(params, OIDC_PROTO_TOKEN, token);
 
 		if (oidc_util_http_post_form(r, provider->revocation_endpoint_url,
 				params, basic_auth, bearer_auth, c->oauth.ssl_validate_server,
 				&response, c->http_timeout_long, c->outgoing_proxy,
 				oidc_dir_cfg_pass_cookies(r), NULL,
-				NULL) == FALSE) {
+				NULL, NULL) == FALSE) {
 			oidc_warn(r, "revoking access token failed");
 		}
 	}
@@ -2835,17 +2697,14 @@
 		const char *accept = oidc_util_hdr_in_accept_get(r);
 		if ((apr_strnatcmp(url, OIDC_IMG_STYLE_LOGOUT_PARAM_VALUE) == 0)
 				|| ((accept) && strstr(accept, OIDC_CONTENT_TYPE_IMAGE_PNG))) {
-			// terminate with DONE instead of OK
-			// to avoid Apache returning auth/authz error 401 for the redirect URI
-			return oidc_util_http_send(r,
-					(const char *) &oidc_transparent_pixel,
+			return oidc_util_http_send(r, (const char*) &oidc_transparent_pixel,
 					sizeof(oidc_transparent_pixel), OIDC_CONTENT_TYPE_IMAGE_PNG,
-					DONE);
+					OK);
 		}
 
 		/* standard HTTP based logout: should be called in an iframe from the OP */
 		return oidc_util_html_send(r, "Logged Out", NULL, NULL,
-				"<p>Logged Out</p>", DONE);
+				"<p>Logged Out</p>", OK);
 	}
 
 	/* see if we don't need to go somewhere special after killing the session locally */
@@ -2893,7 +2752,6 @@
 	}
 
 	// TODO: jwk symmetric key based on provider
-	// TODO: share more code with regular id_token validation and unsolicited state
 
 	if (oidc_jwt_parse(r->pool, logout_token, &jwt,
 			oidc_util_merge_symmetric_key(r->pool, cfg->private_keys, NULL),
@@ -2902,6 +2760,11 @@
 		goto out;
 	}
 
+	if ((jwt->header.alg == NULL) || (strcmp(jwt->header.alg, "none") == 0)) {
+		oidc_error(r, "logout token is not signed");
+		goto out;
+	}
+
 	provider = oidc_get_provider_for_issuer(r, cfg, jwt->payload.iss, FALSE);
 	if (provider == NULL) {
 		oidc_error(r, "no provider found for issuer: %s", jwt->payload.iss);
@@ -3015,12 +2878,12 @@
 	sid = oidc_make_sid_iss_unique(r, sid, provider->issuer);
 	oidc_cache_get_sid(r, sid, &uuid);
 	if (uuid == NULL) {
-		oidc_error(r,
-				"could not find session based on sid/sub provided in logout token: %s",
+		// this may happen when we are the caller
+		oidc_warn(r,
+				"could not (or no longer) find a session based on sid/sub provided in logout token: %s",
 				sid);
-		// return HTTP 200 according to (new?) spec and terminate early
-		// to avoid Apache returning auth/authz error 500 for the redirect URI
-		rc = DONE;
+		r->user = "";
+		rc = OK;
 		goto out;
 	}
 
@@ -3035,9 +2898,8 @@
 	oidc_cache_set_sid(r, sid, NULL, 0);
 	oidc_cache_set_session(r, uuid, NULL, 0);
 
-	// terminate with DONE instead of OK
-	// to avoid Apache returning auth/authz error 500 for the redirect URI
-	rc = DONE;
+	r->user = "";
+	rc = OK;
 
 out:
 
@@ -3058,12 +2920,21 @@
 	return rc;
 }
 
+#define OIDC_MAX_URL_LENGTH DEFAULT_LIMIT_REQUEST_LINE * 2
+
 static apr_byte_t oidc_validate_redirect_url(request_rec *r, oidc_cfg *c,
-		const char *url, apr_byte_t restrict_to_host, char **err_str,
+		const char *redirect_to_url, apr_byte_t restrict_to_host, char **err_str,
 		char **err_desc) {
 	apr_uri_t uri;
 	const char *c_host = NULL;
 	apr_hash_index_t *hi = NULL;
+	size_t i = 0;
+	char *url = apr_pstrndup(r->pool, redirect_to_url, OIDC_MAX_URL_LENGTH);
+
+	// replace potentially harmful backslashes with forward slashes
+	for (i = 0; i < strlen(url); i++)
+		if (url[i] == '\\')
+			url[i] = '/';
 
 	if (apr_uri_parse(r->pool, url, &uri) != APR_SUCCESS) {
 		*err_str = apr_pstrdup(r->pool, "Malformed URL");
@@ -3202,6 +3073,9 @@
 									OIDC_STR_QUERY,
 									oidc_util_escape_string(r, url));
 		}
+		//char *state = NULL;
+		//oidc_proto_generate_nonce(r, &state, 8);
+		//url = apr_psprintf(r->pool, "%s&state=%s", logout_request, state);
 		url = logout_request;
 	}
 
@@ -3217,22 +3091,18 @@
 	//	char *jwks_type = NULL;
 	//	oidc_util_get_request_parameter(r, OIDC_REDIRECT_URI_REQUEST_JWKS, &jwks_type);
 	char *jwks = apr_pstrdup(r->pool, "{ \"keys\" : [");
-	apr_hash_index_t *hi = NULL;
+	int i = 0;
 	apr_byte_t first = TRUE;
 	oidc_jose_error_t err;
 
 	if (c->public_keys != NULL) {
 
 		/* loop over the RSA public keys */
-		for (hi = apr_hash_first(r->pool, c->public_keys); hi; hi =
-				apr_hash_next(hi)) {
-
-			const char *s_kid = NULL;
-			oidc_jwk_t *jwk = NULL;
+		for (i = 0; i < c->public_keys->nelts; i++) {
+			const oidc_jwk_t *jwk =
+					((const oidc_jwk_t**) c->public_keys->elts)[i];
 			char *s_json = NULL;
 
-			apr_hash_this(hi, (const void**) &s_kid, NULL, (void**) &jwk);
-
 			if (oidc_jwk_to_json(r->pool, jwk, &s_json, &err) == TRUE) {
 				jwks = apr_psprintf(r->pool, "%s%s %s ", jwks, first ? "" : ",",
 						s_json);
@@ -3689,6 +3559,29 @@
 			json_object_set_new(json, OIDC_HOOK_INFO_USER_INFO, claims);
 	}
 
+	/* include the maximum session lifetime in the session info */
+	if (apr_hash_get(c->info_hook_data, OIDC_HOOK_INFO_SESSION_EXP,
+			APR_HASH_KEY_STRING)) {
+		apr_time_t session_expires = oidc_session_get_session_expires(r,
+				session);
+		json_object_set_new(json, OIDC_HOOK_INFO_SESSION_EXP,
+				json_integer(apr_time_sec(session_expires)));
+	}
+
+	/* include the inactivity timeout in the session info */
+	if (apr_hash_get(c->info_hook_data, OIDC_HOOK_INFO_SESSION_TIMEOUT,
+			APR_HASH_KEY_STRING)) {
+		json_object_set_new(json, OIDC_HOOK_INFO_SESSION_TIMEOUT,
+				json_integer(apr_time_sec(session->expiry)));
+	}
+
+	/* include the remote_user in the session info */
+	if (apr_hash_get(c->info_hook_data, OIDC_HOOK_INFO_SESSION_REMOTE_USER,
+			APR_HASH_KEY_STRING)) {
+		json_object_set_new(json, OIDC_HOOK_INFO_SESSION_REMOTE_USER,
+				json_string(session->remote_user));
+	}
+
 	if (apr_hash_get(c->info_hook_data, OIDC_HOOK_INFO_SESSION,
 			APR_HASH_KEY_STRING)) {
 		json_t *j_session = json_object();
@@ -3696,14 +3589,6 @@
 				session->state);
 		json_object_set_new(j_session, OIDC_HOOK_INFO_SESSION_UUID,
 				json_string(session->uuid));
-		json_object_set_new(j_session, OIDC_HOOK_INFO_SESSION_TIMEOUT,
-				json_integer(apr_time_sec(session->expiry)));
-		apr_time_t session_expires = oidc_session_get_session_expires(r,
-				session);
-		json_object_set_new(j_session, OIDC_HOOK_INFO_SESSION_EXP,
-				json_integer(apr_time_sec(session_expires)));
-		json_object_set_new(j_session, OIDC_HOOK_INFO_SESSION_REMOTE_USER,
-				json_string(session->remote_user));
 		json_object_set_new(json, OIDC_HOOK_INFO_SESSION, j_session);
 
 	}
@@ -3754,9 +3639,6 @@
 int oidc_handle_redirect_uri_request(request_rec *r, oidc_cfg *c,
 		oidc_session_t *session) {
 
-	/* track if the session needs to be updated/saved into the cache */
-	apr_byte_t needs_save = FALSE;
-
 	if (oidc_proto_is_redirect_authorization_response(r, c)) {
 
 		/* this is an authorization response from the OP using the Basic Client profile or a Hybrid flow*/
@@ -3787,9 +3669,13 @@
 
 	} else if (oidc_util_request_has_parameter(r,
 			OIDC_REDIRECT_URI_REQUEST_JWKS)) {
-
-		/* handle JWKs request */
-		return oidc_handle_jwks(r, c);
+		/*
+		 * Will be handled in the content handler; avoid:
+		 * No authentication done but request not allowed without authentication
+		 * by setting r->user
+		 */
+		r->user = "";
+		return OK;
 
 	} else if (oidc_util_request_has_parameter(r,
 			OIDC_REDIRECT_URI_REQUEST_SESSION)) {
@@ -3821,12 +3707,13 @@
 		if (session->remote_user == NULL)
 			return HTTP_UNAUTHORIZED;
 
-		/* set r->user, set headers/env-vars, update expiry, update userinfo + AT */
-		int rc = oidc_handle_existing_session(r, c, session, &needs_save);
-		if (rc != OK)
-			return rc;
-
-		return oidc_handle_info_request(r, c, session, needs_save);
+		/*
+		 * Will be handled in the content handler; avoid:
+		 * No authentication done but request not allowed without authentication
+		 * by setting r->user
+		 */
+		r->user = "";
+		return OK;
 
 	} else if ((r->args == NULL) || (apr_strnatcmp(r->args, "") == 0)) {
 
@@ -3844,7 +3731,7 @@
 		//		oidc_util_get_request_parameter(r, "error_description", &descr);
 		//
 		//		/* send user facing error to browser */
-		//		return oidc_util_html_send_error(r, error, descr, DONE);
+		//		return oidc_util_html_send_error(r, error, descr, OK);
 		return oidc_handle_redirect_authorization_response(r, c, session);
 	}
 
@@ -3875,59 +3762,7 @@
 	}
 
 	/* check if this is a sub-request or an initial request */
-	if (ap_is_initial_req(r)) {
-
-		int rc = OK;
-		apr_byte_t needs_save = FALSE;
-
-		/* load the session from the request state; this will be a new "empty" session if no state exists */
-		oidc_session_t *session = NULL;
-		oidc_session_load(r, &session);
-
-		/* see if the initial request is to the redirect URI; this handles potential logout too */
-		if (oidc_util_request_matches_url(r, oidc_get_redirect_uri(r, c))) {
-
-			/* handle request to the redirect_uri */
-			rc = oidc_handle_redirect_uri_request(r, c, session);
-
-			/* free resources allocated for the session */
-			oidc_session_free(r, session);
-
-			return rc;
-
-			/* initial request to non-redirect URI, check if we have an existing session */
-		} else if (session->remote_user != NULL) {
-
-			/* this is initial request and we already have a session */
-			rc = oidc_handle_existing_session(r, c, session, &needs_save);
-			if (rc == OK) {
-
-				/* check if something was updated in the session and we need to save it again */
-				if (needs_save) {
-					if (oidc_session_save(r, session, FALSE) == FALSE) {
-						oidc_warn(r, "error saving session");
-						rc = HTTP_INTERNAL_SERVER_ERROR;
-					}
-				}
-			}
-
-			/* free resources allocated for the session */
-			oidc_session_free(r, session);
-
-			/* strip any cookies that we need to */
-			oidc_strip_cookies(r);
-
-			return rc;
-		}
-
-		/* free resources allocated for the session */
-		oidc_session_free(r, session);
-
-		/*
-		 * else: initial request, we have no session and it is not an authorization or
-		 *       discovery response: just hit the default flow for unauthenticated users
-		 */
-	} else {
+	if (!ap_is_initial_req(r)) {
 
 		/* not an initial request, try to recycle what we've already established in the main request */
 		if (r->main != NULL)
@@ -3966,10 +3801,61 @@
 		}
 		/*
 		 * else: not initial request, but we could not find a session, so:
-		 * just hit the default flow for unauthenticated users
+		 * try to load a new session as if this were the initial request
 		 */
 	}
 
+	int rc = OK;
+	apr_byte_t needs_save = FALSE;
+
+	/* load the session from the request state; this will be a new "empty" session if no state exists */
+	oidc_session_t *session = NULL;
+	oidc_session_load(r, &session);
+
+	/* see if the initial request is to the redirect URI; this handles potential logout too */
+	if (oidc_util_request_matches_url(r, oidc_get_redirect_uri(r, c))) {
+
+		/* handle request to the redirect_uri */
+		rc = oidc_handle_redirect_uri_request(r, c, session);
+
+		/* free resources allocated for the session */
+		oidc_session_free(r, session);
+
+		return rc;
+
+		/* initial request to non-redirect URI, check if we have an existing session */
+	} else if (session->remote_user != NULL) {
+
+		/* this is initial request and we already have a session */
+		rc = oidc_handle_existing_session(r, c, session, &needs_save);
+		if (rc == OK) {
+
+			/* check if something was updated in the session and we need to save it again */
+			if (needs_save) {
+				if (oidc_session_save(r, session, FALSE) == FALSE) {
+					oidc_warn(r, "error saving session");
+					rc = HTTP_INTERNAL_SERVER_ERROR;
+				}
+			}
+		}
+
+		/* free resources allocated for the session */
+		oidc_session_free(r, session);
+
+		/* strip any cookies that we need to */
+		oidc_strip_cookies(r);
+
+		return rc;
+	}
+
+	/* free resources allocated for the session */
+	oidc_session_free(r, session);
+
+	/*
+	 * else: we have no session and it is not an authorization or
+	 *       discovery response: just hit the default flow for unauthenticated users
+	 */
+
 	return oidc_handle_unauthenticated_user(r, c);
 }
 
@@ -3980,10 +3866,14 @@
 
 	/* get the bearer access token from the Authorization header */
 	const char *access_token = NULL;
-	if (oidc_oauth_get_bearer_token(r, &access_token) == TRUE)
+	if (oidc_oauth_get_bearer_token(r, &access_token) == TRUE) {
+
+		r->ap_auth_type = apr_pstrdup(r->pool, OIDC_AUTH_TYPE_OPENID_OAUTH20);
 		return oidc_oauth_check_userid(r, c, access_token);
+	}
 
 	/* no bearer token found: then treat this as a regular OIDC browser request */
+	r->ap_auth_type = apr_pstrdup(r->pool, OIDC_AUTH_TYPE_OPENID_CONNECT);
 	return oidc_check_userid_openidc(r, c);
 }
 
@@ -4000,22 +3890,26 @@
 			r->parsed_uri.path, r->args, ap_is_initial_req(r));
 
 	/* see if any authentication has been defined at all */
-	if (ap_auth_type(r) == NULL)
+	const char *current_auth = ap_auth_type(r);
+	if (current_auth == NULL)
 		return DECLINED;
 
 	/* see if we've configured OpenID Connect user authentication for this request */
-	if (apr_strnatcasecmp((const char *) ap_auth_type(r),
-			OIDC_AUTH_TYPE_OPENID_CONNECT) == 0)
+	if (strcasecmp(current_auth, OIDC_AUTH_TYPE_OPENID_CONNECT) == 0) {
+
+		r->ap_auth_type = (char*) current_auth;
 		return oidc_check_userid_openidc(r, c);
+	}
 
 	/* see if we've configured OAuth 2.0 access control for this request */
-	if (apr_strnatcasecmp((const char *) ap_auth_type(r),
-			OIDC_AUTH_TYPE_OPENID_OAUTH20) == 0)
+	if (strcasecmp(current_auth, OIDC_AUTH_TYPE_OPENID_OAUTH20) == 0) {
+
+		r->ap_auth_type = (char*) current_auth;
 		return oidc_oauth_check_userid(r, c, NULL);
+	}
 
 	/* see if we've configured "mixed mode" for this request */
-	if (apr_strnatcasecmp((const char *) ap_auth_type(r),
-			OIDC_AUTH_TYPE_OPENID_BOTH) == 0)
+	if (strcasecmp(current_auth, OIDC_AUTH_TYPE_OPENID_BOTH) == 0)
 		return oidc_check_mixed_userid_oauth(r, c);
 
 	/* this is not for us but for some other handler */
@@ -4041,6 +3935,9 @@
 
 #if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
 
+#define OIDC_OAUTH_BEARER_SCOPE_ERROR "OIDC_OAUTH_BEARER_SCOPE_ERROR"
+#define OIDC_OAUTH_BEARER_SCOPE_ERROR_VALUE "Bearer error=\"insufficient_scope\", error_description=\"Different scope(s) or other claims required\""
+
 /*
  * find out which action we need to take when encountering an unauthorized request
  */
@@ -4048,48 +3945,49 @@
 
 	oidc_debug(r, "enter");
 
-	oidc_cfg *c = ap_get_module_config(r->server->module_config,
-			&auth_openidc_module);
+	oidc_cfg *c = ap_get_module_config(r->server->module_config, &auth_openidc_module);
 
-	if (apr_strnatcasecmp((const char *) ap_auth_type(r),
-			OIDC_AUTH_TYPE_OPENID_OAUTH20) == 0) {
-		oidc_oauth_return_www_authenticate(r, "insufficient_scope",
-				"Different scope(s) or other claims required");
+	if (apr_strnatcasecmp((const char*) ap_auth_type(r),
+						  OIDC_AUTH_TYPE_OPENID_OAUTH20) == 0) {
+		oidc_debug(r, "setting environment variable %s to \"%s\" for usage in mod_headers", OIDC_OAUTH_BEARER_SCOPE_ERROR, OIDC_OAUTH_BEARER_SCOPE_ERROR_VALUE);
+		apr_table_set(r->subprocess_env, OIDC_OAUTH_BEARER_SCOPE_ERROR, OIDC_OAUTH_BEARER_SCOPE_ERROR_VALUE);
 		return AUTHZ_DENIED;
 	}
 
 	/* see if we've configured OIDCUnAutzAction for this path */
 	switch (oidc_dir_cfg_unautz_action(r)) {
-	// TODO: document that AuthzSendForbiddenOnFailure is required to return 403 FORBIDDEN
-	case OIDC_UNAUTZ_RETURN403:
-	case OIDC_UNAUTZ_RETURN401:
-		return AUTHZ_DENIED;
-		break;
-	case OIDC_UNAUTZ_AUTHENTICATE:
-		/*
-		 * exception handling: if this looks like a XMLHttpRequest call we
-		 * won't redirect the user and thus avoid creating a state cookie
-		 * for a non-browser (= Javascript) call that will never return from the OP
-		 */
-		if (oidc_is_xml_http_request(r) == TRUE)
+		// TODO: document that AuthzSendForbiddenOnFailure is required to return 403 FORBIDDEN
+		case OIDC_UNAUTZ_RETURN403:
+		case OIDC_UNAUTZ_RETURN401:
 			return AUTHZ_DENIED;
-		break;
+			break;
+		case OIDC_UNAUTZ_AUTHENTICATE:
+			/*
+			 * exception handling: if this looks like a XMLHttpRequest call we
+			 * won't redirect the user and thus avoid creating a state cookie
+			 * for a non-browser (= Javascript) call that will never return from the OP
+			 */
+			if (oidc_is_xml_http_request(r) == TRUE)
+				return AUTHZ_DENIED;
+			break;
 	}
 
 	oidc_authenticate_user(r, c, NULL, oidc_get_current_url(r), NULL,
-			NULL, NULL, oidc_dir_cfg_path_auth_request_params(r),
-			oidc_dir_cfg_path_scope(r));
+			NULL, NULL, oidc_dir_cfg_path_auth_request_params(r), oidc_dir_cfg_path_scope(r));
 
 	const char *location = oidc_util_hdr_out_location_get(r);
 	if (location != NULL) {
-		oidc_debug(r, "send HTML refresh with authorization redirect: %s",
-				location);
+		oidc_debug(r, "send HTML refresh with authorization redirect: %s", location);
 
-		char *html_head = apr_psprintf(r->pool,
-				"<meta http-equiv=\"refresh\" content=\"0; url=%s\">",
-				location);
+		char *html_head =
+				apr_psprintf(r->pool, "<meta http-equiv=\"refresh\" content=\"0; url=%s\">", location);
 		oidc_util_html_send(r, "Stepup Authentication", html_head, NULL, NULL,
 				HTTP_UNAUTHORIZED);
+		/*
+		 * a hack for Apache 2.4 to prevent it from writing its own 401 HTML document
+		 * text by making ap_send_error_response in http_protocol.c return early...
+		 */
+		r->header_only = 1;
 	}
 
 	return AUTHZ_DENIED;
@@ -4103,7 +4001,7 @@
 		const void *parsed_require_args,
 		oidc_authz_match_claim_fn_type match_claim_fn) {
 
-	oidc_debug(r, "enter");
+	oidc_debug(r, "enter: require_args=\"%s\"", require_args);
 
 	/* check for anonymous access and PASS mode */
 	if (r->user != NULL && strlen(r->user) == 0) {
@@ -4238,15 +4136,15 @@
 	if (ap_auth_type(r) == NULL)
 		return FALSE;
 
-	if (apr_strnatcasecmp((const char *) ap_auth_type(r),
+	if (apr_strnatcasecmp((const char*) ap_auth_type(r),
 			OIDC_AUTH_TYPE_OPENID_CONNECT) == 0)
 		return TRUE;
 
-	if (apr_strnatcasecmp((const char *) ap_auth_type(r),
+	if (apr_strnatcasecmp((const char*) ap_auth_type(r),
 			OIDC_AUTH_TYPE_OPENID_OAUTH20) == 0)
 		return TRUE;
 
-	if (apr_strnatcasecmp((const char *) ap_auth_type(r),
+	if (apr_strnatcasecmp((const char*) ap_auth_type(r),
 			OIDC_AUTH_TYPE_OPENID_BOTH) == 0)
 		return TRUE;
 
@@ -4256,12 +4154,55 @@
  * handle content generating requests
  */
 int oidc_content_handler(request_rec *r) {
-	if (oidc_enabled(r) == FALSE)
-		return DECLINED;
 	oidc_cfg *c = ap_get_module_config(r->server->module_config,
 			&auth_openidc_module);
-	return oidc_util_request_matches_url(r, oidc_get_redirect_uri(r, c)) ?
-			OK : DECLINED;
+	int rc = DECLINED;
+	/* track if the session needs to be updated/saved into the cache */
+	apr_byte_t needs_save = FALSE;
+	oidc_session_t *session = NULL;
+
+	if (oidc_enabled(r) == TRUE) {
+
+		if (oidc_util_request_matches_url(r, oidc_get_redirect_uri(r, c)) == TRUE) {
+
+			if (oidc_util_request_has_parameter(r,
+					OIDC_REDIRECT_URI_REQUEST_INFO)) {
+
+				oidc_session_load(r, &session);
+
+				rc = oidc_handle_existing_session(r, c, session, &needs_save);
+				if (rc == OK)
+					/* handle request for session info */
+					rc = oidc_handle_info_request(r, c, session, needs_save);
+
+				/* free resources allocated for the session */
+				oidc_session_free(r, session);
+
+			} else if (oidc_util_request_has_parameter(r,
+					OIDC_REDIRECT_URI_REQUEST_JWKS)) {
+
+				/* handle JWKs request */
+				rc = oidc_handle_jwks(r, c);
+
+			} else {
+
+				rc = OK;
+
+			}
+
+		} else if (oidc_request_state_get(r, OIDC_REQUEST_STATE_KEY_DISCOVERY) != NULL) {
+
+			rc = oidc_discovery(r, c);
+
+		} else if (oidc_request_state_get(r, OIDC_REQUEST_STATE_KEY_AUTHN) != NULL) {
+
+			rc = OK;
+
+		}
+
+	}
+
+	return rc;
 }
 
 extern const command_rec oidc_config_cmds[];
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/mod_auth_openidc.h libapache2-mod-auth-openidc-2.4.9/src/mod_auth_openidc.h
--- libapache2-mod-auth-openidc-2.4.4.1/src/mod_auth_openidc.h	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/mod_auth_openidc.h	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -95,6 +87,8 @@
 /* keys for storing info in the request state */
 #define OIDC_REQUEST_STATE_KEY_IDTOKEN "i"
 #define OIDC_REQUEST_STATE_KEY_CLAIMS  "c"
+#define OIDC_REQUEST_STATE_KEY_DISCOVERY  "d"
+#define OIDC_REQUEST_STATE_KEY_AUTHN  "a"
 
 /* parameter name of the callback URL in the discovery response */
 #define OIDC_DISC_CB_PARAM "oidc_callback"
@@ -216,12 +210,13 @@
 
 #define OIDC_COOKIE_EXT_SAME_SITE_LAX    "SameSite=Lax"
 #define OIDC_COOKIE_EXT_SAME_SITE_STRICT "SameSite=Strict"
-#define OIDC_COOKIE_EXT_SAME_SITE_NONE   "SameSite=None"
+#define OIDC_COOKIE_EXT_SAME_SITE_NONE(r) \
+		oidc_util_request_is_secure(r) ? "SameSite=None" : NULL
 
-#define OIDC_COOKIE_SAMESITE_STRICT(c) \
-	c->cookie_same_site ? OIDC_COOKIE_EXT_SAME_SITE_STRICT : OIDC_COOKIE_EXT_SAME_SITE_NONE
-#define OIDC_COOKIE_SAMESITE_LAX(c) \
-	c->cookie_same_site ? OIDC_COOKIE_EXT_SAME_SITE_LAX : OIDC_COOKIE_EXT_SAME_SITE_NONE
+#define OIDC_COOKIE_SAMESITE_STRICT(c, r) \
+		c->cookie_same_site ? OIDC_COOKIE_EXT_SAME_SITE_STRICT : OIDC_COOKIE_EXT_SAME_SITE_NONE(r)
+#define OIDC_COOKIE_SAMESITE_LAX(c, r) \
+		c->cookie_same_site ? OIDC_COOKIE_EXT_SAME_SITE_LAX : OIDC_COOKIE_EXT_SAME_SITE_NONE(r)
 
 /* https://tools.ietf.org/html/draft-ietf-tokbind-ttrp-01 */
 #define OIDC_TB_CFG_PROVIDED_ENV_VAR     "Sec-Provided-Token-Binding-ID"
@@ -273,6 +268,7 @@
 	char *client_id;
 	char *client_secret;
 	char *token_endpoint_tls_client_key;
+	char *token_endpoint_tls_client_key_pwd;
 	char *token_endpoint_tls_client_cert;
 	int backchannel_logout_supported;
 
@@ -293,8 +289,8 @@
 	oidc_proto_pkce_t *pkce;
 	int userinfo_refresh_interval;
 
-	apr_hash_t *client_signing_keys;
-	apr_hash_t *client_encryption_keys;
+	apr_array_header_t *client_signing_keys;
+	apr_array_header_t *client_encryption_keys;
 
 	char *client_jwks_uri;
 	char *id_token_signed_response_alg;
@@ -323,6 +319,7 @@
 	char *client_secret;
 	char *metadata_url;
 	char *introspection_endpoint_tls_client_key;
+	char *introspection_endpoint_tls_client_key_pwd;
 	char *introspection_endpoint_tls_client_cert;
 	char *introspection_endpoint_url;
 	char *introspection_endpoint_method;
@@ -355,9 +352,9 @@
 	char *default_slo_url;
 
 	/* public keys in JWK format, used by parters for encrypting JWTs sent to us */
-	apr_hash_t *public_keys;
+	apr_array_header_t *public_keys;
 	/* private keys in JWK format used for decrypting encrypted JWTs sent to us */
-	apr_hash_t *private_keys;
+	apr_array_header_t *private_keys;
 
 	/* a pointer to the (single) provider that we connect to */
 	/* NB: if metadata_dir is set, these settings will function as defaults for the metadata read from there) */
@@ -393,6 +390,9 @@
 	/* cache_type= redis: Redis host/port server to use */
 	char *cache_redis_server;
 	char *cache_redis_password;
+	int cache_redis_database;
+	int cache_redis_connect_timeout;
+	int cache_redis_timeout;
 #endif
 	int cache_encrypt;
 
@@ -426,6 +426,8 @@
 	apr_byte_t state_input_headers;
 
 	apr_hash_t *redirect_urls_allowed;
+
+	char *ca_bundle_path;
 } oidc_cfg;
 
 int oidc_check_user_id(request_rec *r);
@@ -478,6 +480,8 @@
 #define OIDC_PROTO_SCOPE                 "scope"
 #define OIDC_PROTO_REFRESH_TOKEN         "refresh_token"
 #define OIDC_PROTO_TOKEN_TYPE            "token_type"
+#define OIDC_PROTO_TOKEN_TYPE_HINT       "token_type_hint"
+#define OIDC_PROTO_TOKEN                 "token"
 #define OIDC_PROTO_EXPIRES_IN            "expires_in"
 #define OIDC_PROTO_RESPONSE_TYPE         "response_type"
 #define OIDC_PROTO_RESPONSE_MODE         "response_mode"
@@ -635,7 +639,7 @@
 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, apr_hash_t *client_signing_keys, const char *audience, apr_table_t *params, const char *bearer_access_token, char **basic_auth_str, char **bearer_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 apr_array_header_t *client_signing_keys, const char *audience, apr_table_t *params, const char *bearer_access_token, 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);
@@ -709,6 +713,7 @@
 char *oidc_cfg_dir_authn_header(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_info_base64url(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);
@@ -743,11 +748,12 @@
 const char *oidc_get_redirect_uri_iss(request_rec *r, oidc_cfg *c, oidc_provider_t *provider);
 char *oidc_url_encode(const request_rec *r, const char *str, const char *charsToEncode);
 char *oidc_normalize_header_name(const request_rec *r, const char *str);
+apr_byte_t oidc_util_request_is_secure(request_rec *r);
 void oidc_util_set_cookie(request_rec *r, const char *cookieName, const char *cookieValue, apr_time_t expires, const char *ext);
 char *oidc_util_get_cookie(request_rec *r, const char *cookieName);
-apr_byte_t oidc_util_http_get(request_rec *r, const char *url, const apr_table_t *params, const char *basic_auth, const char *bearer_token, int ssl_validate_server, char **response, int timeout, const char *outgoing_proxy, apr_array_header_t *pass_cookies, const char *ssl_cert, const char *ssl_key);
-apr_byte_t oidc_util_http_post_form(request_rec *r, const char *url, const apr_table_t *params, const char *basic_auth, const char *bearer_token, int ssl_validate_server, char **response, int timeout, const char *outgoing_proxy, apr_array_header_t *pass_cookies, const char *ssl_cert, const char *ssl_key);
-apr_byte_t oidc_util_http_post_json(request_rec *r, const char *url, json_t *data, const char *basic_auth, const char *bearer_token, int ssl_validate_server, char **response, int timeout, const char *outgoing_proxy, apr_array_header_t *pass_cookies, const char *ssl_cert, const char *ssl_key);
+apr_byte_t oidc_util_http_get(request_rec *r, const char *url, const apr_table_t *params, const char *basic_auth, const char *bearer_token, int ssl_validate_server, char **response, int timeout, const char *outgoing_proxy, apr_array_header_t *pass_cookies, const char *ssl_cert, const char *ssl_key, const char *ssl_key_pwd);
+apr_byte_t oidc_util_http_post_form(request_rec *r, const char *url, const apr_table_t *params, const char *basic_auth, const char *bearer_token, int ssl_validate_server, char **response, int timeout, const char *outgoing_proxy, apr_array_header_t *pass_cookies, const char *ssl_cert, const char *ssl_key, const char *ssl_key_pwd);
+apr_byte_t oidc_util_http_post_json(request_rec *r, const char *url, json_t *data, const char *basic_auth, const char *bearer_token, int ssl_validate_server, char **response, int timeout, const char *outgoing_proxy, apr_array_header_t *pass_cookies, const char *ssl_cert, const char *ssl_key, const char *ssl_key_pwd);
 apr_byte_t oidc_util_request_matches_url(request_rec *r, const char *url);
 apr_byte_t oidc_util_request_has_parameter(request_rec *r, const char* param);
 apr_byte_t oidc_util_get_request_parameter(request_rec *r, char *name, char **value);
@@ -765,8 +771,8 @@
 apr_byte_t oidc_util_issuer_match(const char *a, const char *b);
 int oidc_util_html_send_error(request_rec *r, const char *html_template, const char *error, const char *description, int status_code);
 apr_byte_t oidc_util_json_array_has_value(request_rec *r, json_t *haystack, const char *needle);
-void oidc_util_set_app_info(request_rec *r, const char *s_key, const char *s_value, const char *claim_prefix, apr_byte_t as_header, apr_byte_t as_env_var);
-void oidc_util_set_app_infos(request_rec *r, const json_t *j_attrs, const char *claim_prefix, const char *claim_delimiter, apr_byte_t as_header, apr_byte_t as_env_var);
+void oidc_util_set_app_info(request_rec *r, const char *s_key, const char *s_value, const char *claim_prefix, apr_byte_t as_header, apr_byte_t as_env_var, apr_byte_t base64url);
+void oidc_util_set_app_infos(request_rec *r, const json_t *j_attrs, const char *claim_prefix, const char *claim_delimiter, apr_byte_t as_header, apr_byte_t as_env_var, apr_byte_t base64url);
 apr_hash_t *oidc_util_spaced_string_to_hashtable(apr_pool_t *pool, const char *str);
 apr_byte_t oidc_util_spaced_string_equals(apr_pool_t *pool, const char *a, const char *b);
 apr_byte_t oidc_util_spaced_string_contains(apr_pool_t *pool, const char *str, const char *match);
@@ -774,19 +780,21 @@
 apr_byte_t oidc_json_object_get_int(apr_pool_t *pool, json_t *json, const char *name, int *value, const int default_value);
 apr_byte_t oidc_json_object_get_bool(apr_pool_t *pool, json_t *json, const char *name, int *value, const int default_value);
 char *oidc_util_html_escape(apr_pool_t *pool, const char *input);
+char *oidc_util_javascript_escape(apr_pool_t *pool, const char *input);
 void oidc_util_table_add_query_encoded_params(apr_pool_t *pool, apr_table_t *table, const char *params);
-apr_hash_t * oidc_util_merge_key_sets(apr_pool_t *pool, apr_hash_t *k1, apr_hash_t *k2);
+apr_hash_t * oidc_util_merge_key_sets(apr_pool_t *pool, apr_hash_t *k1, const apr_array_header_t *k2);
+apr_hash_t * oidc_util_merge_key_sets_hash(apr_pool_t *pool, apr_hash_t *k1, apr_hash_t *k2);
 apr_byte_t oidc_util_regexp_substitute(apr_pool_t *pool, const char *input, const char *regexp, const char *replace, char **output, char **error_str);
 apr_byte_t oidc_util_regexp_first_match(apr_pool_t *pool, const char *input, const char *regexp, char **output, char **error_str);
 apr_byte_t oidc_util_json_merge(request_rec *r, json_t *src, json_t *dst);
 int oidc_util_cookie_domain_valid(const char *hostname, char *cookie_domain);
 apr_byte_t oidc_util_hash_string_and_base64url_encode(request_rec *r, const char *openssl_hash_algo, const char *input, char **output);
-apr_byte_t oidc_util_jwt_create(request_rec *r, const char *secret, json_t *payload, char **compact_encoded_jwt);
-apr_byte_t oidc_util_jwt_verify(request_rec *r, const char *secret, const char *compact_encoded_jwt, json_t **result);
+apr_byte_t oidc_util_jwt_create(request_rec *r, const char *secret, json_t *payload, char **compact_encoded_jwt, apr_byte_t strip_header);
+apr_byte_t oidc_util_jwt_verify(request_rec *r, const char *secret, const char *compact_encoded_jwt, json_t **result, apr_byte_t stripped_header);
 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, 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);
+apr_hash_t * oidc_util_merge_symmetric_key(apr_pool_t *pool, const apr_array_header_t *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);
 char *oidc_util_get_full_path(apr_pool_t *pool, const char *abs_or_rel_filename);
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/oauth.c libapache2-mod-auth-openidc-2.4.9/src/oauth.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/oauth.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/oauth.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -67,7 +59,7 @@
 	if (oidc_util_http_get(r, url, NULL, NULL, NULL,
 			cfg->oauth.ssl_validate_server, response, cfg->http_timeout_short,
 			cfg->outgoing_proxy, oidc_dir_cfg_pass_cookies(r),
-			NULL, NULL) == FALSE)
+			NULL, NULL, NULL) == FALSE)
 		return FALSE;
 
 	/* decode and see if it is not an error response somehow */
@@ -177,17 +169,28 @@
 	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, bearer_auth, 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_get_full_path(r->pool,
+									c->oauth.introspection_endpoint_tls_client_key_pwd)
+					) :
+					oidc_util_http_post_form(r, c->oauth.introspection_endpoint_url,
+							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),
-							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, 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_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_get_full_path(r->pool,
+									c->oauth.introspection_endpoint_tls_client_key_pwd)
+					);
 }
 
 /*
@@ -302,35 +305,6 @@
 }
 
 /*
- * copy over space separated scope value but do it in an array for authorization purposes
- */
-/*
-static void oidc_oauth_spaced_string_to_array(request_rec *r, json_t *src,
-		const char *src_key, json_t *dst, const char *dst_key) {
-	apr_hash_t *ht = NULL;
-	apr_hash_index_t *hi = NULL;
-	json_t *arr = NULL;
-
-	json_t *src_val = json_object_get(src, src_key);
-
-	if (src_val != NULL)
-		ht = oidc_util_spaced_string_to_hashtable(r->pool,
-				json_string_value(src_val));
-
-	if (ht != NULL) {
-		arr = json_array();
-		for (hi = apr_hash_first(NULL, ht); hi; hi = apr_hash_next(hi)) {
-			const char *k;
-			const char *v;
-			apr_hash_this(hi, (const void**) &k, NULL, (void**) &v);
-			json_array_append_new(arr, json_string(v));
-		}
-		json_object_set_new(dst, dst_key, arr);
-	}
-}
-*/
-
-/*
  * parse (custom/configurable) token expiry claim in introspection result
  */
 static apr_byte_t oidc_oauth_parse_and_cache_token_expiry(request_rec *r,
@@ -389,6 +363,13 @@
 static apr_byte_t oidc_oauth_cache_access_token(request_rec *r, oidc_cfg *c,
 		apr_time_t cache_until, const char *access_token, json_t *json) {
 
+	/* no cache mode */
+	int token_introspection_interval = oidc_cfg_token_introspection_interval(r);
+	if (token_introspection_interval == -1) {
+		oidc_debug(r, "not caching introspection result");
+		return TRUE;
+	}
+
 	oidc_debug(r, "caching introspection result");
 
 	json_t *cache_entry = json_object();
@@ -411,6 +392,12 @@
 	json_t *cache_entry = NULL;
 	char *s_cache_entry = NULL;
 
+	/* no cache mode */
+	int token_introspection_interval = oidc_cfg_token_introspection_interval(r);
+	if (token_introspection_interval == -1) {
+		return FALSE;
+	}
+
 	/* see if we've got the claims for this access_token cached already */
 	oidc_cache_get_access_token(r, access_token, &s_cache_entry);
 
@@ -426,7 +413,6 @@
 	/* compare the timestamp against the freshness requirement */
 	json_t *v = json_object_get(cache_entry, OIDC_OAUTH_CACHE_KEY_TIMESTAMP);
 	apr_time_t now = apr_time_sec(apr_time_now());
-	int token_introspection_interval = oidc_cfg_token_introspection_interval(r);
 	if ((token_introspection_interval > 0)
 			&& (now > json_integer_value(v) + token_introspection_interval)) {
 
@@ -641,7 +627,7 @@
 	oidc_jwks_uri_t jwks_uri = { c->oauth.verify_jwks_uri,
 			c->provider.jwks_refresh_interval, c->oauth.ssl_validate_server };
 	if (oidc_proto_jwt_verify(r, c, jwt, &jwks_uri,
-			oidc_util_merge_key_sets(r->pool, c->oauth.verify_public_keys,
+			oidc_util_merge_key_sets_hash(r->pool, c->oauth.verify_public_keys,
 					c->oauth.verify_shared_keys), NULL) == FALSE) {
 		oidc_error(r,
 				"JWT access token signature could not be validated, aborting");
@@ -807,7 +793,7 @@
 
 	/* store the parsed token (cq. the claims from the response) in the request state so it can be accessed by the authz routines */
 	oidc_request_state_set(r, OIDC_REQUEST_STATE_KEY_CLAIMS,
-			(const char *) s_token);
+			(const char*) s_token);
 
 	/* set the request user */
 	if (oidc_oauth_set_request_user(r, c, token) == FALSE) {
@@ -828,18 +814,19 @@
 	char *authn_header = oidc_cfg_dir_authn_header(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);
+	apr_byte_t pass_base64url = oidc_cfg_dir_pass_info_base64url(r);
 
 	if ((r->user != NULL) && (authn_header != NULL))
 		oidc_util_hdr_in_set(r, authn_header, r->user);
 
 	/* set the resolved claims in the HTTP headers for the target application */
 	oidc_util_set_app_infos(r, token, oidc_cfg_claim_prefix(r),
-			c->claim_delimiter, pass_headers, pass_envvars);
+			c->claim_delimiter, pass_headers, pass_envvars, pass_base64url);
 
 	/* set the access_token in the app headers */
 	if (access_token != NULL) {
 		oidc_util_set_app_info(r, OIDC_APP_INFO_ACCESS_TOKEN, access_token,
-				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars);
+				OIDC_DEFAULT_HEADER_PREFIX, pass_headers, pass_envvars, pass_base64url);
 	}
 
 	/* free JSON resources */
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/parse.c libapache2-mod-auth-openidc-2.4.9/src/parse.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/parse.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/parse.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -1195,6 +1187,9 @@
 			OIDC_HOOK_INFO_ID_TOKEN,
 			OIDC_HOOK_INFO_USER_INFO,
 			OIDC_HOOK_INFO_REFRESH_TOKEN,
+			OIDC_HOOK_INFO_SESSION_EXP,
+			OIDC_HOOK_INFO_SESSION_TIMEOUT,
+			OIDC_HOOK_INFO_SESSION_REMOTE_USER,
 			OIDC_HOOK_INFO_SESSION,
 			NULL };
 	const char *rv = oidc_valid_string_option(pool, arg, options);
@@ -1356,4 +1351,4 @@
 	}
 
 	return NULL;
-}
\ Kein Zeilenumbruch am Dateiende.
+}
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/parse.h libapache2-mod-auth-openidc-2.4.9/src/parse.h
--- libapache2-mod-auth-openidc-2.4.4.1/src/parse.h	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/parse.h	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/proto.c libapache2-mod-auth-openidc-2.4.9/src/proto.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/proto.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/proto.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -136,8 +128,7 @@
 		oidc_error(r, "oidc_proto_generate_random_bytes returned an error");
 		return FALSE;
 	}
-	if (oidc_base64url_encode(r, output, (const char *) bytes, len, TRUE)
-			<= 0) {
+	if (oidc_base64url_encode(r, output, (const char*) bytes, len, TRUE) <= 0) {
 		oidc_error(r, "oidc_base64url_encode returned an error");
 		return FALSE;
 	}
@@ -179,9 +170,9 @@
  * copy a parameter key/value from the authorizion request to the
  * request object if the configuration setting says to include it
  */
-static int oidc_proto_copy_from_request(void* rec, const char* name,
-		const char* value) {
-	oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t *) rec;
+static int oidc_proto_copy_from_request(void *rec, const char *name,
+		const char *value) {
+	oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t*) rec;
 
 	oidc_debug(ctx->r, "processing name: %s, value: %s", name, value);
 
@@ -214,9 +205,9 @@
 /*
  * delete a parameter key/value from the authorizion request if the configuration setting says to remove it
  */
-static int oidc_proto_delete_from_request(void* rec, const char* name,
-		const char* value) {
-	oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t *) rec;
+static int oidc_proto_delete_from_request(void *rec, const char *name,
+		const char *value) {
+	oidc_proto_copy_req_ctx_t *ctx = (oidc_proto_copy_req_ctx_t*) rec;
 
 	oidc_debug(ctx->r, "deleting from query parameters: name: %s, value: %s",
 			name, value);
@@ -296,17 +287,22 @@
 	return (*jwk != NULL);
 }
 
+static oidc_jwk_t* oidc_key_list_first(const apr_array_header_t *key_list) {
+	oidc_jwk_t *rv = NULL;
+	if ((key_list) && (key_list->nelts > 0))
+		rv = ((oidc_jwk_t**) key_list->elts)[0];
+	return rv;
+}
+
 /*
  * generate a request object
  */
-char *oidc_proto_create_request_object(request_rec *r,
-		struct oidc_provider_t *provider, json_t * request_object_config,
+char* oidc_proto_create_request_object(request_rec *r,
+		struct oidc_provider_t *provider, json_t *request_object_config,
 		apr_table_t *params) {
 
-	apr_ssize_t klen = 0;
-	oidc_jwk_t *jwk = NULL;
+	oidc_jwk_t *sjwk = NULL;
 	int jwk_needs_destroy = 0;
-	apr_hash_index_t *hi = NULL;
 
 	oidc_debug(r, "enter");
 
@@ -352,19 +348,17 @@
 	/* see if we need to sign the request object */
 	if (strcmp(request_object->header.alg, "none") != 0) {
 
-		jwk = NULL;
+		sjwk = NULL;
 		jwk_needs_destroy = 0;
-		klen = 0;
 
 		switch (oidc_jwt_alg2kty(request_object)) {
 		case CJOSE_JWK_KTY_RSA:
 			if ((provider->client_signing_keys != NULL)
 					|| (cfg->private_keys != NULL)) {
-				hi = provider->client_signing_keys ?
-						apr_hash_first(r->pool, provider->client_signing_keys) :
-						apr_hash_first(r->pool, cfg->private_keys);
-				apr_hash_this(hi, (const void **) &request_object->header.kid,
-						&klen, (void **) &jwk);
+				sjwk = provider->client_signing_keys ?
+						oidc_key_list_first(provider->client_signing_keys) :
+						oidc_key_list_first(cfg->private_keys);
+				request_object->header.kid = apr_pstrdup(r->pool, sjwk->kid);
 			} else {
 				oidc_error(r,
 						"no global or per-provider private keys have been configured to use for request object signing");
@@ -372,7 +366,7 @@
 			break;
 		case CJOSE_JWK_KTY_OCT:
 			oidc_util_create_symmetric_key(r, provider->client_secret, 0, NULL,
-					FALSE, &jwk);
+					FALSE, &sjwk);
 			jwk_needs_destroy = 1;
 			break;
 		default:
@@ -382,24 +376,24 @@
 			break;
 		}
 
-		if (jwk == NULL) {
+		if (sjwk == NULL) {
 			oidc_jwt_destroy(request_object);
 			json_decref(request_object_config);
 			return FALSE;
 		}
 
-		if (oidc_jwt_sign(r->pool, request_object, jwk, &err) == FALSE) {
+		if (oidc_jwt_sign(r->pool, request_object, sjwk, &err) == FALSE) {
 			oidc_error(r, "signing Request Object failed: %s",
 					oidc_jose_e2s(r->pool, err));
 			if (jwk_needs_destroy)
-				oidc_jwk_destroy(jwk);
+				oidc_jwk_destroy(sjwk);
 			oidc_jwt_destroy(request_object);
 			json_decref(request_object_config);
 			return FALSE;
 		}
 
 		if (jwk_needs_destroy)
-			oidc_jwk_destroy(jwk);
+			oidc_jwk_destroy(sjwk);
 	}
 
 	oidc_jwt_t *jwe = oidc_jwt_new(r->pool, TRUE, FALSE);
@@ -420,17 +414,17 @@
 	/* see if we need to encrypt the request object */
 	if (jwe->header.alg != NULL) {
 
-		oidc_jwk_t *jwk = NULL;
+		oidc_jwk_t *ejwk = NULL;
 
 		switch (oidc_jwt_alg2kty(jwe)) {
 		case CJOSE_JWK_KTY_RSA:
 			oidc_proto_get_encryption_jwk_by_type(r, cfg, provider,
-					CJOSE_JWK_KTY_RSA, &jwk);
+					CJOSE_JWK_KTY_RSA, &ejwk);
 			break;
 		case CJOSE_JWK_KTY_OCT:
 			oidc_util_create_symmetric_key(r, provider->client_secret,
 					oidc_alg2keysize(jwe->header.alg), OIDC_JOSE_ALG_SHA256,
-					FALSE, &jwk);
+					FALSE, &ejwk);
 			break;
 		default:
 			oidc_error(r,
@@ -439,7 +433,7 @@
 			break;
 		}
 
-		if (jwk == NULL) {
+		if (ejwk == NULL) {
 			oidc_jwt_destroy(jwe);
 			oidc_jwt_destroy(request_object);
 			json_decref(request_object_config);
@@ -449,21 +443,21 @@
 		if (jwe->header.enc == NULL)
 			jwe->header.enc = apr_pstrdup(r->pool, CJOSE_HDR_ENC_A128CBC_HS256);
 
-		if (jwk->kid != NULL)
-			jwe->header.kid = jwk->kid;
+		if (ejwk->kid != NULL)
+			jwe->header.kid = ejwk->kid;
 
-		if (oidc_jwt_encrypt(r->pool, jwe, jwk, cser,
+		if (oidc_jwt_encrypt(r->pool, jwe, ejwk, cser,
 				&serialized_request_object, &err) == FALSE) {
 			oidc_error(r, "encrypting JWT failed: %s",
 					oidc_jose_e2s(r->pool, err));
-			oidc_jwk_destroy(jwk);
+			oidc_jwk_destroy(ejwk);
 			oidc_jwt_destroy(jwe);
 			oidc_jwt_destroy(request_object);
 			json_decref(request_object_config);
 			return FALSE;
 		}
 
-		oidc_jwk_destroy(jwk);
+		oidc_jwk_destroy(ejwk);
 
 	} else {
 
@@ -486,8 +480,8 @@
 /*
  * generate a request object and pass it by reference in the authorization request
  */
-static char *oidc_proto_create_request_uri(request_rec *r,
-		struct oidc_provider_t *provider, json_t * request_object_config,
+static char* oidc_proto_create_request_uri(request_rec *r,
+		struct oidc_provider_t *provider, json_t *request_object_config,
 		const char *redirect_uri, apr_table_t *params) {
 
 	oidc_debug(r, "enter");
@@ -534,13 +528,13 @@
 		return;
 
 	/* request_uri is used as default parameter for sending Request Object */
-	char* parameter = OIDC_PROTO_REQUEST_URI;
+	char *parameter = OIDC_PROTO_REQUEST_URI;
 
 	/* get request_object_type parameter from config */
 	json_t *request_object_type = json_object_get(request_object_config,
 			"request_object_type");
 	if (request_object_type != NULL) {
-		const char* request_object_type_str = json_string_value(
+		const char *request_object_type_str = json_string_value(
 				request_object_type);
 		if (request_object_type_str == NULL) {
 			oidc_error(r,
@@ -560,7 +554,7 @@
 	}
 
 	/* create request value */
-	char * value = NULL;
+	char *value = NULL;
 	if (strcmp(parameter, OIDC_PROTO_REQUEST_URI) == 0) {
 		/* parameter is "request_uri" */
 		value = oidc_proto_create_request_uri(r, provider,
@@ -583,9 +577,9 @@
 /*
  * add a key/value pair post parameter
  */
-static int oidc_proto_add_form_post_param(void* rec, const char* key,
-		const char* value) {
-	oidc_proto_form_post_ctx_t *ctx = (oidc_proto_form_post_ctx_t *) rec;
+static int oidc_proto_add_form_post_param(void *rec, const char *key,
+		const char *value) {
+	oidc_proto_form_post_ctx_t *ctx = (oidc_proto_form_post_ctx_t*) rec;
 	oidc_debug(ctx->r, "processing: %s=%s", key, value);
 	ctx->html_body = apr_psprintf(ctx->r->pool,
 			"%s      <input type=\"hidden\" name=\"%s\" value=\"%s\">\n",
@@ -627,7 +621,7 @@
 
 	while (*auth_request_params
 			&& (val = ap_getword(r->pool, &auth_request_params, OIDC_CHAR_AMP))) {
-		key = ap_getword(r->pool, (const char **) &val, OIDC_CHAR_EQUAL);
+		key = ap_getword(r->pool, (const char**) &val, OIDC_CHAR_EQUAL);
 		ap_unescape_url(key);
 		ap_unescape_url(val);
 		if (apr_strnatcmp(val, OIDC_STR_HASH) != 0) {
@@ -686,6 +680,12 @@
 		apr_table_setn(params, OIDC_PROTO_SCOPE, scope);
 	}
 
+	if (provider->client_id == NULL) {
+		oidc_error(r,
+				"no Client ID set for the provider: perhaps you are accessing an endpoint protected with \"AuthType openid-connect\" instead of \"AuthType oauth20\"?)");
+		return HTTP_INTERNAL_SERVER_ERROR;
+	}
+
 	/* add the client ID */
 	apr_table_setn(params, OIDC_PROTO_CLIENT_ID, provider->client_id);
 
@@ -758,7 +758,16 @@
 
 			/* and tell Apache to return an HTTP Redirect (302) message */
 			rv = HTTP_MOVED_TEMPORARILY;
+
+		} else {
+
+			/* signal this to the content handler */
+			oidc_request_state_set(r, OIDC_REQUEST_STATE_KEY_AUTHN, "");
+			r->user = "";
+			rv = OK;
+
 		}
+
 	} else {
 		oidc_error(r, "provider->auth_request_method set to wrong value: %d",
 				provider->auth_request_method);
@@ -927,8 +936,7 @@
 		OIDC_PKCE_METHOD_REFERRED_TB,
 		oidc_proto_pkce_state_referred_tb,
 		oidc_proto_pkce_verifier_referred_tb,
-		oidc_proto_pkce_challenge_referred_tb
-};
+		oidc_proto_pkce_challenge_referred_tb };
 
 #define OIDC_PROTO_STATE_ISSUER          "i"
 #define OIDC_PROTO_STATE_ORIGINAL_URL    "ou"
@@ -941,7 +949,7 @@
 #define OIDC_PROTO_STATE_PKCE_STATE      "ps"
 #define OIDC_PROTO_STATE_STATE           "s"
 
-static const char *oidc_proto_state_get_string_value(
+static const char* oidc_proto_state_get_string_value(
 		oidc_proto_state_t *proto_state, const char *name) {
 	json_t *v = json_object_get(proto_state, name);
 	return v ? json_string_value(v) : NULL;
@@ -952,7 +960,7 @@
 	json_object_set_new(proto_state, name, json_string(value));
 }
 
-oidc_proto_state_t *oidc_proto_state_new() {
+oidc_proto_state_t* oidc_proto_state_new() {
 	return json_object();
 }
 
@@ -960,30 +968,30 @@
 	json_decref(proto_state);
 }
 
-oidc_proto_state_t * oidc_proto_state_from_cookie(request_rec *r, oidc_cfg *c,
+oidc_proto_state_t* oidc_proto_state_from_cookie(request_rec *r, oidc_cfg *c,
 		const char *cookieValue) {
 	json_t *result = NULL;
-	oidc_util_jwt_verify(r, c->crypto_passphrase, cookieValue, &result);
+	oidc_util_jwt_verify(r, c->crypto_passphrase, cookieValue, &result, TRUE);
 	return result;
 }
 
-char *oidc_proto_state_to_cookie(request_rec *r, oidc_cfg *c,
-		oidc_proto_state_t *proto_state) {
+char* oidc_proto_state_to_cookie(request_rec *r, oidc_cfg *c, oidc_proto_state_t *proto_state) {
 	char *cookieValue = NULL;
-	oidc_util_jwt_create(r, c->crypto_passphrase, proto_state, &cookieValue);
+	oidc_util_jwt_create(r, c->crypto_passphrase, proto_state, &cookieValue, TRUE);
 	return cookieValue;
 }
-char *oidc_proto_state_to_string(request_rec *r,
+
+char* oidc_proto_state_to_string(request_rec *r,
 		oidc_proto_state_t *proto_state) {
 	return oidc_util_encode_json_object(r, proto_state, JSON_COMPACT);
 }
 
-const char *oidc_proto_state_get_issuer(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_issuer(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_ISSUER);
 }
 
-const char *oidc_proto_state_get_nonce(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_nonce(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_NONCE);
 }
@@ -993,38 +1001,38 @@
 	return v ? apr_time_from_sec(json_integer_value(v)) : -1;
 }
 
-const char *oidc_proto_state_get_prompt(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_prompt(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_PROMPT);
 }
 
-const char *oidc_proto_state_get_response_type(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_response_type(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_RESPONSE_TYPE);
 }
 
-const char *oidc_proto_state_get_response_mode(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_response_mode(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_RESPONSE_MODE);
 }
 
-const char *oidc_proto_state_get_original_url(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_original_url(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_ORIGINAL_URL);
 }
 
-const char *oidc_proto_state_get_original_method(
+const char* oidc_proto_state_get_original_method(
 		oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_ORIGINAL_METHOD);
 }
 
-const char *oidc_proto_state_get_state(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_state(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_STATE);
 }
 
-const char *oidc_proto_state_get_pkce_state(oidc_proto_state_t *proto_state) {
+const char* oidc_proto_state_get_pkce_state(oidc_proto_state_t *proto_state) {
 	return oidc_proto_state_get_string_value(proto_state,
 			OIDC_PROTO_STATE_PKCE_STATE);
 }
@@ -1354,9 +1362,10 @@
 	}
 
 	/* validate the ID Token JWT, requiring iss match, and valid exp + iat */
-	if (oidc_proto_validate_jwt(r, jwt, provider->validate_issuer ? provider->issuer : NULL, TRUE, TRUE,
-			provider->idtoken_iat_slack,
-			provider->token_binding_policy) == FALSE)
+	if (oidc_proto_validate_jwt(r, jwt,
+			provider->validate_issuer ? provider->issuer : NULL, TRUE, TRUE,
+					provider->idtoken_iat_slack,
+					provider->token_binding_policy) == FALSE)
 		return FALSE;
 
 	/* check if the required-by-spec "sub" claim is present */
@@ -1560,7 +1569,7 @@
 		 /* get the key from the JWKs that corresponds with the key specified in the header */
 		 if (oidc_proto_get_keys_from_jwks_uri(r, cfg, jwt, jwks_uri,
 				 dynamic_keys, &force_refresh) == FALSE) {
-			 oidc_jwk_list_destroy(r->pool, dynamic_keys);
+			 oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
 			 return FALSE;
 		 }
 	 }
@@ -1568,11 +1577,11 @@
 	/* do the actual JWS verification with the locally and remotely provided key material */
 	// TODO: now static keys "win" if the same `kid` was used in both local and remote key sets
 	if (oidc_jwt_verify(r->pool, jwt,
-			oidc_util_merge_key_sets(r->pool, static_keys, dynamic_keys),
+			oidc_util_merge_key_sets_hash(r->pool, static_keys, dynamic_keys),
 			&err) == FALSE) {
 		oidc_error(r, "JWT signature verification failed: %s",
 				oidc_jose_e2s(r->pool, err));
-		oidc_jwk_list_destroy(r->pool, dynamic_keys);
+		oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
 		return FALSE;
 	}
 
@@ -1580,14 +1589,14 @@
 			"JWT signature verification with algorithm \"%s\" was successful",
 			jwt->header.alg);
 
-	oidc_jwk_list_destroy(r->pool, dynamic_keys);
+	oidc_jwk_list_destroy_hash(r->pool, dynamic_keys);
 	return TRUE;
 }
 
 /*
  * return the compact-encoded JWT header contents
  */
-char *oidc_proto_peek_jwt_header(request_rec *r,
+char* oidc_proto_peek_jwt_header(request_rec *r,
 		const char *compact_encoded_jwt, char **alg) {
 	char *input = NULL, *result = NULL;
 	char *p = strstr(compact_encoded_jwt ? compact_encoded_jwt : "", ".");
@@ -1825,7 +1834,7 @@
 		return FALSE;
 
 	oidc_jwk_t *jwk = oidc_jwk_create_symmetric_key(r->pool, NULL,
-			(const unsigned char *) client_secret, strlen(client_secret),
+			(const unsigned char*) client_secret, strlen(client_secret),
 			FALSE, &err);
 	if (jwk == NULL) {
 		oidc_error(r, "parsing of client secret into JWK failed: %s",
@@ -1864,22 +1873,21 @@
 #define OIDC_PROTO_JWT_ASSERTION_ASYMMETRIC_ALG CJOSE_HDR_ALG_RS256
 
 static apr_byte_t oidc_proto_endpoint_auth_private_key_jwt(request_rec *r,
-		oidc_cfg *cfg, const char *client_id, apr_hash_t *client_signing_keys,
-		const char *audience, apr_table_t *params) {
+		oidc_cfg *cfg, const char *client_id,
+		const apr_array_header_t *client_signing_keys, const char *audience,
+		apr_table_t *params) {
 	oidc_jwt_t *jwt = NULL;
 	oidc_jwk_t *jwk = NULL;
-	apr_hash_t *signing_keys = NULL;
+	const apr_array_header_t *signing_keys = NULL;
 
 	oidc_debug(r, "enter");
 
 	if (oidc_proto_jwt_create(r, client_id, audience, &jwt) == FALSE)
 		return FALSE;
 
-	if ((client_signing_keys != NULL)
-			&& (apr_hash_count(client_signing_keys) > 0)) {
+	if ((client_signing_keys != NULL) && (client_signing_keys->nelts > 0)) {
 		signing_keys = client_signing_keys;
-	} else if ((cfg->private_keys != NULL)
-			&& (apr_hash_count(cfg->private_keys) > 0)) {
+	} else if ((cfg->private_keys != NULL) && (cfg->private_keys->nelts > 0)) {
 		signing_keys = cfg->private_keys;
 	} else {
 		oidc_error(r,
@@ -1888,10 +1896,9 @@
 		return FALSE;
 	}
 
-	apr_ssize_t klen = 0;
-	apr_hash_index_t *hi = apr_hash_first(r->pool, signing_keys);
-	apr_hash_this(hi, (const void **) &jwt->header.kid, &klen, (void **) &jwk);
+	jwk = ((oidc_jwk_t**) signing_keys->elts)[0];
 
+	jwt->header.kid = apr_pstrdup(r->pool, jwk->kid);
 	jwt->header.alg = apr_pstrdup(r->pool, CJOSE_HDR_ALG_RS256);
 
 	oidc_proto_jwt_sign_and_add(r, params, jwt, jwk);
@@ -1903,10 +1910,10 @@
 
 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, apr_hash_t *client_signing_keys,
-		const char *audience, apr_table_t *params,
-		const char *bearer_access_token, char **basic_auth_str,
-		char **bearer_auth_str) {
+		const char *client_secret,
+		const apr_array_header_t *client_signing_keys, const char *audience,
+		apr_table_t *params, const char *bearer_access_token,
+		char **basic_auth_str, char **bearer_auth_str) {
 
 	oidc_debug(r, "token_endpoint_auth=%s", token_endpoint_auth);
 
@@ -1998,8 +2005,10 @@
 			oidc_dir_cfg_pass_cookies(r),
 			oidc_util_get_full_path(r->pool,
 					provider->token_endpoint_tls_client_cert),
-					oidc_util_get_full_path(r->pool,
-							provider->token_endpoint_tls_client_key)) == FALSE) {
+			oidc_util_get_full_path(r->pool,
+					provider->token_endpoint_tls_client_key),
+			provider->token_endpoint_tls_client_key_pwd
+			) == FALSE) {
 		oidc_warn(r, "error when calling the token endpoint (%s)",
 				provider->token_endpoint_url);
 		return FALSE;
@@ -2239,7 +2248,7 @@
 							NULL, NULL, access_token, cfg->provider.ssl_validate_server,
 							&s_json, cfg->http_timeout_long,
 							cfg->outgoing_proxy, oidc_dir_cfg_pass_cookies(r),
-							NULL, NULL);
+							NULL, NULL, NULL);
 				}
 			}
 			if ((s_json != NULL) && (strcmp(s_json, "") != 0)) {
@@ -2304,7 +2313,7 @@
 		if (oidc_util_http_get(r, provider->userinfo_endpoint_url,
 				NULL, NULL, access_token, provider->ssl_validate_server, response,
 				cfg->http_timeout_long, cfg->outgoing_proxy,
-				oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE)
+				oidc_dir_cfg_pass_cookies(r), NULL, NULL, NULL) == FALSE)
 			return FALSE;
 	} else if (provider->userinfo_token_method
 			== OIDC_USER_INFO_TOKEN_METHOD_POST) {
@@ -2313,7 +2322,7 @@
 		if (oidc_util_http_post_form(r, provider->userinfo_endpoint_url, params,
 				NULL, NULL, provider->ssl_validate_server, response,
 				cfg->http_timeout_long, cfg->outgoing_proxy,
-				oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE)
+				oidc_dir_cfg_pass_cookies(r), NULL, NULL, NULL) == FALSE)
 			return FALSE;
 	} else {
 		oidc_error(r, "unsupported userinfo token presentation method: %d",
@@ -2337,6 +2346,15 @@
 	oidc_debug(r, "id_token_sub=%s, user_info_sub=%s", id_token_sub,
 			user_info_sub);
 
+	if ((user_info_sub == NULL)
+			&& (apr_table_get(r->subprocess_env, "OIDC_NO_USERINFO_SUB") == NULL)) {
+		oidc_error(r,
+				"mandatory claim (\"%s\") was not returned from userinfo endpoint (https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse)",
+				OIDC_CLAIM_SUB);
+		json_decref(claims);
+		return FALSE;
+	}
+
 	if ((id_token_sub != NULL) && (user_info_sub != NULL)) {
 		if (apr_strnatcmp(id_token_sub, user_info_sub) != 0) {
 			oidc_error(r,
@@ -2369,7 +2387,7 @@
 	if (oidc_util_http_get(r, url, params, NULL, NULL,
 			cfg->provider.ssl_validate_server, &response,
 			cfg->http_timeout_short, cfg->outgoing_proxy,
-			oidc_dir_cfg_pass_cookies(r), NULL, NULL) == FALSE) {
+			oidc_dir_cfg_pass_cookies(r), NULL, NULL, NULL) == FALSE) {
 		/* errors will have been logged by now */
 		return FALSE;
 	}
@@ -2630,7 +2648,7 @@
 /*
  * return the supported flows
  */
-apr_array_header_t *oidc_proto_supported_flows(apr_pool_t *pool) {
+apr_array_header_t* oidc_proto_supported_flows(apr_pool_t *pool) {
 	apr_array_header_t *result = apr_array_make(pool, 6, sizeof(const char*));
 	*(const char**) apr_array_push(result) = OIDC_PROTO_RESPONSE_TYPE_CODE;
 	*(const char**) apr_array_push(result) =
@@ -2885,8 +2903,10 @@
 
 	if ((must_validate_code == TRUE)
 			&& (oidc_proto_validate_code(r, provider, *jwt, response_type, code)
-					== FALSE))
+					== FALSE)) {
+		oidc_jwt_destroy(*jwt);
 		return FALSE;
+	}
 
 	return TRUE;
 }
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/session.c libapache2-mod-auth-openidc-2.4.9/src/session.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/session.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/session.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -74,32 +66,29 @@
 /* the name of the sid attribute in the session */
 #define OIDC_SESSION_SID_KEY                      "sid"
 
-static apr_byte_t oidc_session_encode(request_rec *r, oidc_cfg *c,
-		oidc_session_t *z, char **s_value, apr_byte_t encrypt) {
+static apr_byte_t oidc_session_encode(request_rec *r, oidc_cfg *c, oidc_session_t *z,
+		char **s_value, apr_byte_t encrypt) {
 
 	if (encrypt == FALSE) {
 		*s_value = oidc_util_encode_json_object(r, z->state, JSON_COMPACT);
 		return (*s_value != NULL);
 	}
 
-	if (oidc_util_jwt_create(r, c->crypto_passphrase, z->state,
-			s_value) == FALSE)
+	if (oidc_util_jwt_create(r, c->crypto_passphrase, z->state, s_value, TRUE) == FALSE)
 		return FALSE;
 
 	return TRUE;
 }
 
-static apr_byte_t oidc_session_decode(request_rec *r, oidc_cfg *c,
-		oidc_session_t *z, const char *s_json, apr_byte_t encrypt) {
+static apr_byte_t oidc_session_decode(request_rec *r, oidc_cfg *c, oidc_session_t *z,
+		const char *s_json, apr_byte_t encrypt) {
 
 	if (encrypt == FALSE) {
 		return oidc_util_decode_json_object(r, s_json, &z->state);
 	}
 
-	if (oidc_util_jwt_verify(r, c->crypto_passphrase, s_json,
-			&z->state) == FALSE) {
-		oidc_error(r,
-				"could not verify secure JWT: cache value possibly corrupted");
+	if (oidc_util_jwt_verify(r, c->crypto_passphrase, s_json, &z->state, TRUE) == FALSE) {
+		oidc_error(r, "could not verify secure JWT: cache value possibly corrupted");
 		return FALSE;
 	}
 	return TRUE;
@@ -180,10 +169,10 @@
 
 		rc = oidc_session_load_cache_by_uuid(r, c, uuid, z);
 
-		if (rc == FALSE) {
+		if (rc == FALSE || z->state == NULL) {
 			/* delete the session cookie */
 			oidc_util_set_cookie(r, oidc_cfg_dir_cookie(r), "", 0,
-					OIDC_COOKIE_EXT_SAME_SITE_NONE);
+					OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 		}
 	}
 
@@ -228,7 +217,7 @@
 									(first_time ?
 											OIDC_COOKIE_EXT_SAME_SITE_LAX :
 											OIDC_COOKIE_EXT_SAME_SITE_STRICT) :
-											OIDC_COOKIE_EXT_SAME_SITE_NONE);
+											OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 
 	} else {
 
@@ -237,7 +226,7 @@
 
 		/* clear the cookie */
 		oidc_util_set_cookie(r, oidc_cfg_dir_cookie(r), "", 0,
-				OIDC_COOKIE_EXT_SAME_SITE_NONE);
+				OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 
 		/* remove the session from the cache */
 		rc = oidc_cache_set_session(r, z->uuid, NULL, 0);
@@ -274,12 +263,12 @@
 	oidc_util_set_chunked_cookie(r, oidc_cfg_dir_cookie(r), cookieValue,
 			c->persistent_session_cookie ? z->expiry : -1,
 					c->session_cookie_chunk_size,
-					(z->state == NULL) ? OIDC_COOKIE_EXT_SAME_SITE_NONE :
+					(z->state == NULL) ? OIDC_COOKIE_EXT_SAME_SITE_NONE(r) :
 							c->cookie_same_site ?
 									(first_time ?
 											OIDC_COOKIE_EXT_SAME_SITE_LAX :
 											OIDC_COOKIE_EXT_SAME_SITE_STRICT) :
-											OIDC_COOKIE_EXT_SAME_SITE_NONE);
+											OIDC_COOKIE_EXT_SAME_SITE_NONE(r));
 
 	return TRUE;
 }
@@ -406,8 +395,12 @@
  * terminate a session
  */
 apr_byte_t oidc_session_kill(request_rec *r, oidc_session_t *z) {
-	oidc_session_free(r, z);
-	return oidc_session_save(r, z, FALSE);
+	if (z->state) {
+		json_decref(z->state);
+		z->state = NULL;
+	}
+	oidc_session_save(r, z, FALSE);
+	return oidc_session_free(r, z);
 }
 
 /*
@@ -484,7 +477,7 @@
 		const char *key, const apr_time_t timestamp) {
 	if (timestamp != -1)
 		oidc_session_set(r, z, key,
-				apr_psprintf(r->pool, "%" APR_TIME_T_FMT, timestamp));
+				apr_psprintf(r->pool, "%" APR_TIME_T_FMT, apr_time_sec(timestamp)));
 }
 
 static json_t *oidc_session_get_str2json(request_rec *r, oidc_session_t *z,
@@ -509,7 +502,7 @@
 	const char *s_expires = oidc_session_get_key2string(r, z, key);
 	if (s_expires != NULL)
 		sscanf(s_expires, "%" APR_TIME_T_FMT, &t_expires);
-	return t_expires;
+	return apr_time_from_sec(t_expires);
 }
 
 void oidc_session_set_filtered_claims(request_rec *r, oidc_session_t *z,
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/src/util.c libapache2-mod-auth-openidc-2.4.9/src/util.c
--- libapache2-mod-auth-openidc-2.4.4.1/src/util.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/src/util.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -80,7 +72,7 @@
 	}
 	unsigned int enc_len = apr_base64_encode_len(src_len);
 	char *enc = apr_palloc(r->pool, enc_len);
-	apr_base64_encode(enc, (const char *) src, src_len);
+	apr_base64_encode(enc, (const char*) src, src_len);
 	unsigned int i = 0;
 	while (enc[i] != '\0') {
 		if (enc[i] == '+')
@@ -140,8 +132,10 @@
 	return apr_base64_decode(*dst, dec);
 }
 
-apr_byte_t oidc_util_jwt_create(request_rec *r, const char *secret,
-		json_t *payload, char **compact_encoded_jwt) {
+#define OIDC_JWT_HDR_DIR_A256GCM "eyJhbGciOiAiZGlyIiwgImVuYyI6ICJBMjU2R0NNIn0.."
+
+apr_byte_t oidc_util_jwt_create(request_rec *r, const char *secret, json_t *payload,
+		char **compact_encoded_jwt, apr_byte_t strip_header) {
 
 	apr_byte_t rv = FALSE;
 	oidc_jose_error_t err;
@@ -150,8 +144,7 @@
 	oidc_jwt_t *jwt = NULL;
 	oidc_jwt_t *jwe = NULL;
 
-	if (oidc_util_create_symmetric_key(r, secret, 0, OIDC_JOSE_ALG_SHA256,
-			FALSE, &jwk) == FALSE)
+	if (oidc_util_create_symmetric_key(r, secret, 0, OIDC_JOSE_ALG_SHA256, FALSE, &jwk) == FALSE)
 		goto end;
 
 	jwt = oidc_jwt_new(r->pool, TRUE, FALSE);
@@ -178,12 +171,14 @@
 	jwe->header.enc = apr_pstrdup(r->pool, CJOSE_HDR_ENC_A256GCM);
 
 	const char *cser = oidc_jwt_serialize(r->pool, jwt, &err);
-	if (oidc_jwt_encrypt(r->pool, jwe, jwk, cser, compact_encoded_jwt,
-			&err) == FALSE) {
+	if (oidc_jwt_encrypt(r->pool, jwe, jwk, cser, compact_encoded_jwt, &err) == FALSE) {
 		oidc_error(r, "encrypting JWT failed: %s", oidc_jose_e2s(r->pool, err));
 		goto end;
 	}
 
+	if (strip_header == TRUE)
+		*compact_encoded_jwt += strlen(OIDC_JWT_HDR_DIR_A256GCM);
+
 	rv = TRUE;
 
 end:
@@ -200,11 +195,10 @@
 	return rv;
 }
 
-apr_byte_t oidc_util_jwt_verify(request_rec *r, const char *secret,
-		const char *compact_encoded_jwt, json_t **result) {
+apr_byte_t oidc_util_jwt_verify(request_rec *r, const char *secret, const char *compact_encoded_jwt,
+		json_t **result, apr_byte_t stripped_header) {
 
-	oidc_debug(r, "enter: JWT header=%s",
-			oidc_proto_peek_jwt_header(r, compact_encoded_jwt, NULL));
+	oidc_debug(r, "enter: JWT header=%s", oidc_proto_peek_jwt_header(r, compact_encoded_jwt, NULL));
 
 	apr_byte_t rv = FALSE;
 	oidc_jose_error_t err;
@@ -212,13 +206,16 @@
 	oidc_jwk_t *jwk = NULL;
 	oidc_jwt_t *jwt = NULL;
 
-	if (oidc_util_create_symmetric_key(r, secret, 0, OIDC_JOSE_ALG_SHA256,
-			FALSE, &jwk) == FALSE)
+	if (oidc_util_create_symmetric_key(r, secret, 0, OIDC_JOSE_ALG_SHA256, FALSE, &jwk) == FALSE)
 		goto end;
 
 	apr_hash_t *keys = apr_hash_make(r->pool);
 	apr_hash_set(keys, "", APR_HASH_KEY_STRING, jwk);
 
+	if (stripped_header == TRUE)
+		compact_encoded_jwt =
+				apr_pstrcat(r->pool, OIDC_JWT_HDR_DIR_A256GCM, compact_encoded_jwt, NULL);
+
 	if (oidc_jwt_parse(r->pool, compact_encoded_jwt, &jwt, keys, &err) == FALSE) {
 		oidc_error(r, "parsing JWT failed: %s", oidc_jose_e2s(r->pool, err));
 		goto end;
@@ -293,7 +290,7 @@
 /*
  * escape a string
  */
-char *oidc_util_escape_string(const request_rec *r, const char *str) {
+char* oidc_util_escape_string(const request_rec *r, const char *str) {
 	CURL *curl = curl_easy_init();
 	if (curl == NULL) {
 		oidc_error(r, "curl_easy_init() error");
@@ -313,14 +310,14 @@
 /*
  * escape a string
  */
-char *oidc_util_unescape_string(const request_rec *r, const char *str) {
+char* oidc_util_unescape_string(const request_rec *r, const char *str) {
 	CURL *curl = curl_easy_init();
 	if (curl == NULL) {
 		oidc_error(r, "curl_easy_init() error");
 		return NULL;
 	}
 	int counter = 0;
-	char *replaced = (char *) str;
+	char *replaced = (char*) str;
 	while (str[counter] != '\0') {
 		if (str[counter] == '+') {
 			replaced[counter] = ' ';
@@ -342,10 +339,10 @@
 /*
  * HTML escape a string
  */
-char *oidc_util_html_escape(apr_pool_t *pool, const char *s) {
+char* oidc_util_html_escape(apr_pool_t *pool, const char *s) {
 	// TODO: this has performance/memory issues for large chunks of HTML
 	const char chars[6] = { '&', '\'', '\"', '>', '<', '\0' };
-	const char * const replace[] =
+	const char *const replace[] =
 	{ "&amp;", "&apos;", "&quot;", "&gt;", "&lt;", };
 	unsigned int i, j = 0, k, n = 0, len = strlen(chars);
 	unsigned int m = 0;
@@ -370,9 +367,90 @@
 }
 
 /*
+ * JavaScript escape a string
+ */
+char* oidc_util_javascript_escape(apr_pool_t *pool, const char *s) {
+    const char *cp;
+    char *output;
+    size_t outputlen;
+    int i;
+
+    if (s == NULL) {
+        return NULL;
+    }
+
+    outputlen = 0;
+    for (cp = s; *cp; cp++) {
+        switch (*cp) {
+        case '\'':
+        case '"':
+        case '\\':
+        case '/':
+        case 0x0D:
+        case 0x0A:
+            outputlen += 2;
+            break;
+        case '<':
+        case '>':
+            outputlen += 4;
+            break;
+        default:
+            outputlen += 1;
+            break;
+        }
+    }
+
+    i = 0;
+    output = apr_palloc(pool, outputlen + 1);
+    for (cp = s; *cp; cp++) {
+        switch (*cp) {
+        case '\'':
+            (void)strcpy(&output[i], "\\'");
+            i += 2;
+            break;
+        case '"':
+            (void)strcpy(&output[i], "\\\"");
+            i += 2;
+            break;
+        case '\\':
+            (void)strcpy(&output[i], "\\\\");
+            i += 2;
+            break;
+        case '/':
+            (void)strcpy(&output[i], "\\/");
+            i += 2;
+            break;
+        case 0x0D:
+            (void)strcpy(&output[i], "\\r");
+            i += 2;
+            break;
+        case 0x0A:
+            (void)strcpy(&output[i], "\\n");
+            i += 2;
+            break;
+        case '<':
+            (void)strcpy(&output[i], "\\x3c");
+            i += 4;
+            break;
+        case '>':
+            (void)strcpy(&output[i], "\\x3e");
+            i += 4;
+            break;
+        default:
+            output[i] = *cp;
+            i += 1;
+            break;
+        }
+    }
+    output[i] = '\0';
+    return output;
+}
+
+
+/*
  * get the URL scheme that is currently being accessed
  */
-static const char *oidc_get_current_url_scheme(const request_rec *r) {
+static const char* oidc_get_current_url_scheme(const request_rec *r) {
 	/* first see if there's a proxy/load-balancer in front of us */
 	const char *scheme_str = oidc_util_hdr_in_x_forwarded_proto_get(r);
 	/* if not we'll determine the scheme used to connect to this server */
@@ -380,7 +458,7 @@
 #ifdef APACHE2_0
 		scheme_str = (char *) ap_http_method(r);
 #else
-		scheme_str = (char *) ap_http_scheme(r);
+		scheme_str = (char*) ap_http_scheme(r);
 #endif
 	}
 	if ((scheme_str == NULL)
@@ -395,9 +473,30 @@
 }
 
 /*
+ * get the Port part that is currently being accessed
+ */
+static const char* oidc_get_port_from_host(	const char *host_hdr){
+	char *p = NULL;
+	char *i = NULL;
+
+	if (host_hdr) {
+		if (host_hdr[0]=='[') {
+			i = strchr(host_hdr, ']');
+			p = strchr(i, OIDC_CHAR_COLON);
+		} else {
+			p = strchr(host_hdr, OIDC_CHAR_COLON);
+		}
+	}
+	if (p)
+		return p;
+	else
+		return NULL;
+}
+
+/*
  * get the URL port that is currently being accessed
  */
-static const char *oidc_get_current_url_port(const request_rec *r,
+static const char* oidc_get_current_url_port(const request_rec *r,
 		const char *scheme_str) {
 
 	/*
@@ -414,7 +513,7 @@
 	 */
 	const char *host_hdr = oidc_util_hdr_in_x_forwarded_host_get(r);
 	if (host_hdr) {
-		port_str = strchr(host_hdr, OIDC_CHAR_COLON);
+		port_str = oidc_get_port_from_host(host_hdr);
 		if (port_str)
 			port_str++;
 		return port_str;
@@ -426,7 +525,7 @@
 	 */
 	host_hdr = oidc_util_hdr_in_host_get(r);
 	if (host_hdr) {
-		port_str = strchr(host_hdr, OIDC_CHAR_COLON);
+		port_str = oidc_get_port_from_host(host_hdr);
 		if (port_str) {
 			port_str++;
 			return port_str;
@@ -457,15 +556,24 @@
 /*
  * get the hostname part of the URL that is currently being accessed
  */
-const char *oidc_get_current_url_host(request_rec *r) {
+const char* oidc_get_current_url_host(request_rec *r) {
 	const char *host_str = oidc_util_hdr_in_x_forwarded_host_get(r);
+    char *p = NULL;
+	char *i = NULL;
 	if (host_str == NULL)
 		host_str = oidc_util_hdr_in_host_get(r);
 	if (host_str) {
 		host_str = apr_pstrdup(r->pool, host_str);
-		char *p = strchr(host_str, OIDC_CHAR_COLON);
-		if (p != NULL)
-			*p = '\0';
+
+		if (host_str[0] == '[') {
+			i= strchr(host_str, ']');
+			p = strchr(i, OIDC_CHAR_COLON);
+		} else {
+			p = strchr(host_str, OIDC_CHAR_COLON);
+		}
+
+	if (p != NULL)
+		*p = '\0';
 	} else {
 		/* no Host header, HTTP 1.0 */
 		host_str = ap_get_server_name(r);
@@ -476,7 +584,7 @@
 /*
  * get the base part of the current URL (scheme + host (+ port))
  */
-static const char *oidc_get_current_url_base(request_rec *r) {
+static const char* oidc_get_current_url_base(request_rec *r) {
 
 	const char *scheme_str = oidc_get_current_url_scheme(r);
 	const char *host_str = oidc_get_current_url_host(r);
@@ -492,7 +600,7 @@
 /*
  * get the URL that is currently being accessed
  */
-char *oidc_get_current_url(request_rec *r) {
+char* oidc_get_current_url(request_rec *r) {
 	char *url = NULL, *path = NULL;
 	apr_uri_t uri;
 
@@ -523,7 +631,7 @@
 /*
  * determine absolute redirect uri
  */
-const char *oidc_get_redirect_uri(request_rec *r, oidc_cfg *cfg) {
+const char* oidc_get_redirect_uri(request_rec *r, oidc_cfg *cfg) {
 
 	char *redirect_uri = cfg->redirect_uri;
 
@@ -542,7 +650,7 @@
 /*
  * determine absolute redirect uri that is issuer specific
  */
-const char *oidc_get_redirect_uri_iss(request_rec *r, oidc_cfg *cfg,
+const char* oidc_get_redirect_uri_iss(request_rec *r, oidc_cfg *cfg,
 		oidc_provider_t *provider) {
 	const char *redirect_uri = oidc_get_redirect_uri(r, cfg);
 	if (provider->issuer_specific_redirect_uri != 0) {
@@ -575,7 +683,7 @@
  */
 size_t oidc_curl_write(void *contents, size_t size, size_t nmemb, void *userp) {
 	size_t realsize = size * nmemb;
-	oidc_curl_buffer *mem = (oidc_curl_buffer *) userp;
+	oidc_curl_buffer *mem = (oidc_curl_buffer*) userp;
 
 	/* check if we don't run over the maximum buffer/memory size for HTTP responses */
 	if (mem->size + realsize > OIDC_CURL_MAX_RESPONSE_SIZE) {
@@ -613,10 +721,11 @@
 /*
  * add a url-form-encoded name/value pair
  */
-static int oidc_util_http_add_form_url_encoded_param(void* rec, const char* key,
-		const char* value) {
+static int oidc_util_http_add_form_url_encoded_param(void *rec, const char *key,
+		const char *value) {
 	oidc_http_encode_t *ctx = (oidc_http_encode_t*) rec;
-	oidc_debug(ctx->r, "processing: %s=%s", key, value);
+	oidc_debug(ctx->r, "processing: %s=%s", key,
+			(strncmp(key, OIDC_PROTO_CLIENT_SECRET, strlen(OIDC_PROTO_CLIENT_SECRET)) == 0) ? "***" : value);
 	const char *sep = ctx->encoded_params ? OIDC_STR_AMP : "";
 	ctx->encoded_params = apr_psprintf(ctx->r->pool, "%s%s%s=%s",
 			ctx->encoded_params ? ctx->encoded_params : "", sep,
@@ -628,7 +737,7 @@
 /*
  * construct a URL with query parameters
  */
-char *oidc_util_http_query_encoded_url(request_rec *r, const char *url,
+char* oidc_util_http_query_encoded_url(request_rec *r, const char *url,
 		const apr_table_t *params) {
 	char *result = NULL;
 	if ((params != NULL) && (apr_table_elts(params)->nelts > 0)) {
@@ -652,7 +761,7 @@
 /*
  * construct form-encoded POST data
  */
-char *oidc_util_http_form_encoded_data(request_rec *r,
+char* oidc_util_http_form_encoded_data(request_rec *r,
 		const apr_table_t *params) {
 	char *data = NULL;
 	if ((params != NULL) && (apr_table_elts(params)->nelts > 0)) {
@@ -674,19 +783,21 @@
 		const char *bearer_token, int ssl_validate_server, char **response,
 		int timeout, const char *outgoing_proxy,
 		apr_array_header_t *pass_cookies, const char *ssl_cert,
-		const char *ssl_key) {
+		const char *ssl_key, const char *ssl_key_pwd) {
 	char curlError[CURL_ERROR_SIZE];
 	oidc_curl_buffer curlBuffer;
 	CURL *curl;
 	struct curl_slist *h_list = NULL;
 	int i;
+	oidc_cfg *c = ap_get_module_config(r->server->module_config,
+			&auth_openidc_module);
 
 	/* do some logging about the inputs */
 	oidc_debug(r,
-			"url=%s, data=%s, content_type=%s, basic_auth=%s, bearer_token=%s, ssl_validate_server=%d, timeout=%d, outgoing_proxy=%s, pass_cookies=%pp, ssl_cert=%s, ssl_key=%s",
-			url, data, content_type, basic_auth, bearer_token,
-			ssl_validate_server, timeout, outgoing_proxy, pass_cookies,
-			ssl_cert, ssl_key);
+			"url=%s, data=%s, content_type=%s, basic_auth=%s, bearer_token=%s, ssl_validate_server=%d, timeout=%d, outgoing_proxy=%s, pass_cookies=%pp, ssl_cert=%s, ssl_key=%s, ssl_key_pwd=%s",
+			url, data, content_type, basic_auth ? "****" : "null", bearer_token,
+					ssl_validate_server, timeout, outgoing_proxy, pass_cookies,
+					ssl_cert, ssl_key, ssl_key_pwd ? "****" : "(null)");
 
 	curl = curl_easy_init();
 	if (curl == NULL) {
@@ -713,7 +824,7 @@
 	curlBuffer.memory = NULL;
 	curlBuffer.size = 0;
 	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, oidc_curl_write);
-	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void * )&curlBuffer);
+	curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void* )&curlBuffer);
 
 #ifndef LIBCURL_NO_CURLPROTO
 	curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS,
@@ -727,17 +838,71 @@
 	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,
 			(ssl_validate_server != FALSE ? 2L : 0L));
 
+#if LIBCURL_VERSION_NUM >= 0x071900
+	if (r->subprocess_env != NULL) {
+		const char *env_var_value = apr_table_get(r->subprocess_env,
+				"CURLOPT_SSL_OPTIONS");
+		if (env_var_value != NULL) {
+			oidc_debug(r, "SSL options environment variable %s=%s found",
+					"CURLOPT_SSL_OPTIONS", env_var_value);
+			if (strstr(env_var_value, "CURLSSLOPT_ALLOW_BEAST")) {
+				oidc_debug(r,
+						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_ALLOW_BEAST");
+				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
+						CURLSSLOPT_ALLOW_BEAST);
+			}
+#if LIBCURL_VERSION_NUM >= 0x072c00
+			if (strstr(env_var_value, "CURLSSLOPT_NO_REVOKE")) {
+				oidc_debug(r,
+						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NO_REVOKE");
+				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
+						CURLSSLOPT_NO_REVOKE);
+			}
+#endif
+#if LIBCURL_VERSION_NUM >= 0x074400
+			if (strstr(env_var_value, "CURLSSLOPT_NO_PARTIALCHAIN")) {
+				oidc_debug(r,
+						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NO_PARTIALCHAIN");
+				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
+						CURLSSLOPT_NO_PARTIALCHAIN);
+			}
+#endif
+#if LIBCURL_VERSION_NUM >= 0x074600
+			if (strstr(env_var_value, "CURLSSLOPT_REVOKE_BEST_EFFORT")) {
+				oidc_debug(r,
+						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_REVOKE_BEST_EFFORT");
+				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
+						CURLSSLOPT_REVOKE_BEST_EFFORT);
+			}
+#endif
+#if LIBCURL_VERSION_NUM >= 0x074700
+			if (strstr(env_var_value, "CURLSSLOPT_NATIVE_CA")) {
+				oidc_debug(r,
+						"curl_easy_setopt CURLOPT_SSL_OPTIONS CURLSSLOPT_NATIVE_CA");
+				curl_easy_setopt(curl, CURLOPT_SSL_OPTIONS,
+						CURLSSLOPT_NATIVE_CA);
+			}
+#endif
+		}
+	}
+#endif
+
+	if (c->ca_bundle_path != NULL)
+		curl_easy_setopt(curl, CURLOPT_CAINFO, c->ca_bundle_path);
+
 #ifdef WIN32
-	DWORD buflen;
-	char *ptr = NULL;
-	char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
-	retval[0] = '\0';
-	buflen = SearchPath(NULL, "curl-ca-bundle.crt", NULL, MAX_PATH+1, retval, &ptr);
-	if (buflen > 0)
-		curl_easy_setopt(curl, CURLOPT_CAINFO, retval);
-	else
-		oidc_warn(r, "no curl-ca-bundle.crt file found in path");
-	free(retval);
+	else {
+		DWORD buflen;
+		char *ptr = NULL;
+		char *retval = (char *) malloc(sizeof (TCHAR) * (MAX_PATH + 1));
+		retval[0] = '\0';
+		buflen = SearchPath(NULL, "curl-ca-bundle.crt", NULL, MAX_PATH+1, retval, &ptr);
+		if (buflen > 0)
+			curl_easy_setopt(curl, CURLOPT_CAINFO, retval);
+		else
+			oidc_warn(r, "no curl-ca-bundle.crt file found in path");
+		free(retval);
+	}
 #endif
 
 	/* identify this HTTP client */
@@ -765,6 +930,8 @@
 		curl_easy_setopt(curl, CURLOPT_SSLCERT, ssl_cert);
 	if (ssl_key != NULL)
 		curl_easy_setopt(curl, CURLOPT_SSLKEY, ssl_key);
+	if (ssl_key_pwd != NULL)
+		curl_easy_setopt(curl, CURLOPT_KEYPASSWD, ssl_key_pwd);
 
 	if (data != NULL) {
 		/* set POST data */
@@ -848,11 +1015,11 @@
 		const char *bearer_token, int ssl_validate_server, char **response,
 		int timeout, const char *outgoing_proxy,
 		apr_array_header_t *pass_cookies, const char *ssl_cert,
-		const char *ssl_key) {
+		const char *ssl_key, const char *ssl_key_pwd) {
 	char *query_url = oidc_util_http_query_encoded_url(r, url, params);
 	return oidc_util_http_call(r, query_url, NULL, NULL, basic_auth,
 			bearer_token, ssl_validate_server, response, timeout,
-			outgoing_proxy, pass_cookies, ssl_cert, ssl_key);
+			outgoing_proxy, pass_cookies, ssl_cert, ssl_key, ssl_key_pwd);
 }
 
 /*
@@ -863,12 +1030,12 @@
 		const char *bearer_token, int ssl_validate_server, char **response,
 		int timeout, const char *outgoing_proxy,
 		apr_array_header_t *pass_cookies, const char *ssl_cert,
-		const char *ssl_key) {
+		const char *ssl_key, const char *ssl_key_pwd) {
 	char *data = oidc_util_http_form_encoded_data(r, params);
 	return oidc_util_http_call(r, url, data,
 			OIDC_CONTENT_TYPE_FORM_ENCODED, basic_auth, bearer_token,
 			ssl_validate_server, response, timeout, outgoing_proxy,
-			pass_cookies, ssl_cert, ssl_key);
+			pass_cookies, ssl_cert, ssl_key, ssl_key_pwd);
 }
 
 /*
@@ -878,19 +1045,19 @@
 		json_t *json, const char *basic_auth, const char *bearer_token,
 		int ssl_validate_server, char **response, int timeout,
 		const char *outgoing_proxy, apr_array_header_t *pass_cookies,
-		const char *ssl_cert, const char *ssl_key) {
+		const char *ssl_cert, const char *ssl_key, const char *ssl_key_pwd) {
 	char *data =
 			json != NULL ?
 					oidc_util_encode_json_object(r, json, JSON_COMPACT) : NULL;
 	return oidc_util_http_call(r, url, data, OIDC_CONTENT_TYPE_JSON, basic_auth,
 			bearer_token, ssl_validate_server, response, timeout,
-			outgoing_proxy, pass_cookies, ssl_cert, ssl_key);
+			outgoing_proxy, pass_cookies, ssl_cert, ssl_key, ssl_key_pwd);
 }
 
 /*
  * get the current path from the request in a normalized way
  */
-static char *oidc_util_get_path(request_rec *r) {
+static char* oidc_util_get_path(request_rec *r) {
 	size_t i;
 	char *p;
 	p = r->parsed_uri.path;
@@ -905,7 +1072,7 @@
 /*
  * get the cookie path setting and check that it matches the request path; cook it up if it is not set
  */
-static char *oidc_util_get_cookie_path(request_rec *r) {
+static char* oidc_util_get_cookie_path(request_rec *r) {
 	char *rv = NULL, *requestPath = oidc_util_get_path(r);
 	char *cookie_path = oidc_cfg_dir_cookie_path(r);
 	if (cookie_path != NULL) {
@@ -933,7 +1100,7 @@
 
 #define OIDC_SET_COOKIE_APPEND_ENV_VAR  "OIDC_SET_COOKIE_APPEND"
 
-const char *oidc_util_set_cookie_append_value(request_rec *r, oidc_cfg *c) {
+const char* oidc_util_set_cookie_append_value(request_rec *r, oidc_cfg *c) {
 	const char *env_var_value = NULL;
 
 	if (r->subprocess_env != NULL)
@@ -952,6 +1119,10 @@
 	return env_var_value;
 }
 
+apr_byte_t oidc_util_request_is_secure(request_rec *r) {
+	return (apr_strnatcasecmp("https", oidc_get_current_url_scheme(r)) == 0);
+}
+
 /*
  * set a cookie in the HTTP response headers
  */
@@ -969,7 +1140,7 @@
 
 	/* construct the expire value */
 	if (expires != -1) {
-		expiresString = (char *) apr_pcalloc(r->pool, APR_RFC822_DATE_LEN);
+		expiresString = (char*) apr_pcalloc(r->pool, APR_RFC822_DATE_LEN);
 		if (apr_rfc822_date(expiresString, expires) != APR_SUCCESS) {
 			oidc_error(r, "could not set cookie expiry date");
 		}
@@ -989,7 +1160,7 @@
 		headerString = apr_psprintf(r->pool, "%s; %s=%s", headerString,
 				OIDC_COOKIE_FLAG_DOMAIN, c->cookie_domain);
 
-	if (apr_strnatcasecmp("https", oidc_get_current_url_scheme(r)) == 0)
+	if (oidc_util_request_is_secure(r))
 		headerString = apr_psprintf(r->pool, "%s; %s", headerString,
 				OIDC_COOKIE_FLAG_SECURE);
 
@@ -1018,7 +1189,7 @@
 /*
  * get a cookie from the HTTP request
  */
-char *oidc_util_get_cookie(request_rec *r, const char *cookieName) {
+char* oidc_util_get_cookie(request_rec *r, const char *cookieName) {
 	char *cookie, *tokenizerCtx, *rv = NULL;
 
 	/* get the Cookie value */
@@ -1063,7 +1234,7 @@
 /*
  * get the name of the cookie that contains the number of chunks
  */
-static char *oidc_util_get_chunk_count_name(request_rec *r,
+static char* oidc_util_get_chunk_count_name(request_rec *r,
 		const char *cookieName) {
 	return apr_psprintf(r->pool, "%s%s%s", cookieName,
 			OIDC_COOKIE_CHUNKS_SEPARATOR, OIDC_COOKIE_CHUNKS_POSTFIX);
@@ -1074,7 +1245,7 @@
  */
 static int oidc_util_get_chunked_count(request_rec *r, const char *cookieName) {
 	int chunkCount = 0;
-	char* chunkCountValue = oidc_util_get_cookie(r,
+	char *chunkCountValue = oidc_util_get_cookie(r,
 			oidc_util_get_chunk_count_name(r, cookieName));
 	if (chunkCountValue != NULL) {
 		char *endptr = NULL;
@@ -1088,7 +1259,7 @@
 /*
  * get the name of a chunk
  */
-static char *oidc_util_get_chunk_cookie_name(request_rec *r,
+static char* oidc_util_get_chunk_cookie_name(request_rec *r,
 		const char *cookieName, int i) {
 	return apr_psprintf(r->pool, "%s%s%d", cookieName,
 			OIDC_COOKIE_CHUNKS_SEPARATOR, i);
@@ -1097,7 +1268,7 @@
 /*
  * get a cookie value that is split over a number of chunked cookies
  */
-char *oidc_util_get_chunked_cookie(request_rec *r, const char *cookieName,
+char* oidc_util_get_chunked_cookie(request_rec *r, const char *cookieName,
 		int chunkSize) {
 	char *cookieValue = NULL;
 	char *chunkValue = NULL;
@@ -1185,7 +1356,7 @@
  * http://tools.ietf.org/html/rfc2616#section-2.2) are replaced with
  * a dash ('-') character.
  */
-char *oidc_normalize_header_name(const request_rec *r, const char *str) {
+char* oidc_normalize_header_name(const request_rec *r, const char *str) {
 	/* token = 1*<any CHAR except CTLs or separators>
 	 * CTL = <any US-ASCII control character
 	 *          (octets 0 - 31) and DEL (127)>
@@ -1223,7 +1394,7 @@
 /*
  * see if the currently accessed path has a certain query parameter
  */
-apr_byte_t oidc_util_request_has_parameter(request_rec *r, const char* param) {
+apr_byte_t oidc_util_request_has_parameter(request_rec *r, const char *param) {
 	if (r->args == NULL)
 		return FALSE;
 	const char *option1 = apr_psprintf(r->pool, "%s=", param);
@@ -1290,6 +1461,8 @@
 	return FALSE;
 }
 
+#define OIDC_JSON_MAX_ERROR_STR 4096
+
 /*
  * parse a JSON object
  */
@@ -1305,8 +1478,18 @@
 	/* decode the JSON contents of the buffer */
 	if (*json == NULL) {
 		/* something went wrong */
-		oidc_error(r, "JSON parsing returned an error: %s (%s)",
-				json_error.text, str);
+#if JANSSON_VERSION_HEX >= 0x020B00
+		if (json_error_code(&json_error) == json_error_null_character) {
+			oidc_error(r, "JSON parsing returned an error: %s",
+					json_error.text);
+		} else {
+#endif
+			oidc_error(r, "JSON parsing returned an error: %s (%s)",
+					json_error.text,
+					apr_pstrndup(r->pool, str, OIDC_JSON_MAX_ERROR_STR));
+#if JANSSON_VERSION_HEX >= 0x020B00
+		}
+#endif
 		return FALSE;
 	}
 
@@ -1324,7 +1507,7 @@
 /*
  * encode a JSON object
  */
-char *oidc_util_encode_json_object(request_rec *r, json_t *json, size_t flags) {
+char* oidc_util_encode_json_object(request_rec *r, json_t *json, size_t flags) {
 	char *s = json_dumps(json, flags);
 	char *s_value = apr_pstrdup(r->pool, s);
 	free(s);
@@ -1419,7 +1602,7 @@
  * get the full path to a file based on an (already) absolute filename or a filename
  * that is relative to the Apache root directory
  */
-char *oidc_util_get_full_path(apr_pool_t *pool, const char *abs_or_rel_filename) {
+char* oidc_util_get_full_path(apr_pool_t *pool, const char *abs_or_rel_filename) {
 	return (abs_or_rel_filename) ?
 			ap_server_root_relative(pool, abs_or_rel_filename) : NULL;
 }
@@ -1489,7 +1672,7 @@
 		return FALSE;
 	}
 
-	*rbuf = (char *) apr_palloc(r->pool, len + 1);
+	*rbuf = (char*) apr_palloc(r->pool, len + 1);
 	if (*rbuf == NULL) {
 		oidc_error(r, "could not allocate memory for %lu bytes of POST data.",
 				(unsigned long )len);
@@ -1539,7 +1722,7 @@
 static void oidc_userdata_set_post_param(request_rec *r,
 		const char *post_param_name, const char *post_param_value) {
 	apr_table_t *userdata_post_params = NULL;
-	apr_pool_userdata_get((void **) &userdata_post_params,
+	apr_pool_userdata_get((void**) &userdata_post_params,
 			OIDC_USERDATA_POST_PARAMS_KEY, r->pool);
 	if (userdata_post_params == NULL)
 		userdata_post_params = apr_table_make(r->pool, 1);
@@ -1562,10 +1745,9 @@
 	const char *content_type = NULL;
 
 	content_type = oidc_util_hdr_in_content_type_get(r);
-	if ((r->method_number != M_POST) || (strstr(content_type,
+	if ((r->method_number != M_POST) || (content_type == NULL) || (strstr(content_type,
 			OIDC_CONTENT_TYPE_FORM_ENCODED) != content_type)) {
-		oidc_debug(r, "required content-type %s not found",
-				OIDC_CONTENT_TYPE_FORM_ENCODED);
+		oidc_debug(r, "required content-type %s not found", OIDC_CONTENT_TYPE_FORM_ENCODED);
 		goto end;
 	}
 
@@ -1775,14 +1957,19 @@
  */
 void oidc_util_set_app_info(request_rec *r, const char *s_key,
 		const char *s_value, const char *claim_prefix, apr_byte_t as_header,
-		apr_byte_t as_env_var) {
+		apr_byte_t as_env_var, apr_byte_t base64url) {
 
 	/* construct the header name, cq. put the prefix in front of a normalized key name */
 	const char *s_name = apr_psprintf(r->pool, "%s%s", claim_prefix,
 			oidc_normalize_header_name(r, s_key));
+	char *d_value = NULL;
 
-	if (as_header)
-		oidc_util_hdr_in_set(r, s_name, s_value);
+	if (as_header) {
+		if ((base64url == TRUE) && (s_value != NULL)) {
+			oidc_base64url_encode(r, &d_value, s_value, strlen(s_value), TRUE);
+		}
+		oidc_util_hdr_in_set(r, s_name, (d_value != NULL) ? d_value : s_value);
+	}
 
 	if (as_env_var) {
 
@@ -1799,7 +1986,7 @@
  */
 void oidc_util_set_app_infos(request_rec *r, const json_t *j_attrs,
 		const char *claim_prefix, const char *claim_delimiter,
-		apr_byte_t as_header, apr_byte_t as_env_var) {
+		apr_byte_t as_header, apr_byte_t as_env_var, apr_byte_t base64url) {
 
 	char s_int[255];
 	json_t *j_value = NULL;
@@ -1828,21 +2015,21 @@
 
 			/* set the single string in the application header whose name is based on the key and the prefix */
 			oidc_util_set_app_info(r, s_key, json_string_value(j_value),
-					claim_prefix, as_header, as_env_var);
+					claim_prefix, as_header, as_env_var, base64url);
 
 		} else if (json_is_boolean(j_value)) {
 
 			/* set boolean value in the application header whose name is based on the key and the prefix */
 			oidc_util_set_app_info(r, s_key,
 					(json_is_true(j_value) ? "1" : "0"), claim_prefix,
-					as_header, as_env_var);
+					as_header, as_env_var, base64url);
 
 		} else if (json_is_integer(j_value)) {
 
 			if (sprintf(s_int, "%ld", (long) json_integer_value(j_value)) > 0) {
 				/* set long value in the application header whose name is based on the key and the prefix */
 				oidc_util_set_app_info(r, s_key, s_int, claim_prefix, as_header,
-						as_env_var);
+						as_env_var, base64url);
 			} else {
 				oidc_warn(r,
 						"could not convert JSON number to string (> 255 characters?), skipping");
@@ -1853,14 +2040,14 @@
 			/* set float value in the application header whose name is based on the key and the prefix */
 			oidc_util_set_app_info(r, s_key,
 					apr_psprintf(r->pool, "%lf", json_real_value(j_value)),
-					claim_prefix, as_header, as_env_var);
+					claim_prefix, as_header, as_env_var, base64url);
 
 		} else if (json_is_object(j_value)) {
 
 			/* set json value in the application header whose name is based on the key and the prefix */
 			oidc_util_set_app_info(r, s_key,
 					oidc_util_encode_json_object(r, j_value, 0), claim_prefix,
-					as_header, as_env_var);
+					as_header, as_env_var, base64url);
 
 			/* check if it is a multi-value string */
 		} else if (json_is_array(j_value)) {
@@ -1915,7 +2102,7 @@
 
 			/* set the concatenated string */
 			oidc_util_set_app_info(r, s_key, s_concat, claim_prefix, as_header,
-					as_env_var);
+					as_env_var, base64url);
 
 		} else {
 
@@ -1925,14 +2112,14 @@
 					j_value->type, s_key);
 		}
 
-		iter = json_object_iter_next((json_t *) j_attrs, iter);
+		iter = json_object_iter_next((json_t*) j_attrs, iter);
 	}
 }
 
 /*
  * parse a space separated string in to a hash table
  */
-apr_hash_t *oidc_util_spaced_string_to_hashtable(apr_pool_t *pool,
+apr_hash_t* oidc_util_spaced_string_to_hashtable(apr_pool_t *pool,
 		const char *str) {
 	char *val;
 	const char *data = apr_pstrdup(pool, str);
@@ -2066,8 +2253,8 @@
 		const char *p = params;
 		while (*p && (val = ap_getword(pool, &p, OIDC_CHAR_AMP))) {
 			key = ap_getword(pool, &val, OIDC_CHAR_EQUAL);
-			ap_unescape_url((char *) key);
-			ap_unescape_url((char *) val);
+			ap_unescape_url((char*) key);
+			ap_unescape_url((char*) val);
 			apr_table_add(table, key, val);
 		}
 	}
@@ -2086,13 +2273,13 @@
 	if ((client_secret != NULL) && (strlen(client_secret) > 0)) {
 
 		if (hash_algo == NULL) {
-			key = (unsigned char *) client_secret;
+			key = (unsigned char*) client_secret;
 			key_len = strlen(client_secret);
 		} else {
 			/* hash the client_secret first, this is OpenID Connect specific */
 			oidc_jose_hash_bytes(r->pool, hash_algo,
-					(const unsigned char *) client_secret,
-					strlen(client_secret), &key, &key_len, &err);
+					(const unsigned char*) client_secret, strlen(client_secret),
+					&key, &key_len, &err);
 		}
 
 		if ((key != NULL) && (key_len > 0)) {
@@ -2104,9 +2291,8 @@
 		}
 
 		if (*jwk == NULL) {
-			oidc_error(r,
-					"could not create JWK from the provided secret %s: %s",
-					client_secret, oidc_jose_e2s(r->pool, err));
+			oidc_error(r, "could not create JWK from the provided secret: %s",
+					oidc_jose_e2s(r->pool, err));
 			return FALSE;
 		}
 	}
@@ -2117,10 +2303,16 @@
 /*
  * merge provided keys and client secret in to a single hashtable
  */
-apr_hash_t * oidc_util_merge_symmetric_key(apr_pool_t *pool, apr_hash_t *keys,
-		oidc_jwk_t *jwk) {
-	apr_hash_t *result =
-			(keys != NULL) ? apr_hash_copy(pool, keys) : apr_hash_make(pool);
+apr_hash_t* oidc_util_merge_symmetric_key(apr_pool_t *pool,
+		const apr_array_header_t *keys, oidc_jwk_t *jwk) {
+	apr_hash_t *result = apr_hash_make(pool);
+	int i = 0;
+	if (keys != NULL) {
+		for (i = 0; i < keys->nelts; i++) {
+			const oidc_jwk_t *elem = ((const oidc_jwk_t**) keys->elts)[i];
+			apr_hash_set(result, elem->kid, APR_HASH_KEY_STRING, elem);
+		}
+	}
 	if (jwk != NULL) {
 		apr_hash_set(result, jwk->kid, APR_HASH_KEY_STRING, jwk);
 	}
@@ -2136,14 +2328,14 @@
 	unsigned char *hashed = NULL;
 	unsigned int hashed_len = 0;
 	if (oidc_jose_hash_bytes(r->pool, openssl_hash_algo,
-			(const unsigned char *) input, strlen(input), &hashed, &hashed_len,
+			(const unsigned char*) input, strlen(input), &hashed, &hashed_len,
 			&err) == FALSE) {
 		oidc_error(r, "oidc_jose_hash_bytes returned an error: %s", err.text);
 		return FALSE;
 	}
 
-	if (oidc_base64url_encode(r, output, (const char *) hashed, hashed_len,
-			TRUE) <= 0) {
+	if (oidc_base64url_encode(r, output, (const char*) hashed, hashed_len, TRUE)
+			<= 0) {
 		oidc_error(r, "oidc_base64url_encode returned an error: %s", err.text);
 		return FALSE;
 	}
@@ -2153,7 +2345,20 @@
 /*
  * merge two key sets
  */
-apr_hash_t * oidc_util_merge_key_sets(apr_pool_t *pool, apr_hash_t *k1,
+apr_hash_t* oidc_util_merge_key_sets(apr_pool_t *pool, apr_hash_t *k1,
+		const apr_array_header_t *k2) {
+	apr_hash_t *rv = k1 ? apr_hash_copy(pool, k1) : apr_hash_make(pool);
+	int i = 0;
+	if (k2 != NULL) {
+		for (i = 0; i < k2->nelts; i++) {
+			const oidc_jwk_t *jwk = ((const oidc_jwk_t**) k2->elts)[i];
+			apr_hash_set(rv, jwk->kid, APR_HASH_KEY_STRING, jwk);
+		}
+	}
+	return rv;
+}
+
+apr_hash_t* oidc_util_merge_key_sets_hash(apr_pool_t *pool, apr_hash_t *k1,
 		apr_hash_t *k2) {
 	if (k1 == NULL) {
 		if (k2 == NULL)
@@ -2310,14 +2515,14 @@
 	return TRUE;
 }
 
-static const char *oidc_util_hdr_in_get(const request_rec *r, const char *name) {
+static const char* oidc_util_hdr_in_get(const request_rec *r, const char *name) {
 	const char *value = apr_table_get(r->headers_in, name);
 	if (value)
 		oidc_debug(r, "%s=%s", name, value);
 	return value;
 }
 
-static const char *oidc_util_hdr_in_get_left_most_only(const request_rec *r,
+static const char* oidc_util_hdr_in_get_left_most_only(const request_rec *r,
 		const char *name, const char *separator) {
 	char *last = NULL;
 	const char *value = oidc_util_hdr_in_get(r, name);
@@ -2385,7 +2590,7 @@
 	oidc_util_hdr_table_set(r, r->headers_out, name, value);
 }
 
-static const char *oidc_util_hdr_out_get(const request_rec *r, const char *name) {
+static const char* oidc_util_hdr_out_get(const request_rec *r, const char *name) {
 	return apr_table_get(r->headers_out, name);
 }
 
@@ -2400,7 +2605,7 @@
 	oidc_util_hdr_table_set(r, r->headers_in, name, value);
 }
 
-const char *oidc_util_hdr_in_cookie_get(const request_rec *r) {
+const char* oidc_util_hdr_in_cookie_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_COOKIE);
 }
 
@@ -2408,28 +2613,28 @@
 	oidc_util_hdr_in_set(r, OIDC_HTTP_HDR_COOKIE, value);
 }
 
-const char *oidc_util_hdr_in_user_agent_get(const request_rec *r) {
+const char* oidc_util_hdr_in_user_agent_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_USER_AGENT);
 }
 
-const char *oidc_util_hdr_in_x_forwarded_for_get(const request_rec *r) {
+const char* oidc_util_hdr_in_x_forwarded_for_get(const request_rec *r) {
 	return oidc_util_hdr_in_get_left_most_only(r, OIDC_HTTP_HDR_X_FORWARDED_FOR,
 			OIDC_STR_COMMA OIDC_STR_SPACE);
 }
 
-const char *oidc_util_hdr_in_content_type_get(const request_rec *r) {
+const char* oidc_util_hdr_in_content_type_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_CONTENT_TYPE);
 }
 
-const char *oidc_util_hdr_in_content_length_get(const request_rec *r) {
+const char* oidc_util_hdr_in_content_length_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_CONTENT_LENGTH);
 }
 
-const char *oidc_util_hdr_in_x_requested_with_get(const request_rec *r) {
+const char* oidc_util_hdr_in_x_requested_with_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_X_REQUESTED_WITH);
 }
 
-const char *oidc_util_hdr_in_accept_get(const request_rec *r) {
+const char* oidc_util_hdr_in_accept_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_ACCEPT);
 }
 
@@ -2439,26 +2644,26 @@
 			OIDC_CHAR_SEMI_COLON, needle);
 }
 
-const char *oidc_util_hdr_in_authorization_get(const request_rec *r) {
+const char* oidc_util_hdr_in_authorization_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_AUTHORIZATION);
 }
 
-const char *oidc_util_hdr_in_x_forwarded_proto_get(const request_rec *r) {
+const char* oidc_util_hdr_in_x_forwarded_proto_get(const request_rec *r) {
 	return oidc_util_hdr_in_get_left_most_only(r,
 			OIDC_HTTP_HDR_X_FORWARDED_PROTO, OIDC_STR_COMMA OIDC_STR_SPACE);
 }
 
-const char *oidc_util_hdr_in_x_forwarded_port_get(const request_rec *r) {
+const char* oidc_util_hdr_in_x_forwarded_port_get(const request_rec *r) {
 	return oidc_util_hdr_in_get_left_most_only(r,
 			OIDC_HTTP_HDR_X_FORWARDED_PORT, OIDC_STR_COMMA OIDC_STR_SPACE);
 }
 
-const char *oidc_util_hdr_in_x_forwarded_host_get(const request_rec *r) {
+const char* oidc_util_hdr_in_x_forwarded_host_get(const request_rec *r) {
 	return oidc_util_hdr_in_get_left_most_only(r,
 			OIDC_HTTP_HDR_X_FORWARDED_HOST, OIDC_STR_COMMA OIDC_STR_SPACE);
 }
 
-const char *oidc_util_hdr_in_host_get(const request_rec *r) {
+const char* oidc_util_hdr_in_host_get(const request_rec *r) {
 	return oidc_util_hdr_in_get(r, OIDC_HTTP_HDR_HOST);
 }
 
@@ -2466,18 +2671,18 @@
 	oidc_util_hdr_out_set(r, OIDC_HTTP_HDR_LOCATION, value);
 }
 
-const char *oidc_util_hdr_out_location_get(const request_rec *r) {
+const char* oidc_util_hdr_out_location_get(const request_rec *r) {
 	return oidc_util_hdr_out_get(r, OIDC_HTTP_HDR_LOCATION);
 }
 
-const char *oidc_util_get_provided_token_binding_id(const request_rec *r) {
+const char* oidc_util_get_provided_token_binding_id(const request_rec *r) {
 	const char *result = NULL;
 	if (r->subprocess_env != NULL)
 		result = apr_table_get(r->subprocess_env, OIDC_TB_CFG_PROVIDED_ENV_VAR);
 	return result;
 }
 
-const char *oidc_util_get_client_cert_fingerprint(request_rec *r) {
+const char* oidc_util_get_client_cert_fingerprint(request_rec *r) {
 	const char *fingerprint = NULL;
 
 	if (r->subprocess_env == NULL)
@@ -2521,7 +2726,7 @@
 	}
 
 	if (oidc_jose_hash_bytes(r->pool, OIDC_JOSE_ALG_SHA256,
-			(const unsigned char *) tbp, tbp_len, &tbp_hash, &tbp_hash_len,
+			(const unsigned char*) tbp, tbp_len, &tbp_hash, &tbp_hash_len,
 			NULL) == FALSE) {
 		oidc_warn(r,
 				"hashing Provided Token Binding ID environment variable failed");
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/test/.gitignore libapache2-mod-auth-openidc-2.4.9/test/.gitignore
--- libapache2-mod-auth-openidc-2.4.4.1/test/.gitignore	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/test/.gitignore	2021-07-22 18:31:06.000000000 +0200
@@ -9,3 +9,6 @@
 /*.json
 /*.jwt
 /*.jwk
+/jmx/
+/mod_auth_openidc.results
+/test-cmd-licensed
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/test/mod_auth_openidc.jmx libapache2-mod-auth-openidc-2.4.9/test/mod_auth_openidc.jmx
--- libapache2-mod-auth-openidc-2.4.4.1/test/mod_auth_openidc.jmx	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/test/mod_auth_openidc.jmx	2021-07-22 18:31:06.000000000 +0200
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<jmeterTestPlan version="1.2" properties="3.1" jmeter="3.1.20170220">
+<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1.1.20190725">
   <hashTree>
     <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="OpenID Connect Apache Module Test Plan" enabled="true">
       <stringProp name="TestPlan.comments"></stringProp>
@@ -7,8 +7,8 @@
       <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
       <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
         <collectionProp name="Arguments.arguments">
-          <elementProp name="X_APP_SERVER" elementType="Argument">
-            <stringProp name="Argument.name">X_APP_SERVER</stringProp>
+          <elementProp name="APP_SERVER" elementType="Argument">
+            <stringProp name="Argument.name">APP_SERVER</stringProp>
             <stringProp name="Argument.value">localhost.zmartzone.eu</stringProp>
             <stringProp name="Argument.metadata">=</stringProp>
           </elementProp>
@@ -57,7 +57,7 @@
           </elementProp>
           <elementProp name="APP_PATH_ACCESS" elementType="Argument">
             <stringProp name="Argument.name">APP_PATH_ACCESS</stringProp>
-            <stringProp name="Argument.value">/protected/api/index.php</stringProp>
+            <stringProp name="Argument.value">/api/index.php</stringProp>
             <stringProp name="Argument.metadata">=</stringProp>
           </elementProp>
         </collectionProp>
@@ -94,8 +94,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${APP_SERVER}</stringProp>
           <stringProp name="HTTPSampler.port">${APP_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">${APP_PATH_USER}</stringProp>
@@ -104,9 +102,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab AuthorizationRequestPath" enabled="true">
@@ -125,8 +124,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${AS_HOST}</stringProp>
           <stringProp name="HTTPSampler.port">${AS_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">/${AuthorizationRequestPath}</stringProp>
@@ -135,9 +132,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab AuthzResumePath" enabled="true">
@@ -164,8 +162,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${AS_HOST}</stringProp>
           <stringProp name="HTTPSampler.port">${AS_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">${AuthzResumePath}</stringProp>
@@ -174,9 +170,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab ResumeParam" enabled="true">
@@ -226,8 +223,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${AS_HOST}</stringProp>
           <stringProp name="HTTPSampler.port">${AS_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">/IdpSample/MainPage?cmd=login</stringProp>
@@ -236,9 +231,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab ResumePath" enabled="true">
@@ -257,8 +253,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${AS_HOST}</stringProp>
           <stringProp name="HTTPSampler.port">${AS_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">${ResumePath}</stringProp>
@@ -267,9 +261,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab RedirectURIPath" enabled="true">
@@ -321,8 +316,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${APP_SERVER}</stringProp>
           <stringProp name="HTTPSampler.port">${APP_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">/${RedirectURIPath}</stringProp>
@@ -331,9 +324,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab ApplicationPath" enabled="true">
@@ -367,8 +361,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${AS_HOST}</stringProp>
           <stringProp name="HTTPSampler.port">${AS_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">${AuthzResumePath}</stringProp>
@@ -377,9 +369,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab cSRFToken Parameter" enabled="true">
@@ -441,8 +434,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${AS_HOST}</stringProp>
           <stringProp name="HTTPSampler.port">${AS_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">${AuthzResumePath}</stringProp>
@@ -451,9 +442,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab LocationPath" enabled="true">
@@ -472,8 +464,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${APP_SERVER}</stringProp>
           <stringProp name="HTTPSampler.port">${APP_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">/${LocationPath}</stringProp>
@@ -482,9 +472,10 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab ApplicationPath" enabled="true">
@@ -517,8 +508,6 @@
             </elementProp>
             <stringProp name="HTTPSampler.domain">${APP_SERVER}</stringProp>
             <stringProp name="HTTPSampler.port">${APP_PORT}</stringProp>
-            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-            <stringProp name="HTTPSampler.response_timeout"></stringProp>
             <stringProp name="HTTPSampler.protocol">https</stringProp>
             <stringProp name="HTTPSampler.contentEncoding"></stringProp>
             <stringProp name="HTTPSampler.path">/${ApplicationPath}</stringProp>
@@ -527,9 +516,10 @@
             <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
             <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
             <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-            <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
-            <boolProp name="HTTPSampler.monitor">false</boolProp>
             <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+            <stringProp name="HTTPSampler.implementation">HttpClient4</stringProp>
+            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+            <stringProp name="HTTPSampler.response_timeout"></stringProp>
           </HTTPSamplerProxy>
           <hashTree>
             <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert User Logged In" enabled="true">
@@ -539,6 +529,7 @@
               <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
               <boolProp name="Assertion.assume_success">false</boolProp>
               <intProp name="Assertion.test_type">2</intProp>
+              <stringProp name="Assertion.custom_message"></stringProp>
             </ResponseAssertion>
             <hashTree/>
             <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert Fake Header Removal" enabled="true">
@@ -548,6 +539,7 @@
               <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
               <boolProp name="Assertion.assume_success">false</boolProp>
               <intProp name="Assertion.test_type">6</intProp>
+              <stringProp name="Assertion.custom_message"></stringProp>
             </ResponseAssertion>
             <hashTree/>
           </hashTree>
@@ -567,18 +559,17 @@
           <stringProp name="shareMode">shareMode.all</stringProp>
           <boolProp name="stopThread">false</boolProp>
           <stringProp name="variableNames">USERNAME,PASSWORD</stringProp>
+          <boolProp name="ignoreFirstLine">false</boolProp>
         </CSVDataSet>
         <hashTree/>
-        <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="Hosts Data Set" enabled="true">
-          <stringProp name="filename">hosts.txt</stringProp>
-          <stringProp name="fileEncoding"></stringProp>
-          <stringProp name="variableNames">APP_SERVER</stringProp>
-          <stringProp name="delimiter">,</stringProp>
-          <boolProp name="quotedData">false</boolProp>
-          <boolProp name="recycle">true</boolProp>
-          <boolProp name="stopThread">false</boolProp>
-          <stringProp name="shareMode">shareMode.all</stringProp>
-        </CSVDataSet>
+        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
+          <collectionProp name="HeaderManager.headers">
+            <elementProp name="" elementType="Header">
+              <stringProp name="Header.name">Accept</stringProp>
+              <stringProp name="Header.value">*/*</stringProp>
+            </elementProp>
+          </collectionProp>
+        </HeaderManager>
         <hashTree/>
       </hashTree>
       <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OAuth Resource Access" enabled="true">
@@ -631,8 +622,6 @@
           </elementProp>
           <stringProp name="HTTPSampler.domain">${AS_HOST}</stringProp>
           <stringProp name="HTTPSampler.port">${AS_PORT}</stringProp>
-          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-          <stringProp name="HTTPSampler.response_timeout"></stringProp>
           <stringProp name="HTTPSampler.protocol">https</stringProp>
           <stringProp name="HTTPSampler.contentEncoding"></stringProp>
           <stringProp name="HTTPSampler.path">${AS_PATH_TOKEN}</stringProp>
@@ -641,8 +630,9 @@
           <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
           <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
           <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-          <boolProp name="HTTPSampler.monitor">false</boolProp>
           <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+          <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+          <stringProp name="HTTPSampler.response_timeout"></stringProp>
         </HTTPSamplerProxy>
         <hashTree>
           <RegexExtractor guiclass="RegexExtractorGui" testclass="RegexExtractor" testname="Grab AccessToken" enabled="true">
@@ -661,6 +651,10 @@
               <stringProp name="Header.name">Authorization</stringProp>
               <stringProp name="Header.value">bearer ${AccessToken}</stringProp>
             </elementProp>
+            <elementProp name="" elementType="Header">
+              <stringProp name="Header.name">Accept</stringProp>
+              <stringProp name="Header.value">application/json</stringProp>
+            </elementProp>
           </collectionProp>
         </HeaderManager>
         <hashTree/>
@@ -675,8 +669,6 @@
             </elementProp>
             <stringProp name="HTTPSampler.domain">${APP_SERVER}</stringProp>
             <stringProp name="HTTPSampler.port">${APP_PORT}</stringProp>
-            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
-            <stringProp name="HTTPSampler.response_timeout"></stringProp>
             <stringProp name="HTTPSampler.protocol">https</stringProp>
             <stringProp name="HTTPSampler.contentEncoding"></stringProp>
             <stringProp name="HTTPSampler.path">${APP_PATH_ACCESS}</stringProp>
@@ -685,32 +677,23 @@
             <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
             <boolProp name="HTTPSampler.use_keepalive">false</boolProp>
             <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
-            <boolProp name="HTTPSampler.monitor">false</boolProp>
             <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
+            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
+            <stringProp name="HTTPSampler.response_timeout"></stringProp>
           </HTTPSamplerProxy>
           <hashTree>
             <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Assert AS_USERNAME" enabled="true">
               <collectionProp name="Asserion.test_strings">
                 <stringProp name="240033137">\[OIDC_CLAIM_Username\] =&gt; ${AS_USERNAME}</stringProp>
               </collectionProp>
-              <stringProp name="Assertion.test_field">Assertion.response_data_as_document</stringProp>
+              <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
               <boolProp name="Assertion.assume_success">false</boolProp>
               <intProp name="Assertion.test_type">2</intProp>
+              <stringProp name="Assertion.custom_message"></stringProp>
             </ResponseAssertion>
             <hashTree/>
           </hashTree>
         </hashTree>
-        <CSVDataSet guiclass="TestBeanGUI" testclass="CSVDataSet" testname="Hosts Data Set" enabled="true">
-          <stringProp name="filename">hosts.txt</stringProp>
-          <stringProp name="fileEncoding"></stringProp>
-          <stringProp name="variableNames">APP_SERVER</stringProp>
-          <stringProp name="delimiter">,</stringProp>
-          <boolProp name="quotedData">false</boolProp>
-          <boolProp name="recycle">true</boolProp>
-          <boolProp name="stopThread">false</boolProp>
-          <stringProp name="shareMode">shareMode.all</stringProp>
-        </CSVDataSet>
-        <hashTree/>
       </hashTree>
       <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
         <boolProp name="ResultCollector.error_logging">true</boolProp>
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/test/stub.c libapache2-mod-auth-openidc-2.4.9/test/stub.c
--- libapache2-mod-auth-openidc-2.4.4.1/test/stub.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/test/stub.c	2021-07-22 18:31:06.000000000 +0200
@@ -37,6 +37,13 @@
 	return 0;
 }
 
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
+AP_DECLARE(char *) ap_get_exec_line(apr_pool_t *p, const char *cmd,
+		const char * const *argv) {
+	return NULL;
+}
+#endif
+
 AP_DECLARE(char *) ap_getword(apr_pool_t *atrans, const char **line, char stop) {
 	const char *pos = *line;
 	int len;
@@ -298,8 +305,9 @@
 			return NULL;
 		}
 
+#if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
 		AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *expr,
 				const char **err) {
 			return 0;
 		}
-
+#endif
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/test/test.c libapache2-mod-auth-openidc-2.4.9/test/test.c
--- libapache2-mod-auth-openidc-2.4.4.1/test/test.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/test/test.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
@@ -1293,6 +1285,16 @@
 	TST_ASSERT_STR("test_current_url (8)", url,
 			"http://remotehost:8380/private/?foo=bar&param1=value1";);
 
+	apr_table_set(r->headers_in, "Host", "[fd04:41b1:1170:28:16b0:446b:9fb7:7118]:8380");
+	url = oidc_get_current_url(r);
+	TST_ASSERT_STR("test_current_url (9)", url,
+			"http://[fd04:41b1:1170:28:16b0:446b:9fb7:7118]:8380/private/?foo=bar&param1=value1";);
+
+	apr_table_set(r->headers_in, "Host", "[fd04:41b1:1170:28:16b0:446b:9fb7:7118]");
+	url = oidc_get_current_url(r);
+	TST_ASSERT_STR("test_current_url (10)", url,
+			"http://[fd04:41b1:1170:28:16b0:446b:9fb7:7118]/private/?foo=bar&param1=value1";);
+
 	return 0;
 }
 
@@ -1486,6 +1488,22 @@
 
 #endif
 
+
+static char * test_decode_json_object(request_rec *r) {
+	apr_byte_t rc = FALSE;
+	json_t *json = NULL;
+	rc = oidc_util_decode_json_object(r, "nojson", &json);
+	TST_ASSERT("test invalid JSON", rc == FALSE);
+	rc = oidc_util_decode_json_object(r, "{ \"n\": \"\\u0000<?php echo 'Hello' ?>\"}", &json);
+	TST_ASSERT("test JSON with NULL value", rc == FALSE);
+	rc = oidc_util_decode_json_object(r, "tmjcbnuvyrtygbtbyizkfuabiddgixcvnvupjuwnvxznpspmjaqrlpgmggixxovrpwntkvsvxjtkjjggnevyfyemdrlxtnmzjstmjuyquyyjzzwsfrazgzbdojkcfaeiqawltqsiwwzzgpiikpqoxixhsqtnfbchrcgxbgiaynkscvbvfnpuddrpjbgdtxxlebrswrtukzxqyyfrmwrrtfhcxhjfdoswjzvcchlufkqdaiqakvhyssegikcdkxvqxjrxukllhjduhokudtmhkqhqjheedxnlpbtybpwwogynneilkyffixdchdcjopxtdnhgsinwwktpqxfhmlfwucbtlbojaatocwqxsivdwwxrscsviwtllrqakzyogvseiackzzkkioactssxcglqqgavcpxmfokufechkdjkwbvdcnyboqbinbitixuqxeafdhrzndljsnqdlvxwvzggltmelmdcfouthhhzjuehfoejfyjvrpakmgmhaigkidmpmjtwrgezmwyvkgirhkxwrakbaizldjrcjbwieewdnxmrlgcmnuvhidvkqdokguvphmzywgqgfuwshouxcadbtkoxesyoqikuotloiowjpvztunkguyyrnbmsnwbpghharmvkoqjjoanbejbwdlewiebwkzsuxpxozqzteuozboxdaukiwbqduhdhdlgiewmgeeqwtgyvhexcsdthriprrxvpuqvlcgqlldpnjdtbcuxncoikjxxistytsdlzdmtevmxhoafdcwqqixzxnxnrcoqlkwosdxsktgdejqsopuzaqourcixwktuwjzqagtdjheqgkpeavaoxpechkkdxcrwdtuxvdwpyjgjtsgppnoudyjyslwzxcqtbeqdtppeoelefkumpaamkxjnmdbuzotdzjrwzjspgabxczvxiuogvtmkmvxitdezsrqskorsybwndftfoqrylgsnhetyksfjdctmmarhtxpjukhwxhzjorxgnbpskdzgmskiumcsyquuwknrmkvdxhwgztbbbgiogeynlertjprolkavghiatjaddwlacrlibdnbbhykaqepwujqkmylnnqcfmxqanfvxgmitmsgwotolcwqrxgaftsryrmvlnabnouwqubtocwhkerhvsyzgirkitehbpcvitmjndqwerqopsdhwenihhkybemtmrafhcqbbfrfhemzvklvzxykkczjdokblktoxknolzxgblsjplggysbutvddygjgaqntcavbopwanbdvijawdwuepvzcaoesbkdaaqevbaupyokttlgcplpfijaubzphwutmslimkdxtrgwrfhhvdygznabvhvfhhpuuzwkhaxurnnqrmrhmddkckxzrmzxavvfpyuagjfyjorlfwprlpmwyjbtyzidvabtjckfqesnumqddnrdkunxljarqxcokikkqvrwxfedfumbadugcvzigbdulrcmzihpepztbplwrcvunyrhlwdutawmroajvomwtnbntcdgeqnyjxqlgjcdkaxlezmrldewjvyfjljeskmebuepmbzlhludyzwfnjomfnbgdcseqvqdlxrsuyyakdfsssiobahylumrwzsztpwxvfxyqfhtubamboeykqjltmpdjzhrsdjfebwjhlsyrafmvhefyddxhkchaqxptrqewevrnqftswhhxezdxyowtstejeexrgiujxbxvrvukotxdtlbgdpmorbaglozcmkzyrgnsjauxybuwvozcatacoyrvuxpiqkppoiogpuiykdpxkefxvqwutsbdeerbhztiidxqxsahcbbbrhlqwwnstpsefwtunismgvhbcpholzedkivglyjoperxpfavaxqohcxrcmnfvlzqtflrslurdbxnyoswimovpteleifarnhrivasnxetxnwyumoiwomilmnmpnpfekpwiwpmlqumkfagjbxpswraxctsyvsfspoesfhfdifcexfknbbszjfznpwkftkescltnmmodctjmkwdlrgorauknioeqexwwqdkdhdielfewwenuhsosehfksfaybktumfaprcjgvgenfdffbgduawryrvlbphdjsryjzpspymqhflowqzvesljaasfjvheeqikfocyxuopsgsojcqmyfkwkspnspjewmsxljvezucnpuobfqblakqwbixswbbtuboneixdjhdbzdckkcarsswtutwhkjijwdtsyjirdcibtavigjnehntnpdpdugwryxybsqoboayifiwxyuiqlbjksjpuhwdglymyisjdhkovwexkroooorosiudclbrldesiolbrbeusyrpuuypioziymwdguquitxuiaxeayabeccbrislwinxxyumylbncyshsywkzdyqoaqtwbulnseynghoomiypzefykoexnzhwdcqoctptqabzbuiwxyahaptodtufofabylxtlggldckhuicuqdbueuuoknehkkuypnjwqfhocshmofgbmokiuxvcdonwuwqbjxgdukuubswwvxxqugjxnngnbnvtcophhamvbcuxdfkqubfiftrtfyljjulmansvnhnaoczrmxlqokbovddmpydamzicfzkmwcgecljucaezvhrcssupxkleiximldarmtszakjuficulnaefldyogmnavrfscsptejaexghcrqtnluzmxvxrixxgjtyujoruzzzrfgtymtfsdyrmhvvggyeeykdaglptsuqmqbknimmxnuftjjolcvhpdumqehmyevqlsotrsldgedxmuablgarjfwoqyuakqxbqewdlsfkamxrteaebqwwcsupefoshtwlghkqtqjddikfrwjilzuqfwpcxalauddfotnwlsdwpxynlqjpfcuodeyubenztxsjwxycrdhlluauutfrvgqzacvnfawqibftocxrksltmjvlnyjfeagoxpcepioattazmuuklhjxhpbughkmyjdgrxwqwdafgbnerlhlngeqojhjuvwwmqdradedjuugcqzkfozddctctckaqoruksjsqjpzdbvmocutxpnacbkihboujejmjdhorkpyzubhaxpksjfpzmwauwxkwyjisfsqdjdkvsifgxjygclbdtzultcejefyephhhgbazkqqdvhtkkllyopdcsnbqjcpyvienhqqkwyxfvjrgsymuxxsejqerumcldjuitavdgogcsvjgbwllaijavuaeqxvndvzyrlmmzrhijywctihqizqmjfosrddqqdyilnfzyvwkkqgnnhlajqgdsnhhzpphfjtkeafxlgaarcfdapicfmukyyzvbgoatibulenkkwtyrnzgbmcytazrieabuerwkxoffzuohdhqqhfbxqmqestunudduaywtrmdmbvidyvterebgvxwbhlbsqmghcktujvhfwmhralmodiywvyzvurzghwcpxqtzwmhnderhpognxnynmrfraklflrgppszmuxtwddrkzvqyvvmhlwcnzmspekcwhphabtmzdgvfyrvdzimpxbrkjbntiixkgxhepzqrugnmjyfbkcacbdgxbhraauoagihygwkiikuyximjjdnvslnfaouofwgdacnndhramvxazuzksploibonvneeixkykpwjmrlwkvbesxaklqkoaulskwdfstelqxyyrpvnzkjcmhvxbjvbmrgdoyiwlzubaovirtciwcptmrdggpcgtxptkfwjsnhbxprqjiezncmmypjfbgljzrawwdikhoaqggoizoixpnykwyotofdrduvgfcwxvzjuacxolorrfpunnkzltgbdkztiwjctjedtupmckbjajwcjnkbmywilylfhckksaowsbvhnktfeklaekpflbtsqpyxhrcwmjjgnqtmoumvcswredhtexnaojzjagrvwcieizjfvvmzzxmzwwvqthmrqvtviuyiqffjdpqmeknhwylmteliysiaenkhkiuojxdwscvtacbwfixhrcaxlfeakidxgrmgitrmrzdzhwjyazzikrclajgksENDxxxx", &json);
+	TST_ASSERT("test invalid long JSON", rc == FALSE);
+	rc = oidc_util_decode_json_object(r, "{}", &json);
+	TST_ASSERT("test valid JSON", rc == TRUE);
+	json_decref(json);
+	return 0;
+}
+
 static char * all_tests(apr_pool_t *pool, request_rec *r) {
 	char *message;
 	TST_RUN(test_public_key_parse, pool);
@@ -1519,6 +1537,8 @@
 	TST_RUN(test_current_url, r);
 	TST_RUN(test_accept, r);
 
+	TST_RUN(test_decode_json_object, r);
+
 #if MODULE_MAGIC_NUMBER_MAJOR >= 20100714
 	TST_RUN(test_authz_worker, r);
 #endif
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/test/test-cmd.c libapache2-mod-auth-openidc-2.4.9/test/test-cmd.c
--- libapache2-mod-auth-openidc-2.4.4.1/test/test-cmd.c	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/test/test-cmd.c	2021-07-22 18:31:06.000000000 +0200
@@ -18,18 +18,10 @@
  */
 
 /***************************************************************************
- * Copyright (C) 2017-2020 ZmartZone IAM
+ * Copyright (C) 2017-2021 ZmartZone Holding BV
  * Copyright (C) 2013-2017 Ping Identity Corporation
  * All rights reserved.
  *
- * For further information please contact:
- *
- *      Ping Identity Corporation
- *      1099 18th St Suite 2950
- *      Denver, CO 80202
- *      303.468.2900
- *      http://www.pingidentity.com
- *
  * DISCLAIMER OF WARRANTIES:
  *
  * THE SOFTWARE PROVIDED HEREUNDER IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
diff -Nru libapache2-mod-auth-openidc-2.4.4.1/.travis.yml libapache2-mod-auth-openidc-2.4.9/.travis.yml
--- libapache2-mod-auth-openidc-2.4.4.1/.travis.yml	2020-09-03 16:52:30.000000000 +0200
+++ libapache2-mod-auth-openidc-2.4.9/.travis.yml	2021-07-22 18:31:06.000000000 +0200
@@ -1,7 +1,5 @@
 language: c
 
-dist: trusty
-
 arch:
   - amd64
   - ppc64le

Attachment: OpenPGP_signature
Description: OpenPGP digital signature


Reply to: