@farm.js/supabase 0.1.0-beta.96 → 0.1.0-beta.98

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/dist/index.d.ts CHANGED
@@ -35,6 +35,14 @@ export interface SupabaseIntegrationInput {
35
35
  providers?: string[];
36
36
  defaultProvider?: string;
37
37
  pages?: SupabaseIntegrationPages;
38
+ /**
39
+ * Additional origins allowed to submit the sign-in, sign-up, and sign-out
40
+ * routes, using the same pattern syntax as `serverActions.allowedOrigins`
41
+ * (`https://app.example.com`, `example.com`, `*.example.com`). The app's own
42
+ * origin is always trusted; set this only when a different trusted origin
43
+ * posts these forms.
44
+ */
45
+ allowedOrigins?: string[];
38
46
  log?: FarmIntegrationLogger;
39
47
  }
40
48
  interface ResolvedSupabaseEnv {
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAsB,MAAM,eAAe,CAAC;AACvE,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAC;AAiCvB,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9E,MAAM,MAAM,gCAAgC,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;AAExF,MAAM,WAAW,kCAAkC;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,kBAAkB,CAAC;IACxC,OAAO,EAAE,gCAAgC,CAAC;CAC3C;AAED;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,CACxC,OAAO,EAAE,kCAAkC,KACxC,yBAAyB,CAAC;AAE/B,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,2BAA2B,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC,GAAG,CAAC,EAAE,qBAAqB,CAAC;CAC7B;AAED,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,qBAAqB;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwuBD,wBAAgB,QAAQ,CAAC,KAAK,GAAE,wBAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAuE7B,OAAO,WAAW,6BAA6B;;;;0BAkbzC,OAAO,WAAW,6BAA6B;;GAkBpF"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAsB,MAAM,eAAe,CAAC;AACvE,OAAO,EAEL,KAAK,6BAA6B,EAClC,KAAK,qBAAqB,EAC3B,MAAM,eAAe,CAAC;AAsCvB,MAAM,WAAW,wBAAwB;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,yBAAyB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAC9E,MAAM,MAAM,gCAAgC,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;AAExF,MAAM,WAAW,kCAAkC;IACjD,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,OAAO,kBAAkB,CAAC;IACxC,OAAO,EAAE,gCAAgC,CAAC;CAC3C;AAED;;;;;GAKG;AACH,MAAM,MAAM,2BAA2B,GAAG,CACxC,OAAO,EAAE,kCAAkC,KACxC,yBAAyB,CAAC;AAE/B,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,2BAA2B,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,GAAG,CAAC,EAAE,qBAAqB,CAAC;CAC7B;AAED,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,UAAU,qBAAqB;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwuBD,wBAAgB,QAAQ,CAAC,KAAK,GAAE,wBAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAuG7B,OAAO,WAAW,6BAA6B;;;;0BA2czC,OAAO,WAAW,6BAA6B;;GAiCpF"}
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createServerClient } from "@supabase/ssr";
2
2
  import { defineIntegration, } from "@farm.js/core";
3
+ import { describeIntegrationOriginRejection, resolveIntegrationAllowedOrigins, validateIntegrationRequestOrigin, } from "@farm.js/core/integrations";
3
4
  import { api as clientApi } from "@farm.js/core/client";
4
5
  import { createPathInferredClientApi, createDocumentNavigationMatchers, escapeHtml, getOrigin, getReturnTo, integrationConfig, normalizeMatchers, parseCookieHeaderList, resolveAppPath, resolveCallbackSettings, toAbsoluteUrl, withSearchParams, } from "@farm.js/integration-utils";
5
6
  import { supabaseAuthFormFields, } from "./client.js";
@@ -657,12 +658,33 @@ export function supabase(input = {}) {
657
658
  label: "Supabase integration",
658
659
  }, env.appBaseUrl);
659
660
  const callbackPath = callbackSettings.callbackPath;
661
+ const allowedOrigins = resolveIntegrationAllowedOrigins(input.allowedOrigins, "supabase.allowedOrigins");
660
662
  const api = createSupabaseApi({
661
663
  loginPath,
662
664
  signupPath,
663
665
  logoutPath,
664
666
  sessionPath,
665
667
  });
