Skip to content

chore(docs): add tutorial for avoiding corporate email scanning HEAD reqs#3900

Merged
ndom91 merged 5 commits into
mainfrom
ndom91/tutorial-corporate-email-scanning-head-req
Mar 22, 2022
Merged

chore(docs): add tutorial for avoiding corporate email scanning HEAD reqs#3900
ndom91 merged 5 commits into
mainfrom
ndom91/tutorial-corporate-email-scanning-head-req

Conversation

@ndom91

@ndom91 ndom91 commented Feb 9, 2022

Copy link
Copy Markdown
Contributor

Reasoning 💡

Saw someone else had this problem, and the "tutorial" / fix was relatively quick, so I decided to just write it up for the future.

See:

  1. NextAuth not working behind corporate proxy #2509
  2. Signing in using magic link with Outlook email when SafeLinks enabled does not work #1840

EDIT: There may need to be some extra checks before just returning a 200 all willy-nilly haha. What do yuo think Balazs?

Checklist 🧢

  • Documentation
  • Tests
  • Ready to be merged

Affected issues 🎟

@ndom91 ndom91 requested a review from balazsorban44 February 9, 2022 21:44
@vercel

vercel Bot commented Feb 9, 2022

Copy link
Copy Markdown

This pull request is being automatically deployed with Vercel (learn more).
To see the status of your deployment, click below or on the icon next to each commit.

🔍 Inspect: https://vercel.com/nextauthjs/next-auth/4GoiB2CZaCsXhfZxurQEdymXeuCj
✅ Preview: Failed

[Deployment for 3a678fe failed]

@balazsorban44 balazsorban44 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The write-up was very good, I actually am very clear about this problem now!

Although I'm not sure in the case of HEAD how this would be an issue, as we only run NextAuth.js logic when the request is POST or GET.

body: `Error: Action ${action} with HTTP ${method} is not supported by NextAuth.js` as any,

I have to assume that some of those IT checks actually send a GET request instead, which could invalidate the token indeed.

I would rather add some kind of user-agent detection to the tutorial. Do we know SafeLink or any of those if they send some detectable info?

@ndom91

ndom91 commented Feb 10, 2022

Copy link
Copy Markdown
Contributor Author

The write-up was very good, I actually am very clear about this problem now!

Although I'm not sure in the case of HEAD how this would be an issue, as we only run NextAuth.js logic when the request is POST or GET.

body: `Error: Action ${action} with HTTP ${method} is not supported by NextAuth.js` as any,

I have to assume that some of those IT checks actually send a GET request instead, which could invalidate the token indeed.

I would rather add some kind of user-agent detection to the tutorial. Do we know SafeLink or any of those if they send some detectable info?

Yeah great idea. I think we can be pretty sure that at least office 365 / safelink use HEAD requests. Others I'm not sure.

What I can definitely picture is that they have a distinguishable user-agent or something like that for their bot. I just don't know how we'd get it 🤔

@ndom91

ndom91 commented Feb 12, 2022

Copy link
Copy Markdown
Contributor Author

