Skip to content

Complete OAuth2 authorization support#1446

Open
mcruzdev wants to merge 8 commits into
serverlessworkflow:mainfrom
mcruzdev:issue-1003
Open

Complete OAuth2 authorization support#1446
mcruzdev wants to merge 8 commits into
serverlessworkflow:mainfrom
mcruzdev:issue-1003

Conversation

@mcruzdev

@mcruzdev mcruzdev commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Many thanks for submitting your Pull Request ❤️!

What this PR does / why we need it:

  • Adds support to private_key_jwt and client_secret_jwt.

Special notes for reviewers:

Additional information (if needed):

Closes #1003

Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Copilot AI review requested due to automatic review settings June 9, 2026 17:36
@mcruzdev mcruzdev requested a review from fjtirado as a code owner June 9, 2026 17:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds broader OAuth2 workflow support and samples, including JWT-based client authentication and token-exchange parameters, and expands request building to carry revocation/introspection endpoint URIs.

Changes:

  • Add workflow sample YAMLs for client_secret_jwt, private_key_jwt, and token exchange grant.
  • Implement JWT client assertion handling and token-exchange subject/actor parameters in request building.
  • Extend OAuth request URI resolution to include revocation/introspection URIs; add JUnit coverage for new samples.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
impl/test/src/test/resources/workflows-samples/oauth2/oAuthPrivateKeyJwtClientCredentialsHttpCall.yaml Adds sample workflow using private_key_jwt client auth for client credentials.
impl/test/src/test/resources/workflows-samples/oauth2/oAuthClientSecretPostTokenExchangeHttpCall.yaml Adds sample workflow for token exchange grant with subject/actor tokens.
impl/test/src/test/resources/workflows-samples/oauth2/oAuthClientSecretJwtClientCredentialsHttpCall.yaml Adds sample workflow using client_secret_jwt client auth for client credentials.
impl/test/src/test/java/io/serverlessworkflow/impl/test/OAuthHTTPWorkflowDefinitionTest.java Adds tests validating form-encoded token request bodies for JWT client auth and token exchange.
impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthRequestBuilder.java Adds revocation/introspection URI resolution alongside token URI.
impl/core/src/main/java/io/serverlessworkflow/impl/auth/JwtClientAssertion.java Introduces handler that maps provided JWT assertions to RFC7523 form params.
impl/core/src/main/java/io/serverlessworkflow/impl/auth/HttpRequestInfoBuilder.java Extends request info to include revocation/introspection URIs.
impl/core/src/main/java/io/serverlessworkflow/impl/auth/HttpRequestInfo.java Adds revocation/introspection URI fields to the record.
impl/core/src/main/java/io/serverlessworkflow/impl/auth/ClientSecretHandler.java Allows token exchange grant through existing client-secret handlers.
impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthUtils.java Adds constants for assertion and token-exchange related param names.
impl/core/src/main/java/io/serverlessworkflow/impl/auth/AbstractAuthRequestBuilder.java Implements JWT assertion auth method selection + subject/actor param forwarding.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +67 to +69
private static String endpointPath(String path, String defaultPath) {
return path != null ? path.replaceAll("^/", "") : defaultPath;
}
Comment thread impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthRequestBuilder.java Outdated
Comment thread impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthRequestBuilder.java Outdated
Comment thread impl/core/src/main/java/io/serverlessworkflow/impl/auth/HttpRequestInfo.java Outdated
Copilot AI review requested due to automatic review settings June 10, 2026 14:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.

Comment on lines +69 to +80
void accept(Map<String, Object> secret) {
Map<String, Object> client = asClient(secret);
if (client == null || client.get(ASSERTION) == null) {
throw new IllegalArgumentException(
"A client assertion must be provided for JWT client authentication");
}
if (PASSWORD.value().equals(secret.get(GRANT))) {
password(secret);
} else {
clientCredentials(secret);
}
}
Comment on lines +67 to +69
private static String endpointPath(String path, String defaultPath) {
return path != null ? path.replaceAll("^/", "") : defaultPath;
}
"actor_token_type=urn:ietf:params:oauth:token-type:access_token"));
}

private String runJwtClientAuthWorkflow(String workflowResource) throws Exception {
Comment on lines +48 to +52
requestBuilder
.withUri(endpointResolver(uri, endpointPath(token, DEFAULT_TOKEN_PATH)))
.withRevocationUri(endpointResolver(uri, endpointPath(revocation, DEFAULT_REVOCATION_PATH)))
.withIntrospectionUri(
endpointResolver(uri, endpointPath(introspection, DEFAULT_INTROSPECTION_PATH)));
mcruzdev added 4 commits June 10, 2026 19:34
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Copilot AI review requested due to automatic review settings June 10, 2026 23:30

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 18 out of 19 changed files in this pull request and generated 4 comments.

Comment on lines +119 to +123
() -> {
Response response = executeRequest(workflowContext, taskContext, model);
ensureSuccessful(response, taskContext, "obtain token");
return response.readEntity(new GenericType<>() {});
});
Comment thread impl/core/src/main/java/io/serverlessworkflow/impl/auth/OAuthRequestBuilder.java Outdated
Comment on lines +42 to +43
throw new UnsupportedOperationException(
"Token introspection is not supported by this provider");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an internal interface, there is not need (I know AI complaints but lets ignore this) to use this kind of antipattern consisting on providing a default implementaiton that is not implementation at all just to keep all implementos compiling (which is not a concern here)

Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Comment thread .gitignore

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this one can be rolleck back (the db files wont appear after you rebase)

Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Copilot AI review requested due to automatic review settings June 12, 2026 15:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 27 out of 27 changed files in this pull request and generated 23 comments.

* <p>{@code active} is the only field guaranteed by the specification; the full response is exposed
* through {@code claims} so callers can inspect additional metadata (scope, exp, sub, ...).
*/
public record TokenIntrospection(boolean active, Map<String, Object> claims) {}
Comment on lines +169 to +189
private Response postManagementRequest(
WorkflowContext workflow,
TaskContext task,
WorkflowModel model,
URI uri,
String token,
String tokenTypeHint) {
Invocation.Builder builder =
commonHeaders(HttpClientResolver.client(workflow, task).target(uri), workflow, task, model);

Form form = new Form();
form.param("token", token);
if (tokenTypeHint != null) {
form.param("token_type_hint", tokenTypeHint);
}
requestInfo
.clientAuthParams()
.forEach((key, value) -> form.param(key, value.apply(workflow, task, model)));

return builder.post(Entity.entity(form, MediaType.APPLICATION_FORM_URLENCODED));
}
Comment on lines +206 to +218
private URI endpoint(
Optional<WorkflowValueResolver<URI>> resolver,
String name,
WorkflowContext workflow,
TaskContext task,
WorkflowModel model) {
return resolver
.map(r -> r.apply(workflow, task, model))
.orElseThrow(
() ->
new UnsupportedOperationException(
"No " + name + " endpoint is configured for this provider"));
}
assertTrue(tokenRequestBody.contains("password=serverless-workflow-test"));
assertTrue(tokenRequestBody.contains("client_id=serverless-workflow"));
assertTrue(tokenRequestBody.contains("client_secret=D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT"));
assertTrue(tokenRequestBody.contains("client_secret=dummy-secret-for-tests"));
Map.of(
"clientId", "serverless-workflow",
"clientSecret", "D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT",
"clientSecret", "dummy-secret-for-tests",
assertTrue(
asJson.containsKey("client_secret")
&& asJson.get("client_secret").equals("D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT"));
&& asJson.get("client_secret").equals("dummy-secret-for-tests"));
Map.of(
"clientId", "serverless-workflow",
"clientSecret", "D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT");
"clientSecret", "dummy-secret-for-tests");
assertTrue(
asJson.containsKey("client_secret")
&& asJson.get("client_secret").equals("D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT"));
&& asJson.get("client_secret").equals("dummy-secret-for-tests"));
Map.of(
"clientId", "serverless-workflow",
"clientSecret", "D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT");
"clientSecret", "dummy-secret-for-tests");
assertTrue(
asJson.containsKey("client_secret")
&& asJson.get("client_secret").equals("D0ACXCUKOUrL5YL7j6RQWplMaSjPB8MT"));
&& asJson.get("client_secret").equals("dummy-secret-for-tests"));
Signed-off-by: Matheus Cruz <matheuscruz.dev@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Complete Oauth 2 authorization support

3 participants