File size: 2,760 Bytes
3a6c182
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
"""Verify caller identity for /api/pipeline/* — Bearer JWTs and/or pipeline client headers."""

from __future__ import annotations

from typing import Optional, Tuple

import jwt

from flask import request

from ..config import Config
from .mirofish_jwt import verify_mirofish_session_jwt


def verify_supabase_user_jwt(token: str) -> Tuple[Optional[str], Optional[str]]:
    """

    Returns (user_id, error_message). user_id is None on failure.

    """
    if not token or not Config.SUPABASE_JWT_SECRET:
        return None, "JWT verification not configured"
    try:
        payload = jwt.decode(
            token,
            Config.SUPABASE_JWT_SECRET,
            algorithms=["HS256"],
            audience="authenticated",
            options={"verify_exp": True},
        )
        sub = payload.get("sub")
        if not sub:
            return None, "Invalid token: missing sub"
        return str(sub), None
    except jwt.ExpiredSignatureError:
        return None, "Token expired"
    except jwt.InvalidTokenError as e:
        return None, f"Invalid token: {e}"


def extract_bearer_token() -> Optional[str]:
    h = request.headers.get("Authorization", "")
    if h.lower().startswith("bearer "):
        return h[7:].strip()
    return None


def resolve_pipeline_user_id() -> Tuple[Optional[str], Optional[str]]:
    """

    When Supabase sync is on:



    1) Authorization: Bearer <MiroFish session JWT> (optional / legacy).

    2) Authorization: Bearer <Supabase Auth JWT> if SUPABASE_JWT_SECRET is set.

    3) X-Mirofish-User-Id + X-Pipeline-Client-Secret matching PIPELINE_CLIENT_SECRET

       (Supabase public.User in browser + VITE_PIPELINE_CLIENT_SECRET).



    Returns (user_id_str, error_message).

    """
    token = extract_bearer_token()
    if token:
        m_uid, m_hint = verify_mirofish_session_jwt(token)
        if m_uid:
            return m_uid, None
        if m_hint == "expired":
            return None, "Token expired"
        if Config.SUPABASE_JWT_SECRET:
            s_uid, s_err = verify_supabase_user_jwt(token)
            if s_uid:
                return s_uid, None
            return None, s_err or "Invalid token"
        return None, "Invalid token"

    uid = (request.headers.get("X-Mirofish-User-Id") or "").strip()
    key = (request.headers.get("X-Pipeline-Client-Secret") or "").strip()
    secret = (Config.PIPELINE_CLIENT_SECRET or "").strip()
    if uid and secret and key == secret:
        return uid, None
    if uid or key:
        return None, "Invalid pipeline client credentials"
    if not secret:
        return None, "Server PIPELINE_CLIENT_SECRET is not configured"
    return None, "Missing credentials"