Skip to content

Commit 48db646

Browse files
authored
OIDC id token validation and token refresh (helidon-io#8153)
OIDC id token authentication and access token refreshing Signed-off-by: David Kral <david.k.kral@oracle.com>
1 parent 1899ca1 commit 48db646

13 files changed

Lines changed: 866 additions & 63 deletions

File tree

docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_OidcProvider.adoc

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///////////////////////////////////////////////////////////////////////////////
22

3-
Copyright (c) 2023 Oracle and/or its affiliates.
3+
Copyright (c) 2023, 2024 Oracle and/or its affiliates.
44

55
Licensed under the Apache License, Version 2.0 (the "License");
66
you may not use this file except in compliance with the License.
@@ -71,13 +71,35 @@ This type provides the following service implementations:
7171
|`client-timeout-millis` |Duration |`30000` |Timeout of calls using web client.
7272
|`cookie-domain` |string |{nbsp} |Domain the cookie is valid for.
7373
Not used by default.
74+
|`cookie-encryption-enabled` |boolean |`false` |Whether to encrypt token cookie created by this microservice.
75+
Defaults to `false`.
76+
|`cookie-encryption-enabled-id-token` |boolean |`true` |Whether to encrypt id token cookie created by this microservice.
77+
Defaults to `true`.
78+
|`cookie-encryption-enabled-refresh-token` |boolean |`true` |Whether to encrypt refresh token cookie created by this microservice.
79+
Defaults to `true`.
80+
|`cookie-encryption-enabled-tenant-name` |boolean |`true` |Whether to encrypt tenant name cookie created by this microservice.
81+
Defaults to `true`.
82+
|`cookie-encryption-name` |string |{nbsp} |Name of the encryption configuration available through Security#encrypt(String, byte[]) and
83+
Security#decrypt(String, String).
84+
If configured and encryption is enabled for any cookie,
85+
Security MUST be configured in global or current `io.helidon.common.context.Context` (this
86+
is done automatically in Helidon MP).
87+
|`cookie-encryption-password` |char[] |{nbsp} |Master password for encryption/decryption of cookies. This must be configured to the same value on each microservice
88+
using the cookie.
7489
|`cookie-http-only` |boolean |`true` |When using cookie, if set to true, the HttpOnly attribute will be configured.
7590
Defaults to `OidcCookieHandler.Builder#DEFAULT_HTTP_ONLY`.
7691
|`cookie-max-age-seconds` |long |{nbsp} |When using cookie, used to set MaxAge attribute of the cookie, defining how long
7792
the cookie is valid.
7893
Not used by default.
7994
|`cookie-name` |string |`JSESSIONID` |Name of the cookie to use.
8095
Defaults to `DEFAULT_COOKIE_NAME`.
96+
|`cookie-name-id-token` |string |`JSESSIONID_2` |Name of the cookie to use for id token.
97+
Defaults to `DEFAULT_COOKIE_NAME`_2.
98+
99+
This cookie is only used when logout is enabled, as otherwise it is not needed.
100+
Content of this cookie is encrypted.
101+
|`cookie-name-refresh-token` |string |`JSESSIONID_3` |The name of the cookie to use for the refresh token.
102+
Defaults to `DEFAULT_REFRESH_COOKIE_NAME`.
81103
|`cookie-name-tenant` |string |`HELIDON_TENANT` |The name of the cookie to use for the tenant name.
82104
Defaults to `DEFAULT_TENANT_COOKIE_NAME`.
83105
|`cookie-path` |string |`/` |Path the cookie is valid for.
@@ -97,6 +119,9 @@ This type provides the following service implementations:
97119
process header containing a JWT.
98120
Default is "Authorization" header with a prefix "bearer ".
99121
|`header-use` |boolean |`true` |Whether to expect JWT in a header field.
122+
|`id-token-signature-validation` |boolean |`true` |Whether id token signature check should be enabled.
123+
Signature check is enabled by default, and it is highly recommended to not change that.
124+
Change this setting only when you really know what you are doing, otherwise it could case security issues.
100125
|`identity-uri` |URI |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata.
101126
|`introspect-endpoint-uri` |URI |{nbsp} |Endpoint to use to validate JWT.
102127
Either use this or set #signJwk(JwkKeys) or #signJwk(Resource).
@@ -123,7 +148,8 @@ This type provides the following service implementations:
123148
Defaults to `DEFAULT_PROXY_PORT`
124149
|`proxy-protocol` |string |`http` |Proxy protocol to use when proxy is used.
125150
Defaults to `DEFAULT_PROXY_PROTOCOL`.
126-
|`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT token when parameter is used.
151+
|`query-id-token-param-name` |string |`id_token` |Name of a query parameter that contains the JWT id token when parameter is used.
152+
|`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT access token when parameter is used.
127153
|`query-param-tenant-name` |string |`h_tenant` |Name of a query parameter that contains the tenant name when the parameter is used.
128154
Defaults to #DEFAULT_TENANT_PARAM_NAME.
129155
|`query-param-use` |boolean |`false` |Whether to use a query parameter to send JWT token from application to this
@@ -167,6 +193,9 @@ This type provides the following service implementations:
167193
code.
168194
If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined
169195
an attempt is made to use #identityUri(URI)/oauth2/v1/token.
196+
|`token-signature-validation` |boolean |`true` |Whether access token signature check should be enabled.
197+
Signature check is enabled by default, and it is highly recommended to not change that.
198+
Change this setting only when you really know what you are doing, otherwise it could case security issues.
170199
|`use-jwt-groups` |boolean |`true` |Claim `groups` from JWT will be used to automatically add
171200
groups to current subject (may be used with jakarta.annotation.security.RolesAllowed annotation).
172201
|`validate-jwt-with-jwk` |boolean |`true` |Use JWK (a set of keys to validate signatures of JWT) to validate tokens.

docs/src/main/asciidoc/config/io_helidon_security_providers_oidc_common_OidcConfig.adoc

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
///////////////////////////////////////////////////////////////////////////////
22

3-
Copyright (c) 2023 Oracle and/or its affiliates.
3+
Copyright (c) 2023, 2024 Oracle and/or its affiliates.
44

55
Licensed under the Apache License, Version 2.0 (the "License");
66
you may not use this file except in compliance with the License.
@@ -60,13 +60,35 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid
6060
|`client-timeout-millis` |Duration |`30000` |Timeout of calls using web client.
6161
|`cookie-domain` |string |{nbsp} |Domain the cookie is valid for.
6262
Not used by default.
63+
|`cookie-encryption-enabled` |boolean |`false` |Whether to encrypt token cookie created by this microservice.
64+
Defaults to `false`.
65+
|`cookie-encryption-enabled-id-token` |boolean |`true` |Whether to encrypt id token cookie created by this microservice.
66+
Defaults to `true`.
67+
|`cookie-encryption-enabled-refresh-token` |boolean |`true` |Whether to encrypt refresh token cookie created by this microservice.
68+
Defaults to `true`.
69+
|`cookie-encryption-enabled-tenant-name` |boolean |`true` |Whether to encrypt tenant name cookie created by this microservice.
70+
Defaults to `true`.
71+
|`cookie-encryption-name` |string |{nbsp} |Name of the encryption configuration available through Security#encrypt(String, byte[]) and
72+
Security#decrypt(String, String).
73+
If configured and encryption is enabled for any cookie,
74+
Security MUST be configured in global or current `io.helidon.common.context.Context` (this
75+
is done automatically in Helidon MP).
76+
|`cookie-encryption-password` |char[] |{nbsp} |Master password for encryption/decryption of cookies. This must be configured to the same value on each microservice
77+
using the cookie.
6378
|`cookie-http-only` |boolean |`true` |When using cookie, if set to true, the HttpOnly attribute will be configured.
6479
Defaults to `OidcCookieHandler.Builder#DEFAULT_HTTP_ONLY`.
6580
|`cookie-max-age-seconds` |long |{nbsp} |When using cookie, used to set MaxAge attribute of the cookie, defining how long
6681
the cookie is valid.
6782
Not used by default.
6883
|`cookie-name` |string |`JSESSIONID` |Name of the cookie to use.
6984
Defaults to `DEFAULT_COOKIE_NAME`.
85+
|`cookie-name-id-token` |string |`JSESSIONID_2` |Name of the cookie to use for id token.
86+
Defaults to `DEFAULT_COOKIE_NAME`_2.
87+
88+
This cookie is only used when logout is enabled, as otherwise it is not needed.
89+
Content of this cookie is encrypted.
90+
|`cookie-name-refresh-token` |string |`JSESSIONID_3` |The name of the cookie to use for the refresh token.
91+
Defaults to `DEFAULT_REFRESH_COOKIE_NAME`.
7092
|`cookie-name-tenant` |string |`HELIDON_TENANT` |The name of the cookie to use for the tenant name.
7193
Defaults to `DEFAULT_TENANT_COOKIE_NAME`.
7294
|`cookie-path` |string |`/` |Path the cookie is valid for.
@@ -86,6 +108,9 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid
86108
process header containing a JWT.
87109
Default is "Authorization" header with a prefix "bearer ".
88110
|`header-use` |boolean |`true` |Whether to expect JWT in a header field.
111+
|`id-token-signature-validation` |boolean |`true` |Whether id token signature check should be enabled.
112+
Signature check is enabled by default, and it is highly recommended to not change that.
113+
Change this setting only when you really know what you are doing, otherwise it could case security issues.
89114
|`identity-uri` |URI |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata.
90115
|`introspect-endpoint-uri` |URI |{nbsp} |Endpoint to use to validate JWT.
91116
Either use this or set #signJwk(JwkKeys) or #signJwk(Resource).
@@ -107,7 +132,8 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid
107132
Defaults to `DEFAULT_PROXY_PORT`
108133
|`proxy-protocol` |string |`http` |Proxy protocol to use when proxy is used.
109134
Defaults to `DEFAULT_PROXY_PROTOCOL`.
110-
|`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT token when parameter is used.
135+
|`query-id-token-param-name` |string |`id_token` |Name of a query parameter that contains the JWT id token when parameter is used.
136+
|`query-param-name` |string |`accessToken` |Name of a query parameter that contains the JWT access token when parameter is used.
111137
|`query-param-tenant-name` |string |`h_tenant` |Name of a query parameter that contains the tenant name when the parameter is used.
112138
Defaults to #DEFAULT_TENANT_PARAM_NAME.
113139
|`query-param-use` |boolean |`false` |Whether to use a query parameter to send JWT token from application to this
@@ -151,6 +177,9 @@ Type: link:{javadoc-base-url}/io.helidon.security.providers.oidc.common/io/helid
151177
code.
152178
If not defined, it is obtained from #oidcMetadata(Resource), if that is not defined
153179
an attempt is made to use #identityUri(URI)/oauth2/v1/token.
180+
|`token-signature-validation` |boolean |`true` |Whether access token signature check should be enabled.
181+
Signature check is enabled by default, and it is highly recommended to not change that.
182+
Change this setting only when you really know what you are doing, otherwise it could case security issues.
154183
|`validate-jwt-with-jwk` |boolean |`true` |Use JWK (a set of keys to validate signatures of JWT) to validate tokens.
155184
Use this method when you want to use default values for JWK or introspection endpoint URI.
156185

microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityFilter.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@
4343
import io.helidon.security.integration.common.SecurityTracing;
4444
import io.helidon.security.internal.SecurityAuditEvent;
4545
import io.helidon.security.providers.common.spi.AnnotationAnalyzer;
46+
import io.helidon.webserver.security.SecurityHttpFeature;
4647

4748
import jakarta.annotation.PostConstruct;
4849
import jakarta.annotation.Priority;
@@ -56,6 +57,7 @@
5657
import jakarta.ws.rs.container.ContainerResponseFilter;
5758
import jakarta.ws.rs.core.Application;
5859
import jakarta.ws.rs.core.Context;
60+
import jakarta.ws.rs.core.MultivaluedMap;
5961
import jakarta.ws.rs.core.Response;
6062
import org.glassfish.jersey.server.ExtendedUriInfo;
6163
import org.glassfish.jersey.server.model.AbstractResourceModelVisitor;
@@ -182,6 +184,16 @@ public void filter(ContainerRequestContext requestContext, ContainerResponseCont
182184
} else {
183185
return;
184186
}
187+
io.helidon.common.context.Context helidonContext = Contexts.context()
188+
.orElseThrow(() -> new IllegalStateException("Context must be available in Jersey"));
189+
helidonContext.get(SecurityHttpFeature.CONTEXT_RESPONSE_HEADERS, Map.class)
190+
.map(it -> (Map<String, List<String>>) it)
191+
.ifPresent(it -> {
192+
MultivaluedMap<String, Object> headers = responseContext.getHeaders();
193+
for (Map.Entry<String, List<String>> entry : it.entrySet()) {
194+
entry.getValue().forEach(value -> headers.add(entry.getKey(), value));
195+
}
196+
});
185197

186198
SecurityFilterContext fc = (SecurityFilterContext) requestContext.getProperty(PROP_FILTER_CONTEXT);
187199
SecurityDefinition methodSecurity = jerseySecurityContext.methodSecurity();

microprofile/security/src/main/java/io/helidon/microprofile/security/SecurityFilterCommon.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2023 Oracle and/or its affiliates.
2+
* Copyright (c) 2018, 2024 Oracle and/or its affiliates.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@
2424

2525
import io.helidon.common.HelidonServiceLoader;
2626
import io.helidon.common.config.Config;
27+
import io.helidon.common.context.Contexts;
2728
import io.helidon.common.uri.UriQuery;
2829
import io.helidon.microprofile.security.spi.SecurityResponseMapper;
2930
import io.helidon.security.AuthenticationResponse;
@@ -37,6 +38,7 @@
3738
import io.helidon.security.integration.common.AtnTracing;
3839
import io.helidon.security.integration.common.AtzTracing;
3940
import io.helidon.security.integration.common.SecurityTracing;
41+
import io.helidon.webserver.security.SecurityHttpFeature;
4042

4143
import jakarta.ws.rs.WebApplicationException;
4244
import jakarta.ws.rs.container.ContainerRequestContext;
@@ -198,6 +200,9 @@ protected void processAuthentication(SecurityFilterContext context,
198200
switch (responseStatus) {
199201
case SUCCESS -> {
200202
//everything is fine, we can continue with processing
203+
io.helidon.common.context.Context helidonContext = Contexts.context()
204+
.orElseThrow(() -> new IllegalStateException("Context must be available in Jersey"));
205+
helidonContext.register(SecurityHttpFeature.CONTEXT_RESPONSE_HEADERS, response.responseHeaders());
201206
}
202207
case FAILURE_FINISH -> {
203208
if (methodSecurity.authenticationOptional()) {

0 commit comments

Comments
 (0)