Module slack_sdk.signature

Slack request signature verifier

Expand source code
"""Slack request signature verifier"""
import hashlib
import hmac
from time import time
from typing import Dict, Optional, Union


class Clock:
    def now(self) -> float:  # skipcq: PYL-R0201
        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

Classes

class Clock
Expand source code
class Clock:
    def now(self) -> float:  # skipcq: PYL-R0201
        return time()

Methods

def now(self) ‑> float
Expand source code
def now(self) -> float:  # skipcq: PYL-R0201
    return time()
class SignatureVerifier (signing_secret: str, clock: Clock = <slack_sdk.signature.Clock object>)

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

Expand source code
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

Methods

def generate_signature(self, *, timestamp: str, body: Union[str, bytes]) ‑> Optional[str]

Generates a signature

Expand source code
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
def is_valid(self, body: Union[str, bytes], timestamp: str, signature: str) ‑> bool

Verifies if the given signature is valid

Expand source code
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 is_valid_request(self, body: Union[str, bytes], headers: Dict[str, str]) ‑> bool

Verifies if the given signature is valid

Expand source code
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),
    )