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 SPA Form Based Authentication instructions #36818

Merged
merged 1 commit into from
Nov 4, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/src/main/asciidoc/security-authentication-mechanisms.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,83 @@ quarkus.http.auth.form.landing-page=
# do not redirect, respond with HTTP 401 Unauthorized
quarkus.http.auth.form.login-page=
quarkus.http.auth.form.error-page=

# HttpOnly must be false if you want to logout on the client, it can be true if logging out on from the server
quarkus.http.auth.form.http-only-cookie=false
----

Now that you have disabled redirects for the SPA, you must login and logout programmatically from your client.
Below are example JavaScript methods for logging into the `j_security_check` endpoint and logging out of the application by destroying the cookie.

[source,javascript]
----
const login = () => {
// Create an object to represent the form data
const formData = new URLSearchParams();
formData.append("j_username", username);
formData.append("j_password", password);

// Make an HTTP POST request using fetch against j_security_check endpoint
fetch("j_security_check", {
method: "POST",
body: formData,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
})
.then((response) => {
if (response.status === 200) {
// Authentication was successful
console.log("Authentication successful");
} else {
// Authentication failed
console.error("Invalid credentials");
}
})
.catch((error) => {
console.error(error);
});
};
----

To logout of the SPA from the client the cookie must be set to `quarkus.http.auth.form.http-only-cookie=false` so you can destroy
the cookie and possibly redirect back to your main page.

[source,javascript]
----
const logout= () => {
// delete the credential cookie essentially killing the session
const removeCookie = `quarkus-credential=; Max-Age=0;path=/`;
Copy link
Member

Choose a reason for hiding this comment

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

Does it work out of the box, with the httpOnly flag set to true by default ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sberyozkin out of the box it looks like httpOnly is set to false by default:
image

Copy link
Member

Choose a reason for hiding this comment

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

@melloware thanks, https://github.com/quarkusio/quarkus/blob/main/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/FormAuthConfig.java#L127 indeed have a default false value. It should be set to true by default, but it is another issue.

So IMHO it has to be noted that this solution only works with the HttpOnly disabled, and I wonder if it is a good idea to recommend a logout solution which depends on the cookie being accessible to JavaScript.

Should a logout handler solution be recommended ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good points. I guess I should mention that it has to be httponly false and also provide an example server side controller logout?

Copy link
Member

@michalvavrik michalvavrik Nov 3, 2023

Choose a reason for hiding this comment

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

It should be set to true by default

+1

I wonder if it is a good idea to recommend a logout solution which depends on the cookie being accessible to JavaScript.

Here @melloware tries to document logout for SPA. SPA FWs actively prevents XSS (sanitize) unless you explicitly override it, in which is you are aware of what you are doing. All points you raised are absolutely true, but what is recommended here is not dangerous for typical SPA.

That is why I suggested this new code examples should be rather treated as meta code, because JS + AJAX doesn't provide OOTB protection nor the typical way you handle things (how you store state, route, ...) with SPA.

Good points. I guess I should mention that it has to be httponly false and also provide an example server side controller logout?

+1

I'd also mention why - XSS and make it clear when it is needed.

Copy link
Member

@michalvavrik michalvavrik Nov 3, 2023

Choose a reason for hiding this comment

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

This thread is on logout, same is true for login documented here because you won't be able to use this cookie for BE requests if you can't access it.

document.cookie = removeCookie;

// perform post logout actions here such as redirecting back to your login page
};
----

To logout of the SPA from the server the cookie can be set to `quarkus.http.auth.form.http-only-cookie=true` and use this example
code to destroy the cookie.

[source,java]
----
@ConfigProperty(name = "quarkus.http.auth.form.cookie-name")
String cookieName;

@Inject
CurrentIdentityAssociation identity;

@POST
public Response logout() {
if (identity.getIdentity().isAnonymous()) {
Copy link
Member

Choose a reason for hiding this comment

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

@Authenticated would do a same job if you insist on 401. Personally I don't know what is expected behavior.

throw new UnauthorizedException("Not authenticated");
}
final NewCookie removeCookie = new NewCookie.Builder(cookieName)
.maxAge(0)
.expiry(Date.from(Instant.EPOCH))
.path("/")
.build();
return Response.noContent().cookie(removeCookie).build();
}

----

The following properties can be used to configure form-based authentication:
Expand Down