668
+ /**
669
+ * Reject a credential or sign-out request that did not come from this app.
670
+ * Returns the response to send, or null when the request may proceed.
671
+ */
672
+ function rejectForeignOrigin(request, clientRequest, { requireOriginMetadata }) {
673
+ const result = validateIntegrationRequestOrigin(request, {
674
+ allowedOrigins,
675
+ requireOriginMetadata,
676
+ });
677
+ if (result.ok) {
678
+ return null;
679
+ }
680
+ const message = describeIntegrationOriginRejection(result.reason);
681
+ return clientRequest
682
+ ? jsonError(message, 403)
683
+ : new Response(message, {
684
+ status: 403,
685
+ headers: { "content-type": "text/plain; charset=utf-8" },
686
+ });
687
+ }
666
688
  return defineIntegration({
667
689
  category: "auth",
668
690
  type: "supabase",
@@ -706,6 +728,12 @@ export function supabase(input = {}) {
706
728
  const provider = requestUrl.searchParams.get("provider") || input.defaultProvider;
707
729
  const returnTo = getReturnTo(requestUrl.searchParams.get("returnTo"), "/dashboard");
708
730
  if (request.method === "POST") {
731
+ const rejected = rejectForeignOrigin(request, clientRequest, {
732
+ requireOriginMetadata: true,
733
+ });
734
+ if (rejected) {
735
+ return rejected;
736
+ }
709
737
  const parsedRequest = await parseEmailPasswordRequest(request);
710
738
  if (!parsedRequest.ok) {
711
739
  if (clientRequest) {
@@ -856,6 +884,12 @@ export function supabase(input = {}) {
856
884
  feedback: readPageFeedback(context),
857
885
  });
858
886
  }
887
+ const rejected = rejectForeignOrigin(request, clientRequest, {
888
+ requireOriginMetadata: true,
889
+ });
890
+ if (rejected) {
891
+ return rejected;
892
+ }
859
893
  const parsedRequest = await parseEmailPasswordRequest(context.request);
860
894
  if (!parsedRequest.ok) {
861
895
  if (clientRequest) {
@@ -985,6 +1019,16 @@ export function supabase(input = {}) {
985
1019
  const requestUrl = context.url;
986
1020
  let returnTo = getReturnTo(requestUrl.searchParams.get("returnTo"), "/");
987
1021
  const clientRequest = isIntegrationClientRequest(request);
1022
+ // A POST carries an Origin in every supported browser, so it is held
1023
+ // to the strict check. A GET sign-out is also reachable as a plain
1024
+ // link or bookmark, where no origin metadata exists at all; those
1025
+ // stay allowed and only an explicitly cross-site GET is rejected.
1026
+ const rejected = rejectForeignOrigin(request, clientRequest, {
1027
+ requireOriginMetadata: request.method === "POST",
1028
+ });
1029
+ if (rejected) {
1030
+ return rejected;
1031
+ }
988
1032
  if (request.method === "POST") {
989
1033
  const contentType = context.request.headers.get("content-type") || "";
990
1034
  if (contentType.includes("application/json")) {
@@ -1046,14 +1090,26 @@ export function supabase(input = {}) {
1046
1090
  {
1047
1091
  matcher: protectedMatchers,
1048
1092
  async handler(_request, context) {
1049
- const { supabase } = createSupabaseHandler(context, env, input.instance);
1050
- const { data: { session }, } = await supabase.auth.getSession();
1051
- if (session) {
1093
+ const { supabase, setCookies } = createSupabaseHandler(context, env, input.instance);
1094
+ // getSession() reads the cookie without verifying the token,
1095
+ // so a forged cookie would pass this gate. getUser() checks
1096
+ // the token against Supabase Auth before the route is served.
1097
+ const { data, error } = await supabase.auth.getUser();
1098
+ if (!error && data?.user) {
1052
1099
  return;
1053
1100
  }
1054
- return redirectToPage(signInViewPath, context, env.appBaseUrl, {
1101
+ const redirect = redirectToPage(signInViewPath, context, env.appBaseUrl, {
1055
1102
  returnTo: `${context.url.pathname}${context.url.search}`,
1056
1103
  });
1104
+ if (setCookies.length === 0) {
1105
+ return redirect;
1106
+ }
1107
+ // A failed validation can clear or rotate auth cookies; keep
1108
+ // those directives on the redirect so the browser drops the
1109
+ // stale session instead of replaying it on the next request.
1110
+ const headers = new Headers(redirect.headers);
1111
+ appendSetCookies(headers, setCookies);
1112
+ return new Response(null, { status: redirect.status, headers });
1057
1113
  },
1058
1114
  },
1059
1115
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farm.js/supabase",
3
- "version": "0.1.0-beta.96",
3
+ "version": "0.1.0-beta.98",
4
4
  "description": "Supabase integration for Farm.js",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -31,8 +31,8 @@
31
31
  "dependencies": {
32
32
  "@supabase/ssr": "^0.9.0",
33
33
  "@supabase/supabase-js": "^2.99.2",
34
- "@farm.js/core": "0.1.0-beta.96",
35
- "@farm.js/integration-utils": "0.1.0-beta.96"
34
+ "@farm.js/core": "0.1.0-beta.98",
35
+ "@farm.js/integration-utils": "0.1.0-beta.98"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsc",