Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@OpenIdAuthenticationMechanismDefinition annotation is not repeatable in 4.0 #342

Open
fschoning opened this issue Nov 4, 2024 · 5 comments

Comments

@fschoning
Copy link

I am trying to use two different OIDC authentication provider clients within the same Wildfly 34 Preview JakartaEE 11 web application. As far as I understand, in Jakarta EE 3.0 this is not possible but has been made possible in 4.0 by using Qualifiers.

I have created two servlets, each with the @OpenIdAuthenticationMechanismDefinitionannotation and each with their own Qualifier annotation. However on deployment I still get the error: "Ambiguous dependencies for type OpenIdAuthenticationMechanismDefinition with qualifiers @default"

My code is as follows:

@Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) public @interface QualifierA { }

@Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) public @interface QualifierB { }

@QualifierA @OpenIdAuthenticationMechanismDefinition( providerURI = "${oidcConfigA.issuerUri}", clientId = "${oidcConfigA.clientId}", clientSecret = "${oidcConfigA.clientSecret}", redirectURI = "${baseURL}/oidcredirecturi", jwksReadTimeout = 5000, jwksConnectTimeout = 5000) @ServletSecurity(@HttpConstraint(rolesAllowed = "Everyone")) @WebServlet("/alogin") public class OidcLoginWebServletA extends HttpServlet {

@QualifierB @OpenIdAuthenticationMechanismDefinition( providerURI = "${oidcConfigB.issuerUri}", clientId = "${oidcConfigB.clientId}", clientSecret = "${oidcConfigB.clientSecret}", redirectURI = "${baseURL}/oidcredirecturi", jwksReadTimeout = 5000, jwksConnectTimeout = 5000) @ServletSecurity(@HttpConstraint(rolesAllowed = "Everyone")) @WebServlet("/blogin") public class OidcLoginWebServletB extends HttpServlet {

I do not know if I am using the feature wrong or if this is an issue? I cannot see an example of this in the tck tests.

@OndroMih
Copy link
Contributor

OndroMih commented Jan 6, 2025

Hi, @fschoning, you need to assign a qualifier to at least one of your definitions.

So, like this:

@OpenIdAuthenticationMechanismDefinition(  qualifiers = QualifierA.class
   providerURI = "${oidcConfigA.issuerUri}",  ...)

The mechanisms are for the whole application, they are not connected to the servlet where you define them. You have to add a custom handler to select which mechanism is used for which servlet.

Here's an example in the TCK, with the Basic mechanism: CustomAuthenticationMechanismHandler.java#L47

For OpenID it's very similar - the OpenID annotation also supports the qualifiers attribute in the same way. Each definition should be marked with a different qualifier.

If one of your OpenID definitions doesn't specify the qualifiers attribute, it would be used as the default one.

If more definitions don't specify it, you would see an error for ambiguous dependencies as you reported, because there will be more candidates for the default mechanism, which will conflict.

In order to be able to switch between the mechanisms, you also need to implement a HttpAuthenticationMechanismHandler as an alternative and enable it, e.g. with @Priority annotation, to replace the default handler. An example of this is in the same file in the TCK: CustomAuthenticationMechanismHandler.java. There, you can inject the mechanisms you defined using the qualifiers you assigned to them, e.g. :

@Alternative
@Priority(APPLICATION)
@ApplicationScoped
public class CustomAuthenticationMechanismHandler implements HttpAuthenticationMechanismHandler {

    @Inject
    @QualifierA
    HttpAuthenticationMechanism mechanismA;

    @Inject
    @QualifierB
    HttpAuthenticationMechanism mechanismB;

    @Override
    public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response,
            HttpMessageContext httpMessageContext) throws AuthenticationException {

        if (if (getRequestRelativeURI(request).startsWith("/alogin")) {) {
             return mechanismA.validateRequest(request, response, httpMessageContext);
        } else {
             return mechanismB.validateRequest(request, response, httpMessageContext);
        }
   }

@fschoning
Copy link
Author

Thanks for the pointer Ondro. I have added the qualifier parameters and the custom handler, but I still get an injection error.

I moved the @OpenIdAuthenticationMechanismDefinition from my web servlet class and put both on the CustomAuthenticationMechanismHandler class as in the tck test.

Here is my code:

@OpenIdAuthenticationMechanismDefinition(
        qualifiers = {QualifierA.class},
        providerURI = "${oidcConfigQualifierA.issuerUri}",
        clientId = "${oidcConfigQualifierA.clientId}",
        clientSecret = "${oidcConfigQualifierA.clientSecret}",
        redirectURI = "${baseURL}/oidcredirecturi",
        jwksReadTimeout = 5000, jwksConnectTimeout = 5000)

@OpenIdAuthenticationMechanismDefinition(
        qualifiers = {QualifierB.class},
        providerURI = "${oidcConfigQualifierB.issuerUri}",
        clientId = "${oidcConfigQualifierB.clientId}",
        clientSecret = "${oidcConfigQualifierB.clientSecret}",
        redirectURI = "${baseURL}/oidcredirecturi",
        jwksReadTimeout = 5000, jwksConnectTimeout = 5000)

@Alternative
@Priority(APPLICATION)
@ApplicationScoped
public class CustomAuthenticationMechanismHandler implements HttpAuthenticationMechanismHandler {

    @Inject
    @QualifierA
    HttpAuthenticationMechanism qualifierAAuthenticationMechanism;

    @Inject
    @QualifierB
    HttpAuthenticationMechanism qualifierBAuthenticationMechanism;

    @Override
    public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext httpMessageContext) throws AuthenticationException {

        try {
            URI uri = new URI(request.getRequestURI().toString());
            return switch (uri.getHost()) {
                case "events.qualifierA.com" -> qualifierAAuthenticationMechanism.validateRequest(request, response, httpMessageContext);
                case "events.qualifierB.com" -> qualifierBAuthenticationMechanism.validateRequest(request, response, httpMessageContext);
                default -> AuthenticationStatus.NOT_DONE;
            };
        } catch (URISyntaxException e) {
            return AuthenticationStatus.NOT_DONE;
        }
    }

}

And this is the error on deploy:

Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type HttpAuthenticationMechanism with qualifiers @QualifierA
at injection point [BackedAnnotatedField] @Inject @QualifierA com.launchautomator.events.viewer.security.CustomAuthenticationMechanismHandler.qualifierAAuthenticationMechanism
at com.launchautomator.events.viewer.security.CustomAuthenticationMechanismHandler.qualifierAAuthenticationMechanism(CustomAuthenticationMechanismHandler.java:0)

I am using Wildfly 34.0.0.preview and 34.0.1.preview both with the same result.

I also attempted to create a jakarta ee security tck test by copying the test you referred to and changing it to use OpenID instead, but ran into too many build problems to get the tck test compiled and run (I had to remove copyright checks, and then got missing glassfish build errors and I gave up there).

Appreciate any further pointers.

Franz

@OndroMih
Copy link
Contributor

OndroMih commented Jan 9, 2025

Interesting. I tried it on GlassFish 8 milestone and it doesn't work there either, I have the same result as you. However, when I replace @OpenIdAuthenticationMechanismDefinition with @BasicAuthenticationMechanismDefinition, all works. The tests in the TCK only test with basic mechanism, so the tests pass. But it doesn't work with OpenID.

This would be an issue for the Soteria project, which is used by both WildFly and GlassFish. Or directly for WildFly. The Security API is OK, the only issue is that the TCK doesn't test the case with OpenID annotations.

@fschoning
Copy link
Author

Thanks for checking. Will you open the issue in Soteria? I see you are already adding a tck test for this.

@OndroMih
Copy link
Contributor

OndroMih commented Jan 9, 2025

I create an issue for Soteria: eclipse-ee4j/soteria#389

I suggest that you create an issue for the WildFly team, @fschoning , so that they are aware.

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

No branches or pull requests

2 participants