@wtfalch/auth 0.8.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/auth.js +33 -2
  2. package/package.json +1 -1
package/dist/auth.js CHANGED
@@ -135,7 +135,12 @@ export function createAuth(input) {
135
135
  };
136
136
  };
137
137
  const callback = async (request) => {
138
- const { location, cookies } = await complete(request.url, request.headers.get('cookie'));
138
+ // Behind a proxy the request can arrive as http:// or on an internal host,
139
+ // and `validCallback` would refuse it. The browser reached this app, so the
140
+ // path and query are what it asked for, on the app's own origin.
141
+ const { pathname, search } = new URL(request.url);
142
+ const callbackUrl = new URL(`${pathname}${search}`, options().appUrl).href;
143
+ const { location, cookies } = await complete(callbackUrl, request.headers.get('cookie'));
139
144
  return redirect(location, cookies);
140
145
  };
141
146
  const logout = async (request) => {
@@ -503,7 +508,7 @@ export function createAuth(input) {
503
508
  const device = await requestDeviceAuthorization(options(), scopeFor(options().organizationId));
504
509
  const cookie = await sealQrTransaction(options(), { dc: device.deviceCode, iv: device.interval }, device.expiresIn);
505
510
  return {
506
- url: device.verificationUriComplete,
511
+ url: qrVerificationUrl(options(), device.verificationUriComplete),
507
512
  userCode: device.userCode,
508
513
  expiresIn: device.expiresIn,
509
514
  interval: device.interval,
@@ -694,3 +699,29 @@ function reasonOf(error) {
694
699
  return 'denied';
695
700
  return 'exchange';
696
701
  }
702
+ /**
703
+ * The issuer always builds `verification_uri_complete` on itself
704
+ * (`<issuer>/device?user_code=...`). A namespace with its own login host
705
+ * keeps that namespace's users, sessions and passkeys there, so the phone
706
+ * must land on the login host instead -- but only by rewriting a URL that is
707
+ * exactly what the issuer was expected to return; anything else is refused
708
+ * rather than sent somewhere unexpected.
709
+ */
710
+ function qrVerificationUrl(options, verificationUriComplete) {
711
+ const loginOrigin = options.namespace?.loginOrigin;
712
+ const issuerOrigin = new URL(options.issuer).origin;
713
+ if (!loginOrigin || loginOrigin === issuerOrigin)
714
+ return verificationUriComplete;
715
+ let url;
716
+ try {
717
+ url = new URL(verificationUriComplete);
718
+ }
719
+ catch {
720
+ throw new AuthError('request', 'the issuer returned an unusable device verification URL');
721
+ }
722
+ if (url.origin !== issuerOrigin || url.pathname !== '/device')
723
+ throw new AuthError('request', 'the issuer returned an unexpected device verification URL');
724
+ const rewritten = new URL(`${loginOrigin}/device`);
725
+ rewritten.search = url.search;
726
+ return rewritten.href;
727
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/auth",
3
- "version": "0.8.0",
3
+ "version": "0.9.1",
4
4
  "description": "Sign in against auth.wtfalch.dev: a server session for a Next.js app, and a browser client with silent single sign-on across subdomains.",
5
5
  "repository": {
6
6
  "type": "git",