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

Add stateless authentication (Quarkus OIDC) #30

Open
Dudeplayz opened this issue May 8, 2023 · 9 comments
Open

Add stateless authentication (Quarkus OIDC) #30

Dudeplayz opened this issue May 8, 2023 · 9 comments
Labels
enhancement New feature or request security

Comments

@Dudeplayz
Copy link
Collaborator

In the first, check if it working OOB.

@UbiquitousBear
Copy link

UbiquitousBear commented Jan 12, 2024

I've spent a few hours trying to get OIDC working with Quakus + Hilla.

For notes, I've set up my config as such:

quarkus.oidc.auth-server-url=https://
quarkus.oidc.client-id=code
quarkus.oidc.credentials.secret=woof
quarkus.oidc.application-type=web_app
quarkus.http.auth.permission.authenticated.paths=/*
quarkus.http.auth.permission.authenticated.policy=authenticated
quarkus.oidc.authentication.scopes=openid,profile,email
quarkus.oidc.authentication.user-info-required=true
quarkus.oidc.roles.role-claim-path=groups

This has enabled the app to automatically redirect to the OAuth RS to authorize, and the application receives and stores the auth.

I've had to add an Endpoint, one I'm calling AuthenticationApi with the following code:

@BrowserCallable
@AnonymousAllowed
@AllArgsConstructor
public class AuthenticationApi {
    private final SingleSignOnContext context;
    private final UserInfo userInfo;
    private final SecurityIdentity securityIdentity;

    public @Nonnull SingleSignOnData fetchAll() {
        return context.getSingleSignOnData();
    }

    public String token() {
        return Optional.ofNullable(securityIdentity.getCredential(AccessTokenCredential.class)).map(token -> token.getToken()).orElse(null);
    }

    public @Nonnull List<@Nonnull String> getRegisteredProviders() {
        return context.getRegisteredProviders();
    }

    public OidcUser getAuthenticatedUser() {
        return context.getSingleSignOnData().isAuthenticated() ? OidcUser.fromUserInfo(userInfo) : null;
    }
}

Of interest is the token method, as I need to get the token to bootstrap the HTTPXmlRequests made to the API from the react front-end, using a middleware:

import { Middleware, MiddlewareContext, MiddlewareNext } from '@hilla/frontend';
import { AuthenticationApi } from 'Frontend/generated/endpoints';

export const AuthenticatedRequestMiddleware: Middleware = async function(
  context: MiddlewareContext,
  next: MiddlewareNext
) {

    if (!context.request.url.includes("AuthenticationApi")) {
        const token = await AuthenticationApi.token()
        context.request.headers.append("Authorization", "Bearer " + token)
    }

    return await next(context);    
};

With the above, sadly, it's making a new call to get the token, so I should store it in a cache (or swr).

The conundrum however appears at the controller level: per Quarkus' documentation, the @PermitAll annotation: Specifies that all security roles are allowed to invoke the specified methods. @PermitAll lets everybody in, even without authentication., which is in contrast to what Hilla does: @PermitAll Allows any authenticated user to call a method via the request.

I suppose the only way around this right now is to use RBAC: @RolesAllowed("dcc-editor"). which does work. I may need to create a custom Annotation to hide the above.

@mcollovati
Copy link
Owner

Hi @UbiquitousBear, thank you for giving a try to quarkus-hilla and for the feedback.

To better understand the problem, is the issue related to calls to QuarkusEndpointController.serveEndpoint() or to the specific methods in the @BrowserCallable annotated class?

@mcollovati
Copy link
Owner

If the problem is the @BrowserCallable methods blocked by Quarkus security and redirected to the Identity Provider, you should permit access to the QuarkusEndpointController path

quarkus.http.auth.permission.hilla.paths=/connect/*
quarkus.http.auth.permission.hilla.policy=permit

This should probably be done automatically by quarkus-hilla in some way.

@UbiquitousBear
Copy link

@mcollovati To clarify, I believe there's two different definitions for @PermitAll: which makes things confusing - that said, what I've done so far is to force OIDC on all endpoints, which seems to fit the job for me, then use @RolesAllowed. Using @PermitAll doesn't seem to permit unauthenticated calls, but I need to play around a bit more.

On a different note, is it possible to define a custom annotation as such:

@BrowserCallable
@RolesAllowed("dcc-editor")
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticatedApiEndpoint {}

Any use this on Endpoints/BrowserCallables? It'd mean I don't need to replicate @BrowserCallable and @RolesAllowed("dcc-editor") across endpoints. I've tried using this, but getting a 404 from Quarkus - presumably because the endpoint isn't beaned (dev-ui doesn't show it as an endpoint).

@mcollovati
Copy link
Owner

I believe there's two different definitions for @permitAll

That's indeed true. What we can do is to introduce a build step that converts the @PermitAll annotation to @RolesAllowed("**") on classes annotated with @BrowserCallable or @EndpointExposed.

About the meta annotations, I don't know if this works out-of-the-box in a plain Hilla project.
You can try to add the @Stereotype annotation to your definition and see if in this way the bean is discovered

@UbiquitousBear
Copy link

@mcollovati what are we defining as Stateless here; that requests are made without the sessions cookie?

@Dudeplayz
Copy link
Collaborator Author

@UbiquitousBear we define it as not be bound to a server side session/context (e.g. this definition) in terms of authentication/authorization. I named the ticket this way, because I wasn't sure what other auth mechanisms are available, so it is a bit missleading. Cookie/Header based auth per request is basically always stateless, as long it is not bound to a specific server side session or context. Hilla is designed to be stateless and as we are replacing Spring Boot internals with equivalent Quarkus code, it should work. We just haven't tested it ourself nor added tests for it. So we can't guarantee for it.

@vexa
Copy link

vexa commented Oct 29, 2024

What does it look like? It is already a key functionality that everything should work statelessly. Is work still being done at this point or is there already a definitive solution? Thank you very much for your work!

@Dudeplayz
Copy link
Collaborator Author

What does it look like? It is already a key functionality that everything should work statelessly. Is work still being done at this point or is there already a definitive solution? Thank you very much for your work!

Hi @vexa, thanks for the interest! We still hadn't the time to test it. It could work out of the box, but we can't gurantee it. I am self interested in using it in my own projects, but can't say when I will dig into it. It is still on our todo list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request security
Projects
Status: 📋 Backlog
Development

No branches or pull requests

4 participants