Did some more research here, and it looks like opencollective were also not able to find any other distinguishing factors, other than that the bots user-agents showed they were using "old" versions of chrome (See: https://github.com/opencollective/opencollective-frontend/blob/2852c113685ffc8273419839c6012f11a7751fbb/lib/robots-detector.js).

But you're right, it does seem to be GET requests in most cases, not HEAD.

EDIT: Although there is a bunch of stuff we do in the handler before even checking if its POST/GET only.. Maybe it snuck by us and is happening somewhere in these lines before this check and that guy in #1840 (#1841) was on to something? Idk

const { options: userOptions, req } = params
setLogger(userOptions.logger, userOptions.debug)
const assertionResult = assertConfig(params)
if (typeof assertionResult === "string") {
logger.warn(assertionResult)
} else if (assertionResult instanceof Error) {
// Bail out early if there's an error in the user config
const { pages, theme } = userOptions
logger.error(assertionResult.code, assertionResult)
if (pages?.error) {
return {
redirect: `${pages.error}?error=Configuration`,
}
}
const render = renderPage({ theme })
return render.error({ error: "configuration" })
}
const { action, providerId, error, method = "GET" } = req
const { options, cookies } = await init({
userOptions,
action,
providerId,
host: req.host,
callbackUrl: req.body?.callbackUrl ?? req.query?.callbackUrl,
csrfToken: req.body?.csrfToken,
cookies: req.cookies,
isPost: method === "POST",
})
const sessionStore = new SessionStore(
options.cookies.sessionToken,
req,
options.logger
)

EDIT 2: Nevermind, its really just asserting the config and init-ing the options, huh 🤔

EDIT 3: Opencollectives solution (see link above) was to present the user with a "js challenge" of sorts, requiring some sort of interaction (i.e. mousemove, touchstart, etc.) before continuing on and validating the email auth link. Thats kind of neat, doesn't require much code and would set us apart a bit I feel like. This just really does seem to be a niche issue and doesn't come up much :/

EDIT 4: Ghost, the blogging platform, seems to have worked around this by allowing tokens to be reused multiple times within a 10s window, i.e. if the user clicks on the link the outlook SafeLink bot will detonate it first, but then their page should load (i.e. the second visit) within 10s allowing them to also use the token validly. See: https://github.com/TryGhost/Ghost/pull/12519/files

@balazsorban44

Copy link
Copy Markdown
Contributor

Tha is for the thorough research! Some of these are nice tricks, but reusing the one-time token doesn't sound right to me. 😅

@ndom91

ndom91 commented Feb 12, 2022

Copy link
Copy Markdown
Contributor Author

Tha is for the thorough research! Some of these are nice tricks, but reusing the one-time token doesn't sound right to me. sweat_smile

Haha yeah so its just setting a timeout on the deletion of the one-time token from the db to 10s, so if the email link with that token happens to be hit again and looked up in the DB within 10s of the first time, it'll still be there. Kinda clever actually imo

@ndom91

ndom91 commented Mar 12, 2022

Copy link
Copy Markdown
Contributor Author

I think this one can just be merged. Any of the other work-arounds / potential changes we'd have to make are a little bigger, plus theres no "best-practice" out there in the community for dealing with this kind of thing yet so it'd require some more discussion about which route to even take, etc..

So I'd say having a basic tutorial like this is a decent middle-ground for now. What do you think? @balazsorban44

@ndom91 ndom91 merged commit b88a31e into main Mar 22, 2022
@ndom91 ndom91 deleted the ndom91/tutorial-corporate-email-scanning-head-req branch March 22, 2022 22:29
@rnbrady

rnbrady commented Aug 30, 2022

Copy link
Copy Markdown

@balazsorban44 wrote:

I would rather add some kind of user-agent detection to the tutorial. Do we know SafeLink or any of those if they send some detectable info?

@ndom91 wrote:

What I can definitely picture is that they have a distinguishable user-agent or something like that for their bot. I just don't know how we'd get it 🤔

I have a user who can't log in with the email provider so I ran some debug on Vercel. As soon as we send the verification email, we get two requests from their corporate firewall or proxy:

HEAD request with from user agent Barracuda Sentinel (EE)

GET request from Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)

Thought that might be useful to you guys.

@ndom91

ndom91 commented Sep 6, 2022

Copy link
Copy Markdown
Contributor Author

@rnbrady thanks for taking a look. This soundsn exactly like the Office 365 issue that sparked this whole issue/PR initially.

Were you able to get it to work, or the issue is still active for you? If so, I'd recommend opening a fresh issue and maybe pointing to this PR / existing issue(s).

Thanks!

@rnbrady

rnbrady commented Sep 20, 2022

Copy link
Copy Markdown

@ndom91 yes got it working as follows:

const authHandler = NextAuth(authOptions);

export default function auth(req: NextApiRequest, res: NextApiResponse) {
  // Workaround for known email scanners that send GET or HEAD requests which have
  // the effect of cancelling the one time token. We have seen:

  // HEAD request with user-agent: Barracude Sentinel (EE)
  // GET request with user-agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729)
  if (
    req.method === 'HEAD' ||
    /Mozilla.+MSIE.+Windows NT.+WOW64.+Trident.+SLCC2.+NET CLR/.test(
      req.headers['user-agent'],
    ) ||
    /lua-resty-http.+ngx_lua/.test(req.headers['user-agent'])
  ) {
    return res.status(200).send('Please visit the link from a browser.');
  }

  // GET request with double URL encoded param callbackUrl=https%253A%252F%252F
  if (req.url.includes('callbackUrl=https%253A%252F%252F'))
    return res
      .status(400)
      .send('Your proxy has mangled the callbackUrl parameter in the URL.');

  return authHandler(req, res);
}

@Andrew781123

Andrew781123 commented Feb 18, 2023

Copy link
Copy Markdown

Is this solution still valid? I came across the same issue but this solution doesn't solve it. I checked the logs and realise that there is noHEAD request, instead it is aGET request that triggered the magic link.
Screenshot 2023-02-18 at 22 15 33

mnphpexpert added a commit to mnphpexpert/next-auth that referenced this pull request Sep 2, 2024
…reqs (nextauthjs#3900)

* chore(docs): add tutorial for avoiding corporate email scanning HEAD requests breaking email invitations

* fix: move to internal guides section
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.

4 participants