feat: Implement Supabase-backed authentication and entitlement checks for frontend-to-backend requests.
This commit is contained in:
@@ -3,6 +3,8 @@ import { NextResponse } from "next/server";
|
|||||||
import { createSupabaseRouteClient, hasSupabaseServerEnv } from "@/lib/supabase/server";
|
import { createSupabaseRouteClient, hasSupabaseServerEnv } from "@/lib/supabase/server";
|
||||||
|
|
||||||
export const BACKEND_ENTITLEMENT_HEADER = "x-polyweather-entitlement";
|
export const BACKEND_ENTITLEMENT_HEADER = "x-polyweather-entitlement";
|
||||||
|
export const FORWARDED_SUPABASE_USER_ID_HEADER = "x-polyweather-auth-user-id";
|
||||||
|
export const FORWARDED_SUPABASE_EMAIL_HEADER = "x-polyweather-auth-email";
|
||||||
|
|
||||||
type HeaderBuildResult = {
|
type HeaderBuildResult = {
|
||||||
headers: HeadersInit;
|
headers: HeadersInit;
|
||||||
@@ -36,6 +38,18 @@ export async function buildBackendRequestHeaders(
|
|||||||
const {
|
const {
|
||||||
data: { session },
|
data: { session },
|
||||||
} = await supabase.auth.getSession();
|
} = await supabase.auth.getSession();
|
||||||
|
const {
|
||||||
|
data: { user },
|
||||||
|
} = await supabase.auth.getUser();
|
||||||
|
|
||||||
|
const forwardedUserId = String(user?.id || session?.user?.id || "").trim();
|
||||||
|
const forwardedEmail = String(user?.email || session?.user?.email || "").trim();
|
||||||
|
if (forwardedUserId) {
|
||||||
|
headers.set(FORWARDED_SUPABASE_USER_ID_HEADER, forwardedUserId);
|
||||||
|
}
|
||||||
|
if (forwardedEmail) {
|
||||||
|
headers.set(FORWARDED_SUPABASE_EMAIL_HEADER, forwardedEmail);
|
||||||
|
}
|
||||||
|
|
||||||
const accessToken = session?.access_token || "";
|
const accessToken = session?.access_token || "";
|
||||||
if (accessToken) {
|
if (accessToken) {
|
||||||
|
|||||||
+20
-6
@@ -95,6 +95,8 @@ def _env_bool(name: str, default: bool = False) -> bool:
|
|||||||
_ENTITLEMENT_GUARD_ENABLED = _env_bool("POLYWEATHER_REQUIRE_ENTITLEMENT", False)
|
_ENTITLEMENT_GUARD_ENABLED = _env_bool("POLYWEATHER_REQUIRE_ENTITLEMENT", False)
|
||||||
_ENTITLEMENT_HEADER = "x-polyweather-entitlement"
|
_ENTITLEMENT_HEADER = "x-polyweather-entitlement"
|
||||||
_ENTITLEMENT_TOKEN = (os.getenv("POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN") or "").strip()
|
_ENTITLEMENT_TOKEN = (os.getenv("POLYWEATHER_BACKEND_ENTITLEMENT_TOKEN") or "").strip()
|
||||||
|
_FORWARDED_SUPABASE_USER_ID_HEADER = "x-polyweather-auth-user-id"
|
||||||
|
_FORWARDED_SUPABASE_EMAIL_HEADER = "x-polyweather-auth-email"
|
||||||
_SUPABASE_AUTH_REQUIRED = _env_bool(
|
_SUPABASE_AUTH_REQUIRED = _env_bool(
|
||||||
"POLYWEATHER_AUTH_REQUIRED",
|
"POLYWEATHER_AUTH_REQUIRED",
|
||||||
SUPABASE_ENTITLEMENT.enabled,
|
SUPABASE_ENTITLEMENT.enabled,
|
||||||
@@ -211,12 +213,24 @@ def _require_supabase_identity(request: Request) -> Dict[str, str]:
|
|||||||
detail="payment requires SUPABASE_URL and SUPABASE_ANON_KEY",
|
detail="payment requires SUPABASE_URL and SUPABASE_ANON_KEY",
|
||||||
)
|
)
|
||||||
token = extract_bearer_token(request.headers.get("authorization"))
|
token = extract_bearer_token(request.headers.get("authorization"))
|
||||||
if not token:
|
if token:
|
||||||
raise HTTPException(status_code=401, detail="Unauthorized")
|
identity = SUPABASE_ENTITLEMENT.get_identity(token)
|
||||||
identity = SUPABASE_ENTITLEMENT.get_identity(token)
|
if identity:
|
||||||
if not identity:
|
return {"user_id": identity.user_id, "email": identity.email}
|
||||||
raise HTTPException(status_code=401, detail="Unauthorized")
|
|
||||||
return {"user_id": identity.user_id, "email": identity.email}
|
# Trusted fallback for server-to-server proxy calls:
|
||||||
|
# only when entitlement token is valid do we accept forwarded identity headers.
|
||||||
|
if _legacy_service_token_valid(request):
|
||||||
|
forwarded_user_id = str(
|
||||||
|
request.headers.get(_FORWARDED_SUPABASE_USER_ID_HEADER) or ""
|
||||||
|
).strip()
|
||||||
|
if forwarded_user_id:
|
||||||
|
forwarded_email = str(
|
||||||
|
request.headers.get(_FORWARDED_SUPABASE_EMAIL_HEADER) or ""
|
||||||
|
).strip()
|
||||||
|
return {"user_id": forwarded_user_id, "email": forwarded_email}
|
||||||
|
|
||||||
|
raise HTTPException(status_code=401, detail="Unauthorized")
|
||||||
|
|
||||||
|
|
||||||
class WalletChallengeRequest(BaseModel):
|
class WalletChallengeRequest(BaseModel):
|
||||||
|
|||||||
Reference in New Issue
Block a user