File size: 2,585 Bytes
ce8f04a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
82
83
"""Shared auth helpers: dev-token gate, Clerk issuer allowlist, JWKS resolution."""

from __future__ import annotations

import os
from typing import Optional, Set


DEV_TEST_TOKEN = "dev_test_token"
DEV_TEST_USER = "test_dev_user"


def is_dev_environment() -> bool:
    env = (os.getenv("ENV") or os.getenv("ENVIRONMENT") or "").strip().lower()
    return env in ("dev", "development", "local")


def is_production_environment() -> bool:
    """Treat unset ENV as production (HF Spaces / fail-closed)."""
    env = (os.getenv("ENV") or os.getenv("ENVIRONMENT") or "").strip().lower()
    if not env:
        return True
    return env in ("prod", "production", "hf", "huggingface")


def is_dev_token_allowed() -> bool:
    """Honor dev_test_token only when explicitly enabled — never via HF defaults."""
    flag = (os.getenv("ALLOW_DEV_TOKEN") or "").strip().lower()
    if flag in ("1", "true", "yes", "on"):
        return True
    return is_dev_environment()


def is_dev_test_token(token: Optional[str]) -> bool:
    return bool(token) and token == DEV_TEST_TOKEN and is_dev_token_allowed()


def get_allowed_clerk_issuers() -> Set[str]:
    """Allowlisted Clerk issuers from env (never from unverified JWT)."""
    issuers: Set[str] = set()
    for key in ("CLERK_ISSUER", "CLERK_FRONTEND_API"):
        raw = (os.getenv(key) or "").strip()
        if raw:
            issuers.add(raw.rstrip("/"))
    multi = (os.getenv("CLERK_ISSUERS") or "").strip()
    if multi:
        for part in multi.split(","):
            part = part.strip().rstrip("/")
            if part:
                issuers.add(part)
    return issuers


def get_configured_jwks_url() -> Optional[str]:
    url = (os.getenv("CLERK_JWKS_URL") or "").strip()
    return url or None


def get_clerk_audience() -> Optional[str]:
    aud = (os.getenv("CLERK_AUDIENCE") or os.getenv("CLERK_JWT_AUDIENCE") or "").strip()
    return aud or None


def resolve_jwks_url_for_issuer(issuer: str) -> str:
    """
    Resolve JWKS URL without trusting attacker-controlled issuers.

    Prefer CLERK_JWKS_URL. Otherwise build JWKS only for allowlisted issuers.
    """
    configured = get_configured_jwks_url()
    if configured:
        return configured

    allowed = get_allowed_clerk_issuers()
    normalized = (issuer or "").rstrip("/")
    if not allowed:
        raise ValueError(
            "CLERK_ISSUER or CLERK_JWKS_URL must be configured; refusing JWKS fetch"
        )
    if normalized not in allowed:
        raise ValueError("Untrusted token issuer")
    return f"{normalized}/.well-known/jwks.json"