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

offer in-band registration #783

Open
sonnyp opened this issue Oct 4, 2019 · 3 comments
Open

offer in-band registration #783

sonnyp opened this issue Oct 4, 2019 · 3 comments

Comments

@sonnyp
Copy link
Member

sonnyp commented Oct 4, 2019

Goals:
Simple API to allow user registration and documentation "How do I - user registration".

Non goals:
Implement registration within xmpp.js.

References:

@michelepapale1996
Copy link

Hi! I would like to know if it is possible to register users through in-band registration with this lib.

I've tried to use this lib by starting a connection anonymously:

const xmpp = client({service: "ws://--.--.--.--:7070/ws"});

debug(xmpp, true);
xmpp.on("online", async (address) => {
     await xmpp.send(xml("presence"));
    console.log("Online!");
});

xmpp.on("stanza", async (stanza) => {
    if (stanza.is("message")) {
        console.log("NEW MESSAGE:", stanza)
    }
    if (stanza.is("iq") && stanza.attrs.id === 'reg1' ){
        xmpp.send(
            xml(
                'iq',
                { type: 'set' , id: "reg2", xmlns:"jabber:client"},
                xml( 'query', 'jabber:iq:register',
                    xml(
                        'x',
                        {type: 'submit', xmlns: 'jabber:x:data'},
                        xml(
                            'field',
                            {var: "FORM_TYPE"},
                            xml('value', {}, "jabber:iq:register")
                        ),
                        xml(
                            'field',
                            {var: "username"},
                            xml('value', {}, "user")
                        ),
                        xml(
                            'field',
                            {var: "name"},
                            xml('value', {}, "")
                        ),
                        xml(
                            'field',
                            {var: "email"},
                            xml('value', {}, "")
                        ),
                        xml(
                            'field',
                            {var: "password"},
                            xml('value', {}, "prova")
                        )
                    )
                )
            )
        );
    }
    if (stanza.is("iq") && stanza.attrs.id === 'reg2' ){
        console.log(stanza.toString())
    }
});

xmpp.send(xml('iq', { type: 'get' , id: "reg1"}, xml('query', 'jabber:iq:register')));

This is the debug code:

//connection handshake 
Online!
OUT
<iq type="get" id="reg1" xmlns="jabber:client"><query xmlns="jabber:iq:register"/></iq>
IN
<iq xmlns="jabber:client" type="result" id="reg1" to="6n35dxy7bn@--.--.--.--/6n35dxy7bn"><query xmlns="jabber:iq:register"><username/><password/><email/><name/><x xmlns="jabber:x:data" type="form"><title>XMPP Client Registration</title><instructions>Please provide the following information</instructions><field var="FORM_TYPE" type="hidden"><value>jabber:iq:register</value></field><field var="username" type="text-single" label="Username"><required/></field><field var="name" type="text-single" label="Full name"/><field var="email" type="text-single" label="Email"/><field var="password" type="text-private" label="Password"><required/></field></x></query></iq>
OUT
<iq type="set" id="reg2" xmlns="jabber:client"><query xmlns="jabber:iq:register"><x type="submit" xmlns="jabber:x:data"><field var="FORM_TYPE"><value>jabber:iq:register</value></field><field var="username"><value>user</value></field><field var="name"><value></value></field><field var="email"><value></value></field><field var="password"><value>prova</value></field></x></query></iq>
IN
<iq xmlns="jabber:client" type="error" id="reg2" to="6n35dxy7bn@--.--.--.--/6n35dxy7bn"><query xmlns="jabber:iq:register"><x xmlns="jabber:x:data" type="submit"><field var="FORM_TYPE"><value>jabber:iq:register</value></field><field var="username"><value>user</value></field><field var="name"><value/></field><field var="email"><value/></field><field var="password"><value>prova</value></field></x></query><error code="400" type="modify"><bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/></error></iq>

As you can see, I receive a bad-request error, but I do not understand why since the xml is right. Can you help me?

@sonnyp sonnyp mentioned this issue Aug 28, 2020
@gencatz
Copy link

gencatz commented Oct 14, 2020

Does the registration work now or not? Could you please provide an example? Thanks!

@badrihippo
Copy link

Hmm this is not straightforward because by default @xmpp/client always tries to authenticate as some user, or as anonymous if no username is provided. Here we have to open a stream but make the "create account" request before any authentication happens.

Here's a very rough but working example I came up with (you could replace websocket with whatever else you want, I think):

const { client, xml, jid } = require('@xmpp/client')
const debug = require("@xmpp/debug")
let xmpp = client({
    service:"wss://jabber.example:443/xmpp-websocket",
    domain:"jabber.example",
    credentials: authenticate,
})

debug(xmpp, true)
const {iqCaller} = xmpp

async function authenticate(auth, mechanism) {
    console.debug("authenticate", mechanism)
    console.debug("...or not, because we'll sign up here instead hehe")

    // get signup form
    let response1 = await iqCaller.get(
      xml("query", {xmlns: "jabber:iq:register"})
    )

    console.log(`They responded with: ${response1}`)

    /*
     * in practice, at this point you should parse the signup form
     * and see which fields are required, etc. For example, my
     * metronome setup requires you to provide the email address
     * as well.
     */

    // try an actual signup!
    let response2 = await iqCaller.set(
        xml("query", {xmlns: "jabber:iq:register"},
            xml("x",
              {type: "submit", xmlns: "jabber:x:data"},
              xml("field", {type:"hidden", var: "FORM_TYPE"},
                xml("value", {}, "jabber:iq:register")),
              xml("field", {label:"Username", type: "text-single", var: "username"},
                xml("required"),
                xml("value", {}, "somebody")),
              xml("field", {label:"Password", type: "text-private", var: "password"},
                xml("required"),
                xml("value", {}, "s3kr1t")),
            )
        )
    )

    console.log(`They responded with: ${response2}`)

    xmpp.stop()
    process.exit()
}

The problem with this is that xmpp.js expects authentication to happen at this point, so it fails with a timeout after a point. In fact, to implement this I had to override the authentication function so that it sent custom stanzas to create an account rather than actually authenticating.

To prevent a timeout I had to call xmpp.stop(), but then the function very efficiently tries to reconnect again—and then fails the second time around since an account would have already been created by that point. Hence the process.exit() to make sure this doesn't happen. Obviously this is not a very elegant way of doing it.

@sonnyp, not sure if I'm missing something in the documentation, but is there a recommended way to do any of the following?

  1. skip the authentication step and start sending IQs and stuff directly. (Don't try to sign in as anyone, basically)
  2. disable reconnection, so it doesn't try to connect again when the stream is closed. (Alternatively, set a maximum number for retry attempts, which can be set to 0).

I think just one of the above two options would work; of course having both would make it that much less hacky 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants