0
votes

Im working at a project with complex interaction between a wordpress backend (for storing messages, posts), an angular frontend (Angluar 6x) for user interaction, and an eJabber server for chat communication from within the web application.
In order to achieve this, I'm trying to integrate strophe.js into the Angular framework.
The authentication method I use is complex. When a new user registers to the wordpress backend by means of the suitable (angular) form, I've set up hooks that also subscribe the new user automatically to my local eJabberd server. To manage authentication, in order to save the final user from entering twice his/her credentials, I've set up in eJabberd a custom extauth script (based on this) which works in parallel with custom wordpress rest API endpoints (I'm using JWT for that).
The extauth script is working fine and I can successfully connect to my local eJabberd server, with various chat clients (Adium, Apple's Messages, etc.). I can connect (as said, using strophe) to eJabber from within my web app as well.

Here is a snippet of the code I use to manage the connection (ChatPanel.Service.ts):

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Strophe, $pres } from 'strophe.js';

import { EJABBERD } from 'app/api/api.module';

var chatPanelServiceInstance: any = null;

@Injectable()
export class ChatPanelService
{
    contacts: any[];
    chats: any[];
    user: any;
    client: any;

    // Private
    private _xmppConnectionsString: String = "ws://" + EJABBERD.host + ":5280/ws";
    private _xmppConnection: any = null;

    /**
     * Constructor
     */
    constructor(
    )
    {
        chatPanelServiceInstance = this;
        Strophe.log = (level: any, msg: string) => { console.log(level + ": " + msg); };
    }

    /**
     * Log into eJabberd
     *
     * @param {string} jid      user name
     * @param {string} password password (actually, the user token)
     */
    login(jid: string, password: string): void
    {
        if ( ! this._xmppConnection ) {
            this._xmppConnection = new Strophe.Connection( this._xmppConnectionsString , {'keepalive': true});
        }

        this._xmppConnection.connect(jid+'@'+EJABBERD.host, password, this._onConnect);
    }

    /**
     * Disconnect from eJabberd
     */
    logOut(): void
    {
        if ( this._xmppConnection ) {
            this._xmppConnection.options.sync = true;
            this._xmppConnection.flush();
            this._xmppConnection.disconnect("logout");
            this._xmppConnection = null;
        }
    }

    /**
     * eJabberd XMPP message Handler
     * @param {string} msg Message received
     */
    private _onMessage(msg: string): boolean
    {
        console.log("eJabber Msg: " + msg);

        return true;
    }

    /**
     * eJabberd connection Handler
     * @param {any} status connection result
     */
    private _onConnect(status: any): void
    {
        switch (status) {
            case Strophe.Status.CONNECTING:
                console.log("Connecting to eJabberd...");
                break;
            case Strophe.Status.CONNFAIL:
                console.log("eJabberd connection failed!");
                break;
            case Strophe.Status.DISCONNECTING:
                console.log("Disconnecting from eJabberd...");
                break;
            case Strophe.Status.DISCONNECTED:
                console.log("Disconnected from eJabberd");
                break;
            case Strophe.Status.CONNECTED:
                //handler function  [ onMessage() ]  will be called when the user recieves a new message
                chatPanelServiceInstance._xmppConnection.addHandler(chatPanelServiceInstance._onMessage, null, 'message');

                //Setting our presence in the server. so that everyone can know that we are online
                chatPanelServiceInstance._xmppConnection.send($pres().tree());

                console.log("eJabberd connected!");
                break;
            case Strophe.Status.AUTHENTICATING:
                console.log("eJabberd authenticating...");
                break;
            case Strophe.Status.AUTHFAIL:
                console.log("eJabberd authentication failed!");
                break;
            case Strophe.Status.ERROR:
                console.log("eJabberd generic connection error!");
                break;
            case Strophe.Status.ATTACHED:
                console.log("eJabberd connection attached!");
                break;
            case Strophe.Status.REDIRECT:
                console.log("eJabberd connection redirected!");
                break;
            case Strophe.Status.CONNTIMEOUT:
                console.log("eJabberd connection timeout!");
                break;
            default:
                console.log("eJabberd: Unknow connection status");
        }
    }

The problem arises when I try to reconnect to the server after a logout.

When I logout from my web app (i.e logout() from my chatpanel.service is called), if I try to login again I receive no response from the ejabber server. Apparently the server stops listening to any further request from strophe.
I've tried both with websockets and BOSH. The final result is the same.

Here is a snapshot of the browsers console:

First, successfull, login (WebSocket)
[Log] Connecting to eJabberd... (main.js, line 8666)
[Log] 1: Websocket open (main.js, line 8626)
[Log] 1: _connect_cb was called (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 1: SASL authentication succeeded. (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626, x4)
[Log] eJabberd connected! (main.js, line 8687)
[Log] 1: _dataRecv called (main.js, line 8626) 

Logout...
[Log] Disconnecting from eJabberd... (main.js, line 8672)
[Log] 1: Disconnect was called because: logout (main.js, line 8626)
[Log] 1: _doDisconnect was called (main.js, line 8626)
[Log] 1: WebSockets _doDisconnect was called (main.js, line 8626)
[Log] Disconnected from eJabberd (main.js, line 8675)
[Log] 1: Websocket closed (main.js, line 8626)

Second attempt (fails, WebSocket)
[Log] Connecting to eJabberd... (main.js, line 8666)
[Log] 1: Websocket open (main.js, line 8626)
[Log] 1: _connect_cb was called (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] eJabberd authentication failed! (main.js, line 8693)
[Log] 3: WebSocket stream error: connection-timeout - Idle connection (main.js, line 8626)
[Log] eJabberd generic connection error! (main.js, line 8696)
[Log] 1: _doDisconnect was called (main.js, line 8626)
[Log] 1: WebSockets _doDisconnect was called (main.js, line 8626)
[Log] Disconnected from eJabberd (main.js, line 8675)
[Log] 1: Websocket closed (main.js, line 8626)


Successful first login (BOSH)
[Log] Connecting to eJabberd... (main.js, line 8666)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 1.0 posting (main.js, line 8626)
[Log] 0: request id 1.0 state changed to 1 (main.js, line 8626)
[Log] 0: request id 1.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 1.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 1.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 1 should now be removed (main.js, line 8626)
[Log] 0: request id 1.1 got 200 (main.js, line 8626)
[Log] 1: _connect_cb was called (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 2.0 posting (main.js, line 8626)
[Log] 0: request id 2.0 state changed to 1 (main.js, line 8626)
[Log] 0: request id 2.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 2.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 2.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 2 should now be removed (main.js, line 8626)
[Log] 0: request id 2.1 got 200 (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 1: SASL authentication succeeded. (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 3.0 posting (main.js, line 8626)
[Log] 0: request id 3.0 state changed to 1 (main.js, line 8626)
[Log] 0: request id 3.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 3.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 3.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 3 should now be removed (main.js, line 8626)
[Log] 0: request id 3.1 got 200 (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 4.0 posting (main.js, line 8626)
[Log] 0: request id 4.0 state changed to 1 (main.js, line 8626)
[Log] 0: request id 4.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 4.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 4.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 4 should now be removed (main.js, line 8626)
[Log] 0: request id 4.1 got 200 (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 5.0 posting (main.js, line 8626)
[Log] 0: request id 5.0 state changed to 1 (main.js, line 8626)
[Log] 0: request id 5.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 5.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 5.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 5 should now be removed (main.js, line 8626)
[Log] 0: request id 5.1 got 200 (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] eJabberd connected! (main.js, line 8687)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 6.0 posting (main.js, line 8626)
[Log] 0: request id 6.0 state changed to 1 (main.js, line 8626)
[Log] 0: request id 6.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 6.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 6.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 6 should now be removed (main.js, line 8626)
[Log] 0: request id 6.1 got 200 (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 1: no requests during idle cycle, sending blank request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 7.0 posting (main.js, line 8626)
[Log] 0: request id 7.0 state changed to 1 (main.js, line 8626)

Logout...
[Log] Disconnecting from eJabberd... (main.js, line 8672)
[Log] 1: Disconnect was called because: logout (main.js, line 8626)
[Log] 1: _sendTerminate was called (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 2 requests (main.js, line 8626)
[Log] 0: _processRequest: first request has readyState of 1 (main.js, line 8626)
[Log] 0: request id 17.0 posting (main.js, line 8626)
[Log] 0: request id 17.0 state changed to 1 (main.js, line 8626)
[Log] 0: request id 17.0 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: _processRequest: first request has readyState of 1 (main.js, line 8626)
[Log] 0: request id 17 should now be removed (main.js, line 8626)
[Log] 0: request id 17.0 got 200 (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 0: request id 16.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 16.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 16.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 16 should now be removed (main.js, line 8626)
[Log] 0: request id 16.1 got 200 (main.js, line 8626)
[Log] 1: _dataRecv called (main.js, line 8626)
[Log] 1: _doDisconnect was called (main.js, line 8626)
[Log] Disconnected from eJabberd (main.js, line 8675)

Second attempt (after logout, unsuccessful, BOSH)
[Log] Connecting to eJabberd... (main.js, line 8666)
[Log] 0: _throttledRequestHandler called with 1 requests (main.js, line 8626)
[Log] 0: request id 18.0 posting (main.js, line 8626)
[Log] 0: request id 18.0 state changed to 1 (main.js, line 8626)
[Error] Failed to load resource: the server responded with a status of 404 (Not Found) (b7e98bdb6711295ac7cd2c2d7a139415, line 0)
[Log] 0: request id 18.1 state changed to 2 (main.js, line 8626)
[Log] 0: request id 18.1 state changed to 3 (main.js, line 8626)
[Log] 0: request id 18.1 state changed to 4 (main.js, line 8626)
[Log] 0: removing request (main.js, line 8626)
[Log] 0: _throttledRequestHandler called with 0 requests (main.js, line 8626)
[Log] 0: request id 18 should now be removed (main.js, line 8626)
[Log] 0: request id 18.1 got 200 (main.js, line 8626)
[Log] 1: _connect_cb was called (main.js, line 8626)
[Log] 3: Server did not offer a supported authentication mechanism (main.js, line 8626)
[Log] eJabberd connection failed! (main.js, line 8669)
[Log] 1: _doDisconnect was called (main.js, line 8626)
[Log] Disconnected from eJabberd (main.js, line 8675) 

As you can see, with Bosh, the debugger is telling me that the server didn't offer any suitable authentication method.
Apparently the server is not listening anymore to the incoming connection requests. But, oddly enough, if I restart the server, the reconnection succeeds. It looks like strophe.js is still keeping something open even after the disconnection process is completed, and the only way to close it is to stop the server.

I'm not sure it's a strophe.js issue or an ejabberd config problem, but I'm more inclined to think of a client issue (or something I'm missing in my code) since external clients can connect flawlessly to the server.

Any hint will be greatly appreciated.

1

1 Answers

1
votes

Got it.
I post here, as an answer for those, facing a similar problem.
The problem was not Strophe.js but the configuration of the jabber server. The problem was caching.

From eJabber docs (here)

auth_use_cache: false|true: Starting in ejabberd 17.06, caching has received a complete overhaul. Instead of extauth_cache, a set of new variables describes cache behaviour, and the default value is now true. Note that caching interferes with the ability to maintain multiple passwords per account. So if your authentication mechanism supports application-specific passwords, caching must be disabled.

eJabberd is configured to use caching by default for everything where it is applicable.
Since, when I logout from my web app, I invalidate the old JSON web token used for authentication, ejabber is checking the new credentials against the cached value, thus finding a mismatch.
The solution is to disable caching like this (in ejabberd.yml) auth_use_cache: false