bekwam courses

WildFly and Vue.js Form Auth

January 1, 2021

This article is about using JavaEE cookie-based Form Authentication in a Vue.js app. Form Authentication sets a cookie when the user authenticates their credentials. This cookie is transmitted with each request and that includes those issued by Vue through Axios or the WebSocket API. Security is further strengthened by using an HTTP Only Cookie which means that Vue -- and JavaScript in general -- have no capability to handle the user credentials.

The JavaEE code described this article is on GitHub.

To run a deployed version of the demo, go here: https://bekwam.us/ws-form.

Background

A previous article demonstrated Basic Authentication applied to a Vue.js app. The app contained a login form and when the user entered their credentials, the login information was saved. When the user began interacting with a WebSocket, the credentials were incorporated into the URL.

One drawback of Basic Authentication is the handling of the credentials. Inspecting the Browser tools or runtime environment, you can see the credentials in at least a Base 64 encoded format. This means that through interacting with the browser, you can get the credentials. This is a problem not only for the site at hand, but if the user uses the same credentials on different sites, multiple sites may be compromised.

Additionally, hacked code might be mixed in with the Vue app if the v-html tag is used. This tag will render dynamic content in a Vue app, say pulling in a user-generated fragment from a database. If the credentials are in memory as shown in the Browser devtools, a malicious script could send these credentials outside of the app.

Form Authentication

One solution is to use a security mechanism outside of Vue called Form Authentication. This is standard JavaEE and involves a choreography of several web pages. Once this choreography is completed, the Browser interacts directly with the server, leaving Vue free to operate under an umbrella of protection.

This sequence diagram shows the steps to authenticate the demo. The user first requests index.html and clicks on a link to go to file "hw.html". hw.html is a protected resource containing a Vue.js instance and WebSocket code. The JavaEE application server -- WildFly -- challenges this credential-free transmission and directs the user to login.html. The user enters the username and password and proceeds to hw.html.

UML Sequence Diagram
Calling Secured WebSocket

Once at hw.html, the app is a Vue.js SPA. Clicking on the buttons invokes button handlers that interact with the WebSocket API. The Vue.js app contains no special security code beyond the wss:// URL requiring TLS. The responsibility to add in credentials to the transmissions lies with the Browser.

This can be seen in the red "get()" arrows in the diagram. The initial get() bears no credentials. After the redirect, a cookie "JSESSIONID" is set in the browser. When this cookie accompanies a request from the Vue app, that request is allowed to be fulfilled by the app server. This is done with an HTTP Only cookie. Vue can't get at the cookie even if we coded it.

Configuration

To enable the demo, the application server needs a Security Domain. This is the mechanism by which JavaEE will retrieve and compare the stored credentials. This article goes into detail on how to set up the Security Domain.

In addition to the link referenced above, WildFly needs a pair of settings to make its cookies HTTP Only and Secure.


[standalone@localhost:9990 setting] /subsystem=undertow/servlet-container=default/setting=session-cookie:add(http-only=true)

[standalone@localhost:9990 setting] /subsystem=undertow/servlet-container=default/setting=session-cookie:write-attribute(name=secure,value=true)

There is also a special undertow-handlers.conf file in /WEB-INF that enforces a Strict handling of SameSuite cookies. See this link for the WildFly feature description which includes references to the capability.

Server

The server-side code is a WebSocket Endpoint and a Servlet. The WebSocket Endpoint is the demo part of the application. Once authenticated, the user interacts with the WebSocket to get a message. The Servlet is a resource that logs the user out, sending the user to index.html afterwards. Note there is no corresponding Servlet needed for logging in the user; that's handled by WildFly.

See the full code listing for the server-side code here.

UI

The UI code is a collection of .html file, one of which runs a Vue.js app instance. If you ran the live demo, you will have seen index.html. This is the main landing page which will take you to the login.html or to hw.html.

The .html files would normally be processed with a server-side engine like Java Server Pages to enable / disable buttons based on login status. This functionality -- essential for a good design -- was omitted to keep the focus on vue.

The full code listing for the UI is here.

One code snippet worth mentioning is the form element in index.html. This directs a form submission to j_security_check which is a standard JavaEE handler. There is no custom server-side handling code for login.

	
<form action="j_security_check" method="POST">
    <div class="field">
        <label class="label">Username</label>
        <div class="control">

With the login established, the user is permitted to proceed to hw.html and also use the WebSocket endpoint. This code shows that there is no special security code (like username@password) that is part of the WebSocket connect call.


	connect() {
		if( this.ws == null ) {
			this.errorMessage = "";
			this.message = "";
			try {
				//let url = `ws://localhost:8080/ws-form/hw`;
				let url = `wss://bekwam.us/ws-form/hw`;
				this.ws = new WebSocket(url);
				this.ws.onerror = (err) => {
					this.errorMessage = "Can't Connect to Socket-3";
					this.ws = null;
				};
				this.ws.onmessage = ({ data }) => (this.message = data);
			} catch(e) {
				this.errorMessage = e; // url syntax error
			}
		}
	}

Finally, there is the application configuration. These are non-code instructions to WildFly configuring the security mechanism used. This is the web.xml file (a JavaEE artifact), starting with a security-constraint that will security both hw.thml and the WebSocket endpoint "/hw".

	
    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected WS</web-resource-name>
            <url-pattern>/hw</url-pattern>
            <url-pattern>/hw.html</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>codepenuser</role-name>
        </auth-constraint>
    </security-constraint>	
	

This is followed by a role definition. This maps to a group set up in the linked article on Security Domains.


    <security-role>
        <role-name>codepenuser</role-name>
    </security-role>

These elements tell WildFly to use Form Authentication.


    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
            <form-login-page>/login.html</form-login-page>
            <form-error-page>/loginError.html</form-error-page>
        </form-login-config>
    </login-config>

The biggest drawback to this approach is that it mixes server-side code with Vue.js. Vue is at its cleanest when it runs the whole app. However, I just went through a PCI (Payment Card Industry) audit who gave a strong preference to infastructure that disabled the handling of credentials by JavaScript. The solution presented here was for JavaEE, but the same approach can be used in other frameworks and languages like CakePHP.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc