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

Programatically login using Form Authentication #409

Open
Ryaryu opened this issue Mar 7, 2024 · 6 comments
Open

Programatically login using Form Authentication #409

Ryaryu opened this issue Mar 7, 2024 · 6 comments
Labels
question Further information is requested

Comments

@Ryaryu
Copy link

Ryaryu commented Mar 7, 2024

Hello, I'm working on an app that has to use Form Authentication.

Normally we would just create a form posting to /j_security_check and that would work with Quarkus.
But I need to do some work before calling the endpoint, therefore I'm calling a method from a @Named bean.
The problem is that after doing this before-hand work, when I do call ((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).login(username, password);, the method works perfectly but Quarkus doesn't generate a Cookie.

Have you found a solution for this?
Currently I'm picking the external host/url and manually calling /j_security_check with an absolute URL, but that is very ugly and error prone.

@melloware
Copy link
Owner

Because Quarkus obsviously works differently than a normal EE container i am not surprised by this.

@tmulle asked this very question on this ticket how to programmtic "login" and I don't think it ever got answered as that topic morphed more into progammatic logout and the login question i am not sure was ever answered was it @tmulle?

Read this thread: quarkusio/quarkus#27389

@melloware melloware added the question Further information is requested label Mar 7, 2024
@melloware
Copy link
Owner

melloware commented Mar 8, 2024

@Ryaryu it might be worth opening another Quarkus ticket and reference that original ticket that was never answered about programmatic login?

I also asked the Devs on Zulip Chat as well: https://quarkusio.zulipchat.com/#narrow/stream/187038-dev/topic/j_security_check.20Programmatic.20Login.3F

@Ryaryu
Copy link
Author

Ryaryu commented Mar 8, 2024

Fair enough.

@melloware
Copy link
Owner

I will let you know what they say on the chat.

@melloware
Copy link
Owner

@Ryaryu can you post a small sample code of how you authenticate and logout with quarkus Faces? There are other users asking how to create a login form etc. we have an OIDC example from @tmulle but not a basic login form authentication example. Even snippets here will be fine?

@Ryaryu
Copy link
Author

Ryaryu commented Mar 28, 2024

Oh... sure.
I'm using a single Bean to handle both.

@Named
@RequestScoped
public class LoginController {

  @Getter
  @Setter
  String username;

  @Getter
  @Setter
  String password;

  @Inject
  FacesContext facesContext;

  @ConfigProperty(name = "quarkus.http.auth.form.cookie-name")
  String cookieName;

  /**
  * Clear cookieName and redirects to my login page (/login.xhtml)
  */
  public String logout() {
    var fcResponse = (HttpServletResponse) facesContext.getExternalContext().getResponse();
    var cookie = new Cookie(cookieName, "");
    cookie.setMaxAge(0);
    fcResponse.addCookie(cookie);
    return "/login.xhtml?faces-redirect=true";
  }

  public void login() {
    try {
      var request = (HttpServletRequest) facesContext.getExternalContext().getRequest();

      generateCookie(request);

      // redirect to your main page.
      facesContext.getExternalContext().redirect("/principal.xhtml");
    } catch (Exception ex) {
      // do something?
    }
  }

  /**
   * Here we just replace the login form partial URL (in my case /login.xhtml) with /j_security_check
   * and make a request there so Quarkus can create the session cookie
   */
  private void generateCookie(HttpServletRequest request) throws IOException, InterruptedException {
    var securityCheckUrl = request.getRequestURL().toString()
        .replace("/login.xhtml", "/j_security_check");
    var response = jSecurityCheckRequest(securityCheckUrl);

    var fcResponse = (HttpServletResponse) facesContext.getExternalContext().getResponse();
    setCookie(response, fcResponse);
  }

  /**
  * Magic lies here.
  * We set the cookie generated by the /j_security_check request into the FacesContext response.
  */
  private void setCookie(HttpResponse<String> response, HttpServletResponse fcResponse) {
    var responseMap = response.headers().map();
    if (responseMap.containsKey("set-cookie")) {
      var cookieString = responseMap.get("set-cookie").get(0);
      var quarkusCookie = new Cookie(cookieName, cookieString.split("=")[1]);
      quarkusCookie.setMaxAge(8 * 60 * 60);
      quarkusCookie.setHttpOnly(true);
      fcResponse.addCookie(quarkusCookie);
    }
  }

  private HttpResponse<String> jSecurityCheckRequest(String securityCheckUrl)
      throws IOException, InterruptedException {
    var response = HttpClient.newHttpClient().send(HttpRequest.newBuilder()
        .uri(URI.create(securityCheckUrl))
        .POST(HttpRequest.BodyPublishers.ofString(
            "j_username=" + username + "&j_password=" + password))
        .header("Content-Type", "application/x-www-form-urlencoded")
        .build(), HttpResponse.BodyHandlers.ofString());
    return response;
  }

}

You can then call this bean from your xhtml freely, #{loginController.login()} or #{loginController.logout().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants