Invite tokens accepted and ROT13 transformed

TLDR: Anti-virus software / Email client URL checking-bot is likely to blame.

We’ve experienced strange behavior with three of our sites, that results in the user not being able to use the invite link we sent them—and in an account being created without the user’s knowledge. Adding to the confusion the account that’s created is a ROT13 transformation of the user’s name, email, and username.

I’ve got a hunch as to why this is, but haven’t been able to confirm it yet. I’m posting it here for anyone else who might be experiencing the same issue.

Here’s the detailed scoop:
We’re on hosted Discourse installs. We’re using the discourse-invite-tokens plugin and a custom script to generate the invite links. The script makes a HTTP request to the "{}/invites/link API endpoint to get the invite token by passing the user’s email address and group_name, and then returns the invite token. The invite token is combined with our site url and the user’s name, username, and email address.

A sample invite link looks like this:

It’s a one-time use invite link. When a user clicks on it an account is created for them and they’re logged in to Discourse for the first time (we have a private Discourse that you can only see if you log in). This triggers Discourse to send the Account Created email.

However, with the users in question, when they click on the link they’re taken to the login page. That’s the first strange thing because normally when you try clicking a link that’s already been used you get a plain text JSON response in the browser that says:

{"success":false,"message":"Your invite token is invalid. Please contact the site's administrator."}

But the users who have had the issue uniformly report that clicking the link redirects them to the main url of our site (and since they’re not logged in) which shows them the login screen.

From the admin side, we notice that a new user has been created with a gibberish name (points to the Discourse team for helping us realize that the gibberish was actually a ROT13 translation of the the user’s email). is a quick resource for checking ROT13 text.

In this example instead of a user named loislane we found a user with the username ybvfynar. The ROT13 transformation affects the user’s email, username, and real name fields (but not the group we assigned them to because the group name is baked into the invite token not displayed as a query string parameter).

One of the clues that finally lead us down the right track was that the query string parameter’s values were mangled while the parameter’s keys weren’t garbled.

Which lead to this: email - Mangled URL Parameters in IE9 - Stack Overflow

Which lead me to a new search on meta which lead to this:

We don’t really have a way around this yet, so we’re just recreating the invites for people who experience the problem. It’s not a terrible pain because it’s such a low percentage 0.5% of users affected, but it was a pattern we couldn’t explain until now (and we’re still guessing). If our users are all able to identify the same anti-virus or email client watchdog we might be able to do something proactive going forward. If I find out more I’ll update this post with details.

If you’re in the same situation, I hope this helps.

Thanks to @simon, @techAPJ, @codinghorror, and the rest of the Discourse team for helping us deal with the issue.

UPDATE: What’s the cause?

  • One of the three users has this stack: IBM Note Mail Client, Symantech, and both Firefox/Chrome.

Just add a parameter called ‘ok’ with value ‘ok’ to all requests and check if it’s still ‘ok’ when it’s received.

Interesting solution…

But what if the ROT13 translation is happening in a way that’s not visible to the users?

That’s what we believe is happening. Because when they forward the email to us the link looks correct. When they look at it, the link looks correct.

Our assumption is that the anti-virus / security bot is checking each link in an email, but not changing the links for the user.

Here’s a case for what we think is happening:

A user gets an email with links in it. One of those links is an unsubscribe link, the kind you see at the bottom of a lot of email newsletters. The security bot’s job is to make sure none of the links go to malware or phishing sites so it needs to follow the links to the domains they link to (and any domains it’s redirected to). But the security bot needs to do all of this without actually unsubscribing the user because the user didn’t click the usubscribe link, it was the bot that checked the link. So the bot ROT13 transforms all the query parameter values (like the email address) and then follows the links. If it lands on a malware site it blocks the email. If it lands on legitimate sites it allows the email to go through to the user including all of the un-transformed links so that the user can decide which links to click on.

But in our case the link is one-time use so when the security bot checks it the link it creates the ROT13 account and when it passes the link on to the user the link is all used up.

Our user will see the right email in the params and would see “ok” in the params, but since the link was one use only it’s already used up.

1 Like

It’s not about the users looking at the link - my solution is that the system will not use up the link if the ‘ok’ parameter is bad, but ignore it.


Ahh… gotcha.

In this case “the system” is a Discourse hosted install. We don’t have access to what the server is doing when the links are used.

@simon is something like what @michaeld described possible?


Hmm @techAPJ a GET request should never redeem an invite, we should require one more click here, is that not the case?

Then the easy fix is just say not valid if the email address has no @ in it, plus it would not be redeemed anyway


Yes, that is not the case for invite tokens (requires plugin: discourse-invite-tokens).

I agree we should require at least an additional click, if possible a full-blown accept invite page like we have for regular invites with the ability to choose username and optionally provide name and password.


Added above tasks on my list. :memo:


Two things:

  1. a ROT13 email address still has an @ in it, so in our case that wouldn’t be a useful lookup

  2. we’re specifically using the invite plugin BECAUSE it doesn’t have an invite page. The ability to take our paid users directly into Discourse with a username of our choosing (their first and last name combined). The benefits we get far outweigh the inconvenience we’re experiencing with this issue. Please, please don’t add an invite page to the invite token plugin.

Adding an additional click to the token sounds like an elegant solution to our issue.


I think a minimal “extra click” here is a good minimal hurdle (can be a minimal page with … click here to accept invite), especially since some email clients like pre-fetching links and so on. Creating resources on GET is a big no-no.


Great. We’re totally on-board for a minimal page.

We just don’t want to introduce any cognitive load to the process on this intermediary page so a “I accept the invite” button is vastly preferable to a form they need to fill out.

Thank you!


Added a simple confirmation button via:

Also, added a check to verify that the email provided is valid.


3 posts were split to a new topic: Unable to customize the “Accept Invite” button text

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.