bekwam courses

WildFly and Vue.js WebSocket Basic Auth

December 29, 2020

This article shows how to use an authenticated and confidential WebSocket with Vue.js. A CodePen running a Vue.js app will open up a WebSocket connection via SSL to a server. The app will authenticate and communicate a simple request / response protocol over a WebSocket.

The server-side JavaEE code described this article is on GitHub. The source code for the Vue.js portion can be found in the CodePen.

Basic Authentication is a standard capability of JavaEE app servers. WebSockets -- like web applications or RESTful web services -- can use this capability to authenticate users. Additionally, the JavaEE app server provides confidentiality through SSL which can enforced to prevent the WebSocket from operating in a non-SSL deployment.

The following CodePen interacts with a WebSocket hosted at bekwam.us. When you enter the username and password as given in the explanatory paragraph, you can press the Connect button. This will pass the entered credentials as a Basic Authorization HTTP header. Once connected, you can press the Fetch button to interact with the WebSocket hosted at bekwam.us. Finally, you can sever the connection with the Close button.

See the Pen Vue Web Socket Auth Demo by Carl Walker (@walkerca) on CodePen.

You can also try the negative case. Close the connection or refresh the browser. Enter something other than the username / password listed in the CodePen. Notice that you get a red error banner.

This class diagram shows the logical model for the demo. The Browser contacts CodePen and runs the Vue.js app. The Vue.js app uses a standard WebSockets API connection to connect with bekwam.us. On bekwam.us, the WildFly app servers's Elytron subsystem implements the authentication mechanism. Elytron verifies the incoming credentials against a hashed entry in a file.

Class Diagram
WebSocket Basic Auth Logical Model

This sequence diagram shows a development view of the interaction. The Vue.js app uses WebSockets API calls to contact code running in WildFly, specifically the endpoint "SecuredHelloWorldEndpoint". En route to calling the endpoint, the app server examines the credentials passed in the header. Since they match, the connection is allowed. In this manner, the WebSocket operates like a plain RESTful web service, utilizing an HTTP request before upgrading to the WebSockets protool.

Sequence Diagram
WebSocket Basic Auth Development View

To run the demo, you'll need to configure WildFly. This article shows how to set up the CodePenSecurityDomain referenced below.

Server Code

The WebSockets endpoint is a single Java class with a method annotated to handle the onMessage event. This is packaged and deployed in a WAR (Web ARchive) file. The open and close behaviors are the default. When a message is received from the Vue.js app -- it doesn't matter what the actual contents are -- the endpoint will return "Hello, World!" plus a timestamp to show unique calls.


@ServerEndpoint("/hw")
public class SecuredHelloWorldEndpoint {
    @OnMessage
    public void sayHW(Session session, String dummy) throws Exception {
        session.getBasicRemote().sendText(
                "Hello, World! " +
                        LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
        );
    }
}

Notice that there is no security code mixed in with the endpoint's functionality. Instead, configuration within the deployed WAR file secures the WebSocket. This is the app configuration from the web.xml file.


    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Protected WS</web-resource-name>
            <url-pattern>/hw</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>codepenuser</role-name>
        </auth-constraint>
        <user-data-constraint>
            <description>
                This prevents the WebSocket from operating with ws:// connections
            </description>
            <transport-guarantee>CONFIDENTIAL</transport-guarantee>
        </user-data-constraint>
    </security-constraint>

Like any web resource in a JavaEE app server, the WebSocket is secured by URL. The url-pattern "/hw" is the one registered as the endpoint (see SecuredHelloWorldEndpoint). There is an auth-constraint of codepenuser that says whoever accesses this MUST have the role "codepenuser". There is also a user-data-constraint of CONFIDENTIAL. While this does not enable SSL -- that's done through additional WildFly configuration -- this prohibits the endpoint from responding to ws:// requests. Only wss:// requests are allowed.

The codepenuser role referenced in the security-constraint requires a declaration in a security-role element.


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

Finally, the login-config is specified with an auth-method of BASIC. The realm-name is informational when using BASIC authentication. It can be used to customize a browser popup that throws a password challenge.


    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>CodePenRealm</realm-name>
    </login-config>

In addition to the JavaEE standard web.xml, there is a jboss-web.xml that will be used. This gives the app a shorter URL (not essential to the demo) with the context-root setting. More importantly, it also sets up the Application Security Domain to be used within WildFly.


<jboss-web>
    <context-root>/ws-basic</context-root>
    <security-domain>CodePenSecurityDomain</security-domain>
</jboss-web>

As mentioned in the linked article on setting up the Properties Realm, the security-domain is an Undertow Application Security. This can be confusing because that is mapped to an Elytron Security Domain. Further complicating things are the Legacy Security Security Domains which appear in the WildFly configuration file, but are not part of this or the other article.

Client Code

The CodePen is a Vue.js app. The Vue.js app v-models username and password fields for use in a WebSocket API call. The source from the CodePen forms a URL containing the username and password. See the following snippet.


let url = `wss://${this.username}:${this.password}@bekwam.us/ws-basic/hw`;
this.ws = new WebSocket(url);

this.ws is a data field for the Vue.js component. The connection state is determined by whether or not this field is set.

When the WebSocket is created, a message handler is registered that will set a component data field to the results. The results will be "Helo, World!!!" followed by a timestamp. To see this, repeatedly press the Fetch button which will repeatedly contact the server.


this.ws.onmessage = ({ data }) => (this.message = data);

When the Fetch button is pressed, the Vue.js app call the following method "callWS". Sometime later (hopefully instantaneously) the server will respond and call the onmessage handler.


 callWS() {
      try {
        this.errorMessage = "";
        this.ws.send("dummy");
      } catch(e) {
        console.error(e);
        this.errorMessage = e;
      }
    }

The Close button breaks the WebSocket connection and resets the program state for another trial.


close() {
      this.message = "";
      this.errorMessage = "";
      if( this.ws != null ) {
        try {
          this.ws.close();
          this.ws = null;
          this.username = null;
          this.password = null;
        } catch(e) {
          this.errorMessage = e;
        }
      }
    }

This example showed how to secure a WebSocket endpoint hosted in the WildFly application server and how to call it using the standard WebSockets API in a Vue.js app. Functionally, everything works and is secured. However, a hard refresh will require the credentials to be entered again or the credentials to be stored in the browser. Future articles will present alternatives that treat the credentials better, though with additional configuration and code.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc