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

[core] Make AccountManager.getRegistrationInfo() public #618

Merged
merged 1 commit into from
Sep 14, 2024

Conversation

stokito
Copy link
Contributor

@stokito stokito commented Aug 27, 2024

The getRegistrationInfo() returns a registration form that may also contain a CAPTCHA. We need to get the full Registration object to get the fields. Also it should be possible to call it multiple times to update the form.

This is related to igniterealtime/Spark#877

The getRegistrationInfo() returns a registration form that may also contain a CAPTCHA.
We need to get the full Registration object to get the fields.
Also it should be possible to call it multiple times to update the form.

Signed-off-by: Sergey Ponomarev <[email protected]>
@stokito
Copy link
Contributor Author

stokito commented Aug 27, 2024

Also we'll need to process more fields from response. Here is a real world example:

<!-- request for registration form -->
<iq xmlns="jabber:client" to="conversations.im" type="get" id="65081b4b-8131-4b17-afdd-b317f9560348">
  <query xmlns="jabber:iq:register" />
</iq>

<!-- response with form -->
<iq xmlns="jabber:client" xml:lang="en" from="conversations.im" type="result" id="65081b4b-8131-4b17-afdd-b317f9560348">
  <query xmlns="jabber:iq:register">
    <x type="form" xmlns="jabber:x:data">
      <instructions>Choose a username and password to register with this server</instructions>
      <field var="FORM_TYPE" type="hidden">
        <value>jabber:iq:register</value>
</field>
      <field var="username" type="text-single" label="User">
        <required />
</field>
      <field var="password" type="text-private" label="Password">
        <required />
</field>
      <field var="captcha-fallback-text" type="fixed">
        <value>If you don't see the CAPTCHA image here, visit the web page.</value>
</field>
      <field var="captcha-fallback-url" type="text-single" label="CAPTCHA web page">
        <value>https://xmpp.conversations.im/captcha/7183915110689270672/image</value>
</field>
      <field var="from" type="hidden" label="Attribute 'to' of stanza that triggered challenge">
        <value>conversations.im</value>
</field>
      <field var="challenge" type="hidden" label="Challenge ID">
        <required />
        <value>7183915110689270672</value>
</field>
      <field var="sid" type="hidden" label="Stanza ID">
        <value>65081b4b-8131-4b17-afdd-b317f9560348</value>
</field>
      <field var="ocr" type="text-single" label="Enter the text you see">
        <media xmlns="urn:xmpp:media-element">
          <uri type="image/png">cid:[email protected]</uri>
</media>
        <required />
</field>
</x>
    <data type="image/png" max-age="0" cid="[email protected]" xmlns="urn:xmpp:bob">BASE64_OF_PNG_HERE</data>
    <instructions>You need a client that supports x:data and CAPTCHA to register</instructions>
</query>
</iq>

So we should process the last <data> element that contains a CAPTCHA content.

Copy link
Member

@Flowdalic Flowdalic left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any issues with having a public getRegistrationInfo() method. However, the method should per default use the cached value (if any). So something like this should do the trick:

    public synchronized Registration getRegistrationInfo() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
        return getRegistrationInfo(true);
    }

    public synchronized Registration getRegistrationInfo(boolean useCached) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
        if (useCached && info != null) {
            return info;
        }

        Registration reg = new Registration();
        reg.setTo(connection().getXMPPServiceDomain());
        info = createStanzaCollectorAndSend(reg).nextResultOrThrow();
        return info;
    }

@stokito
Copy link
Contributor Author

stokito commented Aug 27, 2024

I intentionally kept it to always fetch info but store locally. See, unlike simple registrations here we can't keep a state with a form internally.

If a user can't see a CAPTCHA then it will need to press a button "Start registration" again and receive a new info with the new image.
In the same time the getInstructions() and createAccount() will use the newer version of form stored.

So the full flow will be:

  • Call the getRegistrationInfo() and store the form to a variable. Internally the form will be also stored to a the private info field for a cache.
  • Call supportsAccountCreation() that internally will check the cached info. The method can be moved into the Registration itself.
  • If the info contains a form (captcha, email etc.) then render it. We should also get instructions from the from instead of those from info.
  • Call createAccount() and pass values from the form as attributes.

Check my pr to the Spark to see it in action. igniterealtime/Spark@9d8876b#diff-35302a6fb74fb0dde4631403f2d85abd379231a22283c69ce058b12de8c69abfR240

You may try the conversations.im server to see a form with a captcha.

@Flowdalic Flowdalic added this pull request to the merge queue Sep 14, 2024
Merged via the queue into igniterealtime:master with commit af77e56 Sep 14, 2024
2 checks passed
@stokito stokito deleted the account_registration branch September 14, 2024 11:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants