Security Providers
Implemented Security Providers
Helidon provides the following security providers for endpoint protection:
| Provider | Type | Outbound supported | Description |
|---|---|---|---|
| OIDC Provider | Authentication | ✅ | Open ID Connect supporting JWT, Scopes, Groups and OIDC code flow |
| HTTP Basic Authentication | Authentication | ✅ | HTTP Basic authentication for local testing and demos |
| Header Assertion | Authentication | ✅ | Asserting a user based on a header value |
| HTTP Signatures | Authentication | ✅ | Protecting service to service communication through signatures |
| ABAC Authorization | Authorization | 🚫 | Attribute based access control authorization policies |
The following providers are no longer evolved:
| Provider | Type | Outbound supported | Description |
|---|---|---|---|
| JWT Provider | Authentication | ✅ | JWT tokens passed from frontend |
OIDC Provider
Open ID Connect security provider.
Maven Coordinates
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-oidc</artifactId>
</dependency>
Usage
In Helidon, we need to register the redirection support with routing (in
addition to SecurityFeature that integrates with WebServer). This is not
required when redirect is set to false.
Adding support for OIDC redirects
WebServer.builder()
.addFeature(SecurityFeature.builder()
.config(config.get("security"))
.build())
.routing(r -> r.addFeature(OidcFeature.create(config)))
.build();
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
access- | Boolean | true | Whether to check if current IP address matches the one access token was issued for |
client- | Client | Set the configuration related to the client credentials flow | |
cookie- | Boolean | true if server- | Whether to GZIP-compress the access token cookie when this reduces its size, unless password-based legacy cookie encryption is enabled |
cookie- | Boolean | true if server- | Whether to GZIP-compress the ID token cookie when this reduces its size, unless password-based legacy cookie encryption is enabled |
cookie- | String | Domain the cookie is valid for | |
cookie- | Boolean | true | Whether to encrypt token cookie created by this microservice |
cookie- | Boolean | true | Whether to encrypt id token cookie created by this microservice |
cookie- | String | Name of the encryption configuration available through Security# and Security# | |
cookie- | List< | Master password for encryption/decryption of cookies | |
cookie- | Boolean | true | Whether to encrypt refresh token cookie created by this microservice |
cookie- | Boolean | true | Whether to encrypt state cookie created by this microservice |
cookie- | Boolean | true | Whether to encrypt tenant name cookie created by this microservice |
cookie- | Boolean | true | When using cookie, if set to true, the HttpOnly attribute will be configured |
cookie- | Long | When using cookie, used to set MaxAge attribute of the cookie, defining how long the cookie is valid | |
cookie- | String | JSESSIONID | Name of the cookie to use |
cookie- | String | JSESSIONID_ | Name of the cookie to use for id token |
cookie- | String | JSESSIONID_ | The name of the cookie to use for the refresh token |
cookie- | String | JSESSIONID_ | The name of the cookie to use for the state storage |
cookie- | String | HELIDON_ | The name of the cookie to use for the tenant name |
cookie- | String | / | Path the cookie is valid for |
cookie- | Same | LAX | When using cookie, used to set the SameSite cookie value |
cookie- | Boolean | false | When using cookie, if set to true, the Secure attribute will be configured |
cookie- | Boolean | true | Whether to use cookie to store JWT between requests |
fallback- | Boolean | false | Whether unknown tenant ids should use default tenant configuration |
force- | Boolean | false | Force HTTPS for redirects to identity provider |
frontend- | String | Full URI of this application that is visible from user browser | |
header- | Token | A Token to process header containing a JWT | |
header- | Boolean | true | Whether to expect JWT in a header field |
id- | Boolean | true | Whether id token signature check should be enabled |
jwk- | Configuration for jwk-loader | ||
jwt- | String | groups | Path to the JWT payload claim containing the groups to add as role grants |
jwt- | String | Separator used to split a string claim value into multiple groups | |
legacy- | Boolean | false | Whether password-based encrypted OIDC cookies should be written without a version byte and with the legacy PBKDF2 iteration count |
legacy- | Boolean | false | Whether password-based encrypted OIDC cookies should retry decryption with the alternate cookie format after primary decryption fails |
max- | Integer | 5 | Configure maximal number of redirects when redirecting to an OIDC provider within a single authentication attempt |
optional | Boolean | false | Whether authentication is required |
outbound | List< | Add a new target configuration | |
outbound- | Oidc | USER_ | Type of the OIDC outbound |
pkce- | Pkce | S256 | Proof Key Code Exchange (PKCE) challenge creation method |
pkce- | Boolean | false | Whether this provider should support PKCE |
propagate | Boolean | false | Whether to propagate identity |
query- | String | id_ | Name of a query parameter that contains the JWT id token when parameter is used |
query- | String | access | Name of a query parameter that contains the JWT access token when parameter is used |
query- | String | h_ | Name of a query parameter that contains the tenant name when the parameter is used |
query- | Boolean | false | Whether to use a query parameter to send JWT token from application to this server |
redirect | Boolean | false | By default, the client should redirect to the identity server for the user to log in |
redirect- | Redirect | PARAM | Configure the strategy used to count redirects to an identity server |
redirect- | String | h_ | Configure the redirect attempt query parameter and cookie name prefix |
redirect- | String | /oidc/ | URI to register web server component on, used by the OIDC server to redirect authorization requests to after a user logs in or approves scopes |
tenants | Tenant | Configurations of the tenants | |
token- | Boolean | true | Whether access token signature check should be enabled |
use- | Boolean | true | Claim groups from JWT will be used to automatically add groups to current subject (may be used with jakarta. annotation) |
webclient | Web | WebClient configuration used for outbound requests to the identity server. This configuration sets the values to the OIDC WebClient default configuration |
Configuration Example
security:
providers:
- oidc:
client-id: "client-id-of-this-service"
client-secret: "${CLEAR=changeit}"
identity-uri: "https://your-tenant.identity-server.com"
frontend-uri: "http://my-service:8080"
audience: "http://my-service"
outbound:
- name: "internal-services"
hosts: ["*.example.org"]
outbound-token:
header: "X-Internal-Auth"
How does it work?
Building OIDC configuration validates required client and identity values.
An enabled OidcFeature also validates the configured signing-key route and
fixed metadata or JWK values at startup. When using OidcProvider directly,
these authentication-specific checks run when a tenant is first used for inbound
authentication. Outbound-only token propagation and client-credentials exchange
do not require inbound signing keys. Inline and classpath resources are fixed
and are loaded when building the configuration.
Metadata and JWK sources that may become available later are loaded on the first
authentication request that needs them. These include filesystem paths, URIs,
OIDC discovery, and a remote JWK URI obtained from fixed metadata. Concurrent
requests share the initial load rather than starting duplicate loads. Each
attempt runs inside jwk-loader.timeout, attempts are grouped by
jwk-loader.retry, and the complete retry batch runs inside
jwk-loader.circuit-breaker.
If metadata or JWK loading remains temporarily unavailable, optional provider
authentication abstains. Required provider authentication returns 401 Unauthorized with a WWW-Authenticate: Bearer challenge when header
authentication is enabled, and 503 Service Unavailable otherwise. OIDC
redirect callback and logout requests return 503 Service Unavailable for the
same temporary source outage.
By default, an attempt times out after 5 seconds. At most two attempts are made,
separated by a 200 ms delay, within an 11-second overall retry timeout. The
11-second budget accommodates both 5-second attempts and the delay. The
circuit opens after the first exhausted batch, rejects requests for 5 seconds,
and then permits a recovery probe. A successfully loaded value is cached for
the life of that tenant configuration. All three policies use the standard
Helidon Fault Tolerance configuration options under jwk-loader. Configuration
is rejected at startup if the attempt timeout is not positive, exceeds the
overall retry timeout, or does not use current-thread execution. Running the
loader on the calling thread prevents a retry from overlapping a timed-out
attempt that is still unwinding after interruption. The timeout interrupts the
loader at its deadline; prompt termination also depends on the underlying I/O
honoring interruption or enforcing its own timeout.
At runtime, depending on configuration...
If a request comes without a token or with insufficient scopes:
- If
redirectis set totrue(default), request is redirected to the authorization endpoint of the identity server. If set to false,401is returned - User authenticates against the identity server
- The identity server redirects back to Helidon service with a code
- Helidon service contacts the identity server’s token endpoint, to exchange the code for a JWT
- The JWT is stored in a cookie (if cookie support is enabled, which it is by default)
- Helidon service redirects to original endpoint (on itself)
Redirect attempts are counted to prevent infinite login redirects. By default,
Helidon stores the count in the redirect-attempt-param query parameter. Set
redirect-attempt-counter-strategy to COOKIE to store the counter in a small
cookie instead. Set it to NONE to disable redirect attempt counting and
max-redirects loop protection. The redirect-attempt-param value is used as
the cookie name prefix when the COOKIE strategy is used; the full cookie name
also includes a tenant and original URI hash.
Cookie Encryption Secret
Some OIDC cookies are encrypted by default. For production deployments,
configure cookie-encryption-password or cookie-encryption-name explicitly.
The same secret or named encryption configuration must be available to every
service instance that shares encrypted OIDC cookies.
If encrypted cookies are enabled and neither cookie-encryption-password nor
cookie-encryption-name is configured, Helidon uses .helidon-oidc-secret in
the current working directory as a local fallback secret file. When Helidon
generates that fallback file, it logs a warning. This fallback is intended for a
single service instance.
On POSIX file systems, Helidon creates the file with owner read/write permissions only and accepts an existing fallback file only when it is a regular file with owner-only read or read/write permissions. On non-POSIX file systems, Helidon still rejects symlinks and non-regular existing files, and creates the fallback file with an exclusive best-effort create operation.
For rolling upgrades from nodes that used the legacy password-based cookie
encryption defaults, set legacy-cookie-encryption to true while both old and
new nodes are running. This makes upgraded nodes keep writing cookies that older
nodes can decrypt.
After all nodes run the new version, set legacy-cookie-encryption to false
and set legacy-cookie-fallback to true for at least one cookie lifetime or
session grace period. After legacy cookies have expired, set both flags to
false. These flags are temporary compatibility controls for upgrades, not
steady-state security settings.
These flags only affect password-based OIDC cookie encryption; named Security
encryption configured with cookie-encryption-name uses its own encryption
configuration.
For a top-level server-type=idcs, the access-token and ID-token cookies are
GZIP-compressed by default when compression reduces their size. Compression is
disabled by default for other server types and can be enabled explicitly with
cookie-compression-enabled for the access token and
cookie-compression-id-enabled for the ID token. Compression is applied before
encryption when encryption is enabled, and uses a cookie-safe encoded form
otherwise. Password-based legacy cookie encryption disables compression while
legacy-cookie-encryption is true, even if the compression options are
enabled; named Security encryption does not have this override. During a rolling
upgrade from a version that does not understand compressed token cookies, set
both options to false on all nodes before upgrading. New nodes still read
compressed cookies while these settings are disabled, but write the older
uncompressed format. Re-enable compression after all nodes have been upgraded
and the rollback window has closed. Older nodes cannot read compressed cookies.
Rolling back after compression has been re-enabled therefore requires
invalidating affected sessions or waiting for the compressed cookies to expire
and users to authenticate again.
Helidon obtains a token from request (from cookie, header, or query parameter):
- Token is parsed as a singed JWT
- We validate the JWT signature either against local JWK or against the identity server’s introspection endpoint depending on configuration
- We validate the issuer and audience of the token if it matches the configured values
- A subject is created from the JWT, including scopes from the token
- We validate that we have sufficient scopes to proceed, and return
403if not - Handling is returned to security to process other security providers
Multi Tenancy
The OIDC provider also supports multi tenancy. To enable this feature, it is required to do several steps.
- To enable the default multi-tenant support, add the
multi-tenant: trueoption to the OIDC provider configuration - Specify the desired way to provide the tenant name. This step is done over
adding the
tenant-id-styleconfiguration option. For more information, see the table below - Add the tenants section to the OIDC provider configuration
tenants:
- name: "example-tenant"
# ... tenant configuration options
There are four ways to provide the required tenant information to Helidon by default.
Possible tenant-id-style configuration options:
| key | description | additional config options |
|---|---|---|
host-header | Tenant configuration will be selected based on your host present in the Host header value. | |
domain | Similar to the host-header style, but now the tenant name is identified just as a part of the host name. By default, it selects the third domain level.Example: Host header value from inbound request is | tenant-id-domain-level: <domain level> |
token-handler | The tenant name information is expected to be provided through the configured custom header value. | tenant-id-handler:
header: "my-custom-header" |
none | No tenant name finding is used. Default tenant name @default is used instead. |
You can also implement a custom way of discovering the tenant name and tenant configuration. The custom tenant name discovery from request can be done by implementing SPI:
io.helidon.security.providers.oidc.common.spi.TenantIdProvider
and the custom tenant configuration discovery can be provided by implementing SPI:
io.helidon.security.providers.oidc.common.spi.TenantConfigProvider
Available tenant config options
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
audience | String | Audience of issued tokens | |
authorization- | URI | URI of an authorization endpoint used to redirect users to for logging-in | |
base- | String | openid | Configure base scopes |
check- | Boolean | true | Configure audience claim check |
client- | String | Client ID as generated by OIDC server | |
client- | String | Client secret as generated by OIDC server | |
client- | Duration | 30000 | Timeout of calls using web client |
decryption- | Configuration for decryption-keys | ||
identity- | URI | URI of the identity server, base used to retrieve OIDC metadata | |
introspect- | URI | Endpoint to use to validate JWT | |
issuer | String | Issuer of issued tokens | |
jwk- | Configuration for jwk-loader | ||
name | String | Name of the tenant | |
oidc- | Configuration for oidc-metadata | ||
oidc- | Boolean | true | If set to true, metadata will be loaded from default (well known) location, unless it is explicitly defined using oidc-metadata-resource |
optional- | Boolean | false | Allow audience claim to be optional |
scope- | String | Audience of the scope required by this application | |
server- | String | @default | Configure one of the supported types of identity servers |
sign- | Configuration for sign-jwk | ||
token- | Client | CLIENT_ | Type of authentication to use when invoking the token endpoint. With CLIENT_SECRET_BASIC, credentials are sent only to POST requests on the resolved token endpoint scheme, host, and path and, when JWT introspection is used, to POST requests on the resolved introspection endpoint scheme, host, and path |
token- | URI | URI of a token endpoint used to obtain a JWT based on the authentication code | |
validate- | Boolean | true | Use JWK (a set of keys to validate signatures of JWT) to validate tokens |
How does that work?
Multi-tenant support requires to obtain tenant name from the incoming request.
OIDC configuration is selected based on the received tenant name. The way this
tenant name has to be provided is configured via tenant-id-style
configuration. See How to enable tenants for more information.
After matching tenant configuration with the received name, the rest of the OIDC
flow if exactly the same as in How does OIDC work.
Base OIDC configuration is treated as a default tenant, which is used if no
tenant name is provided. This default tenant has the name @default. An
identified tenant name must match a configured tenant; unknown tenant names are
rejected by default. Set fallback-to-default-tenant-enabled: true only when
unknown tenant names should use the default tenant configuration.
It is also important to note, that each tenant configuration is based on the default tenant configuration (base OIDC configuration), and therefore its configuration do not need to change all the properties, if they do not differ from the base OIDC configuration.
CORS Settings
CORS is (now) a single component configured either through config (key cors),
or programmatically via io.helidon.webserver.cors.CorsFeature. To add proper
CORS setup for the OIDC endpoint, use one of these. Component specific CORS
setup will be removed from Helidon.
HTTP Basic Authentication Provider
HTTP Basic authentication support for local testing and demos.
Maven Coordinates
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-http-auth</artifactId>
</dependency>
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
outbound | List< | Add a new outbound target to configure identity propagation or explicit username/password | |
optional | Boolean | false | Whether authentication is required |
realm | String | helidon | Set the realm to use when challenging users |
principal- | Subject | USER | Principal type this provider extracts (and also propagates) |
users | List< | Set user store to validate users |
Configuration Example
security:
providers:
- http-basic-auth:
realm: "helidon"
users:
- login: "john"
password: "${CLEAR=changeit}"
roles: ["admin"]
- login: "jack"
password: "changeit"
roles: ["user", "admin"]
outbound:
- name: "internal-services"
hosts: ["*.example.org"]
# Propagates current user's identity or identity from request property
outbound-token:
header: "X-Internal-Auth"
- name: "partner-service"
hosts: ["*.partner.org"]
# Uses this username and password
username: "partner-user-1"
password: "${CLEAR=changeit}"
For all security providers, outbound target methods loaded from configuration
temporarily match the exact configured token and its standard uppercase form
when the token names a known method in lowercase or mixed case. For example,
get matches get and GET, but does not match Get. Uppercase known methods
and custom methods remain exact and case-sensitive. Programmatic builder method
selectors remain exact.
Loading a non-uppercase known method logs a warning. Update ordinary method names to uppercase now: this compatibility will be removed in a future major version, when configuration will match only the exact token. See the upgrade guide for the known methods and the scope of this compatibility.
Example
See the example on GitHub.
How does it work?
See https://tools.ietf.org/html/rfc7617.
Authentication of request
When a request is received without the Authorization: basic ... header, a
challenge is returned to provide such authentication.
When a request is received with the Authorization: basic ... header, the
username and password is validated against configured users (and users obtained
from custom service if any provided).
Subject is created based on the username and roles provided by the user store.
Identity propagation
When identity propagation is configured, there are several options for identifying username and password to propagate:
- We propagate the current username and password (inbound request must be authenticated using basic authentication).
- We use username and password from an explicitly configured property (See
EndpointConfig.PROPERTY_OUTBOUND_IDandEndpointConfig.PROPERTY_OUTBOUND_SECRET) - We use username and password associated with an outbound target (see example configuration above)
Identity is propagated only if:
- There is an outbound target configured for the endpoint
- Or there is an explicitly configured username/password for the current request (through request property)
Custom user store
Java service loader service
io.helidon.security.providers.httpauth.spi.UserStoreService can be implemented
to provide users to the provider, such as when validated against an internal
database or LDAP server. The user store is defined so you never need the clear
text password of the user.
Warning on security of HTTP Basic Authentication (or lack thereof)
Basic authentication uses base64 encoded username and password and passes it over the network. Base64 is only encoding, not encryption - so anybody that gets hold of the header value can learn the actual username and password of the user. This is a security risk and an attack vector that everybody should be aware of before using HTTP Basic Authentication. HTTP Basic authentication is not supported for production use. We recommend it only for local testing and demo purposes.
Header Authentication Provider
Asserts user or service identity based on a value of a header.
Maven Coordinates
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-header</artifactId>
</dependency>
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
atn- | Token | Token handler to extract username from request | |
authenticate | Boolean | true | Whether to authenticate requests |
outbound | List< | Configure outbound target for identity propagation | |
propagate | Boolean | false | Whether to propagate identity |
optional | Boolean | false | Whether authentication is required |
outbound- | Token | Token handler to create outbound headers to propagate identity | |
principal- | Subject | USER | Principal type this provider extracts (and also propagates) |
Configuration Example
security:
providers:
header-atn:
atn-token:
header: "X-AUTH-USER"
outbound:
- name: "internal-services"
hosts: ["*.example.org"]
# propagates the current user or service id using the same header as authentication
- name: "partner-service"
hosts: ["*.partner.org"]
# propagates an explicit username in a custom header
username: "service-27"
outbound-token:
header: "X-Service-Auth"
How does it work?
This provider inspects a specified request header and extracts the username/service name from it and asserts it as current subject’s principal.
This can be used when we use perimeter authentication (e.g. there is a gateway that takes care of authentication and propagates the user in a header).
Identity propagation
Identity is propagated only if an outbound target matches the target service.
The following options exist when propagating identity: 1. We propagate the current username using the configured header 2. We use username associated with an outbound target (see example configuration above)
Caution
When using this provider, you must be sure the header cannot be explicitly configured by a user or another service. All requests should go through a gateway that removes this header from inbound traffic, and only configures it for authenticated users/services. Another option is to use this with fully trusted parties (such as services within a single company, on a single protected network not accessible to any users), and of course for testing and demo purposes.
HTTP Signatures Provider
Support for HTTP Signatures.
Inbound and outbound sign-headers method selectors loaded from configuration
temporarily match the exact configured token and, for a non-uppercase known
method, its standard uppercase form. For example, PoSt matches PoSt and
POST, but does not match post. An explicit uppercase entry takes precedence
over a compatibility entry regardless of configuration order. If several case
variants imply the same uppercase entry and no explicit uppercase entry exists,
the last configured variant supplies that uppercase entry's signed headers.
Loading a non-uppercase known method logs a warning. Update ordinary method
names to uppercase now; compatibility will be removed in a future major
version, when selectors will match only the exact configured token.
Programmatic builder method selectors remain exact. Outbound target methods
use the same configuration compatibility described in the
upgrade guide.
Maven Coordinates
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-http-sign</artifactId>
</dependency>
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
headers | List< | Add a header that is validated on inbound requests | |
inbound- | Duration | PT5M | Configure the maximum accepted age or future skew for the signed Date header |
outbound | Outbound | Add outbound targets to this builder | |
inbound. | List< | Add inbound configuration | |
backward- | Boolean | false | Enable support for Helidon versions before 3.0.0 (exclusive) |
optional | Boolean | true | Set whether the signature is optional |
realm | String | helidon | Realm to use for challenging inbound requests that do not have "Authorization" header in case header is Http and singatures are not optional |
sign- | List< | Override the default inbound required headers (e.g |
Configuration Example
security:
providers:
- http-signatures:
inbound:
keys:
- key-id: "service1-hmac"
principal-name: "Service1 - HMAC signature"
hmac.secret: "${CLEAR=changeit}"
- key-id: "service1-rsa"
principal-name: "Service1 - RSA signature"
public-key:
keystore:
resource.path: "src/main/resources/keystore.p12"
passphrase: "changeit"
cert.alias: "service_cert"
outbound:
- name: "service2-hmac"
hosts: ["localhost"]
paths: ["/service2"]
signature:
key-id: "service1-hmac"
hmac.secret: "${CLEAR=changeit}"
- name: "service2-rsa"
hosts: ["localhost"]
paths: ["/service2-rsa.*"]
signature:
key-id: "service1-rsa"
private-key:
keystore:
resource.path: "src/main/resources/keystore.p12"
passphrase: "changeit"
key.alias: "myPrivateKey"
Example
See the example on GitHub.
Signature basics
- standard: based on https://tools.ietf.org/html/draft-cavage-http-signatures-03
- key-id: an arbitrary string used to locate signature configuration - when a request is received the provider locates validation configuration based on this id (e.g. HMAC shared secret or RSA public key). Commonly used meanings are: key fingerprint (RSA); API Key
How does it work?
Inbound Signatures We act as a server and another party is calling us with a signed HTTP request. We validate the signature and assume identity of the caller.
By default, inbound validation requires signed date, (request-target), and
host fields. The authorization field must also be signed when it is present,
unless the signature itself is carried in the Authorization header. The signed
Date value must be within PT5M of the server time; configure
inbound-date-validity to another duration, or to PT0S to disable date
freshness validation. Date freshness rejects stale or far-future signatures; it
does not provide nonce-based replay detection within the accepted time window.
The (request-target) field uses the lower-case HTTP method followed by a
space, the request path, and the raw query string from the security environment,
when present. Query parameter order and encoding are significant.
The provider implements
draft-cavage-http-signatures-03,
which requires that lowercase representation. Consequently,
(request-target) does not bind the original method case: methods such as
GET, get, and GeT produce the same signed method text. Do not rely on this
legacy signed component when a trust boundary must distinguish method case.
Use sign-headers to require additional signed fields such as digest,
content-length, or content-type for selected methods. Configured method
selectors use the temporary compatibility described above; this does not
change (request-target) canonicalization.
If a request carries the signature in the Authorization header, that header
value cannot be combined with any other authorization scheme. Use the standalone
Signature header when another Authorization value must be sent with the same
request.
Outbound Signatures We act as a client and we sign our outgoing requests. If
there is a matching outbound target specified in configuration, its
configuration will be applied for signing the outgoing request, otherwise there
is no signature added
By default, outbound signing includes date, (request-target), and host. It
also signs authorization when that field is present, unless the signature
itself is carried in the Authorization header. The provider adds date and
host when they are required but missing.
ABAC Provider
Attribute based access control authorization provider.
Maven Coordinates
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-abac</artifactId>
</dependency>
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
fail- | Boolean | true | Whether to fail if NONE of the attributes is validated |
fail- | Boolean | true | Whether to fail if any attribute is left unvalidated |
Configuration Example
security:
providers:
- abac:
How does it work?
ABAC uses available validators and validates them against attributes of the authenticated user.
Combinations of fail-on-unvalidated and fail-if-none-validated:
true&true: Will fail if any attribute is not validated and if any has failed validationfalse&true: Will fail if there is one or more attributes present and NONE of them is validated or if any has failed validation, Will NOT fail if there is at least one validated attribute and any number of not validated attributes (and NONE failed)false&false: Will fail if there is any attribute that failed validation, Will NOT fail if there are no failed validation or if there are NONE validated
Any attribute of the following objects can be used:
- environment (such as time of request) - e.g. env.time.year
- subject (user) - e.g. subject.principal.id
- subject (service) - e.g. service.principal.id
- object (must be explicitly invoked by developer in code, as object cannot be automatically added to security context) - e.g. object.owner
This provider checks that all defined ABAC validators are validated. If there is a definition for a validator that is not checked, the request is denied (depending on configuration as mentioned above).
ABAC provider also allows an object to be used in authorization process, such as when evaluating if an object’s owner is the current user. The following example uses the Expression language validator to demonstrate the point in an endpoint:
Example of using an object
@Authenticated
@Http.Path("/abac")
public class AbacEndpoint {
@Http.GET
@Authorized(explicit = true)
@PolicyStatement("${env.time.year >= 2017 && object.owner == subject.principal.id}")
public String process(SecurityContext context) {
// probably looked up from a database
SomeResource res = new SomeResource("user");
AuthorizationResponse atzResponse = context.authorize(res);
if (atzResponse.isPermitted()) {
return "fine, sir";
}
return atzResponse.description().orElse("Access not granted");
}
}
The following validators are implemented:
Role Validator
Checks whether user/service is in either of the required role(s).
Configuration Key: role-validator
Annotations: @RolesAllowed, @RoleValidator.Roles
Configuration example for WebServer
security:
web-server.paths:
- path: "/user/*"
roles-allowed: ["user"]
Annotation example
@RolesAllowed("user")
@RoleValidator.Roles(value = "service_role", subjectType = SubjectType.SERVICE)
@Authenticated
@Http.Path("/abac")
public class AbacEndpoint {
}
Scope Validator
Checks whether user has all the required scopes.
Configuration Key: scope-validator
Annotations: @Scope
Configuration example for WebServer
security:
web-server.paths:
- path: "/user/*"
abac.scopes:
["calendar_read", "calendar_edit"]
Annotation example
@Scope("calendar_read")
@Scope("calendar_edit")
@Authenticated
@Http.Path("/abac")
public class AbacEndpoint {
}
Expression Language Policy Validator
Policy executor using Java EE policy expression language (EL)
Configuration Key: policy-javax-el
Annotations: @PolicyStatement
Example of a policy statement: ${env.time.year >= 2017}
Configuration example for WebServer
security:
web-server.paths:
- path: "/user/*"
policy:
statement: "hasScopes('calendar_read','calendar_edit') AND timeOfDayBetween('8:15', '17:30')"
Annotation example
@PolicyStatement("${env.time.year >= 2017}")
@Authenticated
@Http.Path("/abac")
public class AbacEndpoint {
}
Configuration example for endpoint security over configuration
server:
features:
security:
endpoints:
- path: "/somePath"
config:
abac.policy-validator.statement: "\\${env.time.year >= 2017}"
JWT Provider
JWT token authentication and outbound security provider.
Maven Coordinates
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-jwt</artifactId>
</dependency>
Configuration options
| Key | Type | Default | Description |
|---|---|---|---|
allow- | Boolean | false | Whether to allow impersonation by explicitly overriding username from outbound requests using io. property |
allow- | Boolean | false | Configure support for unsigned JWTs without requiring verification JWKs |
atn- | Configuration for atn-token | ||
authenticate | Boolean | true | Whether to authenticate requests |
jwk- | Configuration for jwk-loader | ||
jwt- | String | groups | Path to the JWT payload claim containing the groups to add as role grants |
jwt- | String | Separator used to split a string claim value into multiple groups | |
optional | Boolean | false | Whether authentication is required |
principal- | Subject | USER | Principal type this provider extracts (and also propagates) |
propagate | Boolean | true | Whether to propagate identity |
sign- | Outbound | Configuration of outbound rules | |
use- | Boolean | true | Claim groups from JWT will be used to automatically add groups to current subject (may be used with jakarta. annotation) |
Configuration Example
security:
providers:
- provider:
atn-token:
jwk.resource.resource-path: "verifying-jwk.json"
jwt-issuer: "http://trusted.issuer"
jwt-audience: "http://my.service"
sign-token:
jwk.resource.resource-path: "signing-jwk.json"
jwt-issuer: "http://my.server/identity"
outbound:
- name: "propagate-token"
hosts: ["*.internal.org"]
- name: "generate-token"
hosts: ["1.partner-service"]
jwk-kid: "partner-1"
jwt-kid: "helidon"
jwt-audience: "http://1.partner-service"
Example
See the example on GitHub.
How does it work?
JSON Web Token (JWT) provider has support for authentication and outbound security.
Authentication is based on validating the token (signature, valid before etc.) and on asserting the subject of the JWT subject claim.
Inline and classpath verification JWK resources are loaded and validated when
the provider starts. Filesystem and URI resources are loaded on the first
authentication request because they may become available later. Concurrent
requests share that initial load. Each attempt has a default 5-second timeout;
at most two attempts are made, separated by 200 ms, within an 11-second overall
retry timeout. After the first exhausted batch, the circuit breaker rejects
loads for 5 seconds before permitting a recovery probe. A successful key set is
cached for the life of the provider. Configure the standard Timeout, Retry, and
Circuit Breaker options under jwk-loader.timeout, jwk-loader.retry, and
jwk-loader.circuit-breaker, respectively. The 11-second budget accommodates
both 5-second attempts and the retry delay. Configuration is rejected at
startup if the attempt timeout is not positive, exceeds the overall retry
timeout, or does not use current-thread execution. This prevents a retry from
overlapping a timed-out loader that is still unwinding after interruption. The
deadline interrupts the loader, so prompt termination also depends on the
underlying I/O honoring interruption or enforcing its own timeout.
If verification-key loading remains temporarily unavailable, optional
authentication abstains. Required authentication using the built-in
Authorization Bearer token handler returns 401 Unauthorized with a
WWW-Authenticate: Bearer challenge. A provider using a custom token handler
returns 503 Service Unavailable instead.
If allow-unsigned is explicitly enabled, a token using the none algorithm
without a key ID does not require or trigger loading of verification JWKs.
Signed tokens still require matching verification keys. Enabling unsigned
tokens is dangerous and should be limited to environments where untrusted
parties cannot supply JWTs.
For outbound, we support either token propagation (e.g. the token from request is propagated further) or support for generating a brand-new token based on configuration of this provider.