Skip to content

url_signer — HMAC-signed URLs for Mini Apps

⬅️ Back | 🏠 Docs Root

UrlSignerService

UrlSignerService

Service for securing Telegram Mini App URLs via HMAC-SHA256 signatures.

This service generates tamper-proof URLs by appending a cryptographic signature based on a request identifier and a timestamp. It is essential for protecting backend endpoints from unauthorized access outside the intended user session.

Parameters:

Name Type Description Default
secret_key str

The cryptographic key used for HMAC signature generation.

required
Source code in src/codex_bot/url_signer/service.py
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class UrlSignerService:
    """Service for securing Telegram Mini App URLs via HMAC-SHA256 signatures.

    This service generates tamper-proof URLs by appending a cryptographic
    signature based on a request identifier and a timestamp. It is
    essential for protecting backend endpoints from unauthorized access
    outside the intended user session.

    Args:
        secret_key: The cryptographic key used for HMAC signature generation.
    """

    def __init__(self, secret_key: str) -> None:
        self._secret_key = secret_key.encode("utf-8")

    def generate_signed_url(
        self,
        base_url: str,
        request_id: str | int,
        action: str = "reply",
    ) -> str:
        """Generate a cryptographically signed URL for Telegram Mini Apps.

        Constructs a URL including a request identifier, current timestamp,
        and an HMAC-SHA256 signature calculated over the combined payload.

        Args:
            base_url: The root domain of the Mini App host.
            request_id: A unique identifier for the specific request or entity.
            action: The logical path component identifying the target action.

        Returns:
            A fully qualified URL with integrated security parameters.
        """
        timestamp = str(int(time.time()))
        req_id_str = str(request_id)

        payload = f"{req_id_str}:{timestamp}".encode()
        signature = hmac.new(self._secret_key, payload, hashlib.sha256).hexdigest()

        params = {"req_id": req_id_str, "ts": timestamp, "sig": signature}
        clean_base_url = base_url.rstrip("/")

        return f"{clean_base_url}/tma/{action}/?{urlencode(params, quote_via=quote_plus)}"

    def verify_signed_url(
        self,
        req_id: str,
        timestamp: str,
        signature: str,
        max_age: int = 300,
    ) -> bool:
        """Verifies the URL signature.

        Args:
            req_id: Request ID from the ``req_id`` parameter.
            timestamp: Timestamp from the ``ts`` parameter.
            signature: Signature from the ``sig`` parameter.
            max_age: Maximum signature age in seconds (default is 300).

        Returns:
            ``True`` if the signature is valid and has not expired.

        Example:
            ```python
            is_valid = signer.verify_signed_url(
                req_id=request.query_params["req_id"],
                timestamp=request.query_params["ts"],
                signature=request.query_params["sig"],
            )
            ```
        """
        try:
            ts_int = int(timestamp)
        except ValueError:
            return False

        if int(time.time()) - ts_int > max_age:
            return False

        payload = f"{req_id}:{timestamp}".encode()
        expected = hmac.new(self._secret_key, payload, hashlib.sha256).hexdigest()

        return hmac.compare_digest(expected, signature)

Functions

generate_signed_url(base_url, request_id, action='reply')

Generate a cryptographically signed URL for Telegram Mini Apps.

Constructs a URL including a request identifier, current timestamp, and an HMAC-SHA256 signature calculated over the combined payload.

Parameters:

Name Type Description Default
base_url str

The root domain of the Mini App host.

required
request_id str | int

A unique identifier for the specific request or entity.

required
action str

The logical path component identifying the target action.

'reply'

Returns:

Type Description
str

A fully qualified URL with integrated security parameters.

Source code in src/codex_bot/url_signer/service.py
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
def generate_signed_url(
    self,
    base_url: str,
    request_id: str | int,
    action: str = "reply",
) -> str:
    """Generate a cryptographically signed URL for Telegram Mini Apps.

    Constructs a URL including a request identifier, current timestamp,
    and an HMAC-SHA256 signature calculated over the combined payload.

    Args:
        base_url: The root domain of the Mini App host.
        request_id: A unique identifier for the specific request or entity.
        action: The logical path component identifying the target action.

    Returns:
        A fully qualified URL with integrated security parameters.
    """
    timestamp = str(int(time.time()))
    req_id_str = str(request_id)

    payload = f"{req_id_str}:{timestamp}".encode()
    signature = hmac.new(self._secret_key, payload, hashlib.sha256).hexdigest()

    params = {"req_id": req_id_str, "ts": timestamp, "sig": signature}
    clean_base_url = base_url.rstrip("/")

    return f"{clean_base_url}/tma/{action}/?{urlencode(params, quote_via=quote_plus)}"

verify_signed_url(req_id, timestamp, signature, max_age=300)

Verifies the URL signature.

Parameters:

Name Type Description Default
req_id str

Request ID from the req_id parameter.

required
timestamp str

Timestamp from the ts parameter.

required
signature str

Signature from the sig parameter.

required
max_age int

Maximum signature age in seconds (default is 300).

300

Returns:

Type Description
bool

True if the signature is valid and has not expired.

Example
is_valid = signer.verify_signed_url(
    req_id=request.query_params["req_id"],
    timestamp=request.query_params["ts"],
    signature=request.query_params["sig"],
)
Source code in src/codex_bot/url_signer/service.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
def verify_signed_url(
    self,
    req_id: str,
    timestamp: str,
    signature: str,
    max_age: int = 300,
) -> bool:
    """Verifies the URL signature.

    Args:
        req_id: Request ID from the ``req_id`` parameter.
        timestamp: Timestamp from the ``ts`` parameter.
        signature: Signature from the ``sig`` parameter.
        max_age: Maximum signature age in seconds (default is 300).

    Returns:
        ``True`` if the signature is valid and has not expired.

    Example:
        ```python
        is_valid = signer.verify_signed_url(
            req_id=request.query_params["req_id"],
            timestamp=request.query_params["ts"],
            signature=request.query_params["sig"],
        )
        ```
    """
    try:
        ts_int = int(timestamp)
    except ValueError:
        return False

    if int(time.time()) - ts_int > max_age:
        return False

    payload = f"{req_id}:{timestamp}".encode()
    expected = hmac.new(self._secret_key, payload, hashlib.sha256).hexdigest()

    return hmac.compare_digest(expected, signature)