-
Story
-
Resolution: Unresolved
-
Major
-
1.35.0
-
False
-
-
False
-
-
See https://github.com/apache/incubator-kie-issues/issues/1893
The goal of this story is to introduce and implement oauth2 token exchange in order to make request in name of the user (not impersonation) with the same permissions and with keeping the user identity. This will allow a better security as the user will never share its credentials with anything else than the workflow and will, in the same time, allow the oauth2 token lifecycle management (renewal) to keep the access token alive throughout the workflow execution.
SonataFlow should offer a custom OIDC Filter that interacts with the OpenAPI Generator generated REST Clients. For each REST Client that has Token Exchange enabled, this new extension should register the custom filter and turn off the security filters generated by the OpenAPI Generator.
This extension would be similar to the AccessTokenRequestFilter provided by the Quarkus OIDC Client. But, instead of relying on the injected token in the context, it has to extract the token from a custom header in the workflow request.
This header must be named "X-Authentication-<security-scheme-name>" by default. Users can optionally change this header key for any other value via properties. Adding the "X-Authentication" suffix is a good practice for signaling to tools that this header is custom.
Once the token is extracted, the logic should be near the same as the AccessTokenRequestFilter:
- Extract the target token from the current request headers context injected by the REST Client in the request properties.
- Use this token and the workflow application credentials to exchange the token via the OIDC Client.
- Inject the token into the Authentication Request Header when calling the target service.
A new property must be introduced to enable this feature
Instead of directly requesting the OAuth2 server to do the token exchange, the workflow service should first verify the JWT token expiration:
- Avoid unnecessary requests since the OAuth2 server will deny the exchange request.
- Once cache and refresh tokens are implemented, the workflow application can check for token expiration to request a new access token.
Once the access token is exchanged, the OAuth2 server will return the refresh token. The system must save this token in the primary near cache or in the database. Once a new request comes (or a callback task is resumed), if the current exchanged access token is expired, the system should refresh the access token.
By refreshing the access token, the system can replace the near cache or the database with the new access token to request the target service.
Quarkus OIDC Client already supports Refresh Token calls.
Whenever the workflow service requests an exchange token, the system should save the results in the primary near cache to avoid roundtrips to the OAuth2 server.
The cache key must be the original access token extracted from the custom header in the workflow request.
The tokens saved in this cache should be the access and refresh tokens. The cache should expire after the token expiration date.
In a cloud environment, the system must save the token cache in persistent storage shared among the pods from the same workflow service. This storage is crucial mainly on serverless executions, where the cluster will kill the workflow service pods after execution. The system should be able to restore the cache keys once the workflow execution is resumed. As https://issues.redhat.com/browse/SRVLOGIC-548 introduced token persistence in the database, the cache may be populated using the token persistence table by extracting the tokens associated to the instance.
Quarkus has cache implementation support that we can leverage in this requirement. See Application Data Cache.
The system must perform the token exchange of the oauth2 token found in the headers and save the access and request tokens to the primary cache. This action is required because the original access token might expire while the workflow execution is interrupted. The refresh token allows the workflow to acquire a new token once the system resumes its execution. Usually, refresh tokens have a longer expiration time.
The exchange token and the refresh token associated must also be stored in the secondary cache (e.g: database)
The cache key comprises the workflow instance ID and the "Security ID" sanitized name separated by a pipe. For example, "0000-1111-2222-3333|acme_financial_oauth." "Security ID" is the "SecurityScheme" name found in the corresponding OpenAPI spec file:
securitySchemes: acme-financial-oauth: 👈 type: oauth2 flows: clientCredentials: authorizationUrl: http://localhost/auth/realms/acme/protocol/openid-connect/auth tokenUrl: http://localhost/auth/realms/kogito/acme/openid-connect/token scopes: {}
Quarkus has cache implementation support that we can leverage in this requirement.
Sometimes, users find it helpful to access information from a specific JWT token passed to the workflow service.
SonataFlow runtime can have a custom out-of-the-box function capable of parsing and decoding these tokens so that users can access the claims in the DSL expressions. For example:
functions: - name: extractUser type: custom operation: "service:org.acme.workflow.auth.JWTParserHelper::extractUser" states: - name: extractUserName type: operation actions: - name: extractUserAction functionRef: refName: extractUser arguments: token: "${ $WORKFLOW.headers.\"X-Authorization-acme_financial_auth\" }" stateDataFilter: output: "${ { user: .preferred_username } }"
In this excerpt, the DSL refers to a custom Java function within the project that parses the JWT token from the header "X-Authorization-acme_financial_auth."
Later, the workflow author can access the "user" attribute to return a personalized message to the requester:
- name: loanApproved type: inject data: loanApproved: true stateDataFilter: output: "${ { message: \"Congrats \\(.user)! Your loan has been approved!\", loanApproved } }"
As a shortcut, the function can take the custom header as an input and output the JWT Payload in JSON format, which can be merged into the workflow data context.
This function can also be added to the Serverless Workflow Functions catalog
Acceptance criteria
- The documentation of how to use/configure the feature is written
- A new property to enable the feature is introduced
- If the feature is enabled, whenever an oauth2 token is in the header, it must be exchanged with another one
- the refresh and access token received must be cached and persisted in DB (secondary cache)
- the newly create access token must keep the user identity and permissions associated
- Whenever a workflow execution is stopped/paused then resumed, the exchanged token must be restored from the DB to the local cache
- Whener an exchanged token is close to expiration, it must be renewed/refreshed using the associated refresh token
- A new function is made available to access token information
- is related to
-
SRVLOGIC-548 Support token persitence
-
- ON_QA
-
- links to