@remit/backend 0.0.130 → 0.0.131

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/backend",
3
- "version": "0.0.130",
3
+ "version": "0.0.131",
4
4
  "description": "Remit Mail Inspector API backend",
5
5
  "license": "MIT",
6
6
  "author": "",
@@ -251,7 +251,7 @@ describe("the Microsoft consent for the services an account syncs", () => {
251
251
 
252
252
  assert.equal(
253
253
  location,
254
- `${WEB_ORIGIN}/settings/accounts?oauthError=scope_not_granted`,
254
+ `${WEB_ORIGIN}/settings/accounts?oauthError=scope_not_granted&oauthEmail=person%40example.com&missingServices=Calendar`,
255
255
  );
256
256
  assert.deepEqual(
257
257
  await client.account.listAllByAccountConfig(deriveAccountConfigId(sub)),
@@ -236,7 +236,24 @@ export function getWebOrigin(): string {
236
236
 
237
237
  type ScopeRedemption =
238
238
  | { kind: "granted"; refreshToken: string; grantedScopes: string[] }
239
- | { kind: "refused"; reason: "exchange_failed" | "scope_not_granted" };
239
+ | { kind: "refused"; reason: "exchange_failed" }
240
+ | {
241
+ kind: "refused";
242
+ reason: "scope_not_granted";
243
+ service: AccountServiceName;
244
+ };
245
+
246
+ const scopeRefusalUrl = (
247
+ webOrigin: string,
248
+ email: string,
249
+ missingServices: readonly AccountServiceName[],
250
+ ): string => {
251
+ const url = new URL(`${webOrigin}/settings/accounts`);
252
+ url.searchParams.set("oauthError", "scope_not_granted");
253
+ url.searchParams.set("oauthEmail", email);
254
+ url.searchParams.set("missingServices", missingServices.join(","));
255
+ return url.toString();
256
+ };
240
257
 
241
258
  const redeemEachService = async (
242
259
  serviceFor: (service: AccountServiceName) => MailOAuthService,
@@ -265,8 +282,10 @@ const redeemEachService = async (
265
282
  ? ("exchange_failed" as const)
266
283
  : ("scope_not_granted" as const);
267
284
  });
268
- if (typeof redeemed === "string")
285
+ if (redeemed === "exchange_failed")
269
286
  return { kind: "refused", reason: redeemed };
287
+ if (redeemed === "scope_not_granted")
288
+ return { kind: "refused", reason: redeemed, service };
270
289
  grantedScopes.push(...redeemed.grantedScopes);
271
290
  token = redeemed.refreshToken ?? token;
272
291
  }
@@ -512,6 +531,11 @@ export const MicrosoftOAuthOperations: Record<
512
531
  services.filter((service) => service !== firstService),
513
532
  accountConfigId,
514
533
  );
534
+ if (
535
+ redemption.kind === "refused" &&
536
+ redemption.reason === "scope_not_granted"
537
+ )
538
+ return redirect(scopeRefusalUrl(webOrigin, email, [redemption.service]));
515
539
  if (redemption.kind === "refused")
516
540
  return redirect(
517
541
  `${webOrigin}/settings/accounts?oauthError=${redemption.reason}`,
@@ -535,9 +559,7 @@ export const MicrosoftOAuthOperations: Record<
535
559
  },
536
560
  "MS OAuth callback: granted scopes lack a requested service",
537
561
  );
538
- return redirect(
539
- `${webOrigin}/settings/accounts?oauthError=scope_not_granted`,
540
- );
562
+ return redirect(scopeRefusalUrl(webOrigin, email, missingServices));
541
563
  }
542
564
 
543
565
  // Ensure the account config row exists for this user