forked from slackapi/python-slack-sdk
-
Notifications
You must be signed in to change notification settings - Fork 1
/
verifier.py
67 lines (56 loc) · 2.26 KB
/
verifier.py
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
import hashlib
import hmac
from time import time
from typing import Dict, Optional, Union
class Clock:
def now(self) -> float:
return time()
class SignatureVerifier:
def __init__(self, signing_secret: str, clock: Clock = Clock()):
"""Slack request signature verifier
Slack signs its requests using a secret that's unique to your app.
With the help of signing secrets, your app can more confidently verify
whether requests from us are authentic.
https://api.slack.com/authentication/verifying-requests-from-slack
"""
self.signing_secret = signing_secret
self.clock = clock
def is_valid_request(
self, body: Union[str, bytes], headers: Dict[str, str],
) -> bool:
"""Verifies if the given signature is valid"""
if headers is None:
return False
normalized_headers = {k.lower(): v for k, v in headers.items()}
return self.is_valid(
body=body,
timestamp=normalized_headers.get("x-slack-request-timestamp", None),
signature=normalized_headers.get("x-slack-signature", None),
)
def is_valid(
self, body: Union[str, bytes], timestamp: str, signature: str,
) -> bool:
"""Verifies if the given signature is valid"""
if timestamp is None or signature is None:
return False
if abs(self.clock.now() - int(timestamp)) > 60 * 5:
return False
calculated_signature = self.generate_signature(timestamp=timestamp, body=body)
if calculated_signature is None:
return False
return hmac.compare_digest(calculated_signature, signature)
def generate_signature(
self, *, timestamp: str, body: Union[str, bytes]
) -> Optional[str]:
"""Generates a signature"""
if timestamp is None:
return None
if body is None:
body = ""
if isinstance(body, bytes):
body = body.decode("utf-8")
format_req = str.encode(f"v0:{timestamp}:{body}")
encoded_secret = str.encode(self.signing_secret)
request_hash = hmac.new(encoded_secret, format_req, hashlib.sha256).hexdigest()
calculated_signature = f"v0={request_hash}"
return calculated_signature