Skip to content

WebSocket⚓︎

The nextAuth Server features a WebSocket interface which exposes the session status and has support for a limited number of actions. For instance, it can be used to dynamically update user interfaces or to listen for session status changes server-side, without having to execute API calls repeatedly.

Commands⚓︎

The following table documents the different commands which are sent to and received from the server. Commands are always encoded as strings and are formatted as {Type} {HSID} {Server ID}, with the HSID and Server ID fields taking base64-url-encoded values.

Type Direction Description
REGISTER Client Server This command should be sent to receive status updates for the specified session (i.e., the LOGIN, LOGOUT, and CANPROVOKE commands).
CONFIRMSCAN Server Client The client has successfully scanned and processed a presented QR code.
LOGIN Server Client The specified session is logged in. You will receive either a LOGIN or LOGOUT command in response to REGISTER.
LOGOUT Server Client The specified session is logged out. You will receive either a LOGIN or LOGOUT command in response to REGISTER.
CANPROVOKE Server Client It is possible to provoke a login for the specified session, which can be requested through the PROVOKE command.
PROVOKE Client Server Provoke a login for the specified session by sending a push command to the linked device.
CONFIRMPROVOKE Server Client The login push message has been processed by the client.
CANCEL Server Client The user aborted the authentication flow (Authentication Server >= v2.1.2, Android SDK >= v1.1.4, and iOS SDK >= v1.1.2).
LOGOUT Client Server Log out the specified session.
NOACCOUNTS1 Server Client The user scanned a login QR code (or followed a login deep link) but has no accounts registered in the app for this server (Authentication Server >= v2.2.2, Android SDK >= v1.1.7, and iOS SDK >= v1.2.1).

For instance, you would send the following string to the server in order to start receiving status updates, with the listed server reply for a provokable inactive session.

-> REGISTER {HSID} {Server ID}

<- LOGOUT {HSID} {Server ID}
<- CANPROVOKE {HSID} {Server ID}

JavaScript⚓︎

To set up the WebSocket and handle the changes in the LoginStatus of the session, one can use the javascript below, replacing serverid , hsid and loggedin with the correct values in the init function. This script will reload the current page on receiving a LOGIN (when the current status is false - logged out), or redirect to the main page on receiving a LOGOUT (when the current status is true - logged in). You get the loggedin and hsid values you need through the getSession API calls.

<script>
var nextAuth = (function() {
    var wssocket;
    var wssocketerror;
    var wssocketdisabled;

    return {
        init: function(){
            nextAuth.wsinit([['serverid', 'hsid', loggedin]], 'wss://ws.nextauth.com/');
        },
        wsinit: function(sessiondata, host) {
            serverid = sessiondata[0][0];
            nonce = sessiondata[0][1];
            wssocketerror = true;
            wssocketdisabled = false;

            // test websocket support
            if ('WebSocket' in window || 'MozWebSocket' in window) {
                window.setInterval(nextAuth.wscheck, 500, sessiondata, host);
            }
        },
        logmsg: function(msg) {
            if (typeof console !== "undefined") {
                console.log("nextAuth - " + msg);
            }
        },
        wscheck: function(sessiondata, host) {
            if (!wssocketerror)
                return;
            if (wssocketdisabled)
                return;
            wssocketerror = false;
            try {
                if ('WebSocket' in window) {
                    wssocket = new WebSocket(host);
                } else if ('MozWebSocket' in window) {
                    wssocket = new MozWebSocket(host);
                }
                nextAuth.logmsg('WebSocket - Status ' + wssocket.readyState);
                wssocket.onopen = function(msg) {
                    nextAuth.logmsg("Connected");
                    for (var i = 0; i < sessiondata.length; i++) {
                        nextAuth.wssend("REGISTER " + sessiondata[i][1] + " " + sessiondata[i][0]);
                    }
                };
                wssocket.onmessage = function(msg) {
                    nextAuth.logmsg("Received: " + msg.data);
                    nextAuth.wsreceive(msg.data, sessiondata);
                };
                wssocket.onclose = function(msg) {
                    nextAuth.logmsg("Disconnected");
                    wssocketerror = true;
                };
            } catch (e) {
                nextAuth.logmsg(e);
                wssocketerror = true;
            }
        },
        wssend: function(msg) {
            try {
                wssocket.send(msg);
                nextAuth.logmsg('Sent: ' + msg);
            } catch (e) {
                nextAuth.logmsg(e);
            }
        },
        getsession(sessiondata, serverid, nonce){
            for (var i = 0; i < sessiondata.length; i++){
                if (sessiondata[i][0] == serverid && sessiondata[i][1] == nonce){
                    return i;
                }
            }
            return -1
        },
        wsreceive: function(msg, sessiondata) {
            parts = msg.split(" ");
            cmd = parts[0];
            if (cmd == "LOGOUT") {
                serverid = parts[2];
                nonce = parts[1];
                i = this.getsession(sessiondata, serverid, nonce);
                if (i >= 0) {
                    knownstatus = sessiondata[i][2];
                    if (knownstatus == 1){
                        // currently logged in, received logout
                        wssocketdisabled = true; // kill the socket
                        if (typeof nextauthwsdoupdate == 'function')
                            nextauthwsdoupdate(0, serverid, nonce, i);
                        else
                            window.location.assign("/");
                    }
                }
            } else if (cmd == "LOGIN") {
                serverid = parts[2];
                nonce = parts[1];
                i = this.getsession(sessiondata,serverid,nonce);
                if (i >= 0) {
                    knownstatus = sessiondata[i][2];
                    if (knownstatus != 1) {
                        // currently logged in, received logout
                        wssocketdisabled = true; // kill the socket
                        if (typeof nextauthwsdoupdate == 'function')
                            nextauthwsdoupdate(1, serverid, nonce, i);
                        else
                            window.location.reload();
                    }
                }
            } else if (cmd == "CANPROVOKE") {
                serverid = parts[2];
                nonce = parts[1];
                nextAuth.wscanprovoke(serverid,nonce);
                if (typeof nextauthwscanprovoke == 'function') {
                    nextauthwscanprovoke(serverid,nonce);
                }
            } else if (cmd == "CONFIRMPROVOKE") {
                serverid = parts[2];
                nonce = parts[1];
                nextAuth.wsprovokeconfirm(serverid,nonce);
                if (typeof nextauthwsprovokeconfirm == 'function') {
                    nextauthwsprovokeconfirm(serverid,nonce);
                }
            }
        }
    }
})();

nextAuth.init();
</script>

Note that this script can listen to multiple sessions, possibly at different servers and nonces. Simply add another ['serverid', 'hsid', loggedin] to the first parameter of nextAuth.wsinit inside the init function. In that case, one should implement a nextauthwsdoupdate(x, serverid, nonce, i) function, with x being either 0 (logout) or 1 (login) to do proper session-based handling.


  1. Since the login QR code (or deep link) does not contain the data needed for the SDK to connect with your nextAuth server instance, this will need to be specified in the mobile SDK configuration