Skip to content

Commit

Permalink
Add support for auth using HTTP Authorization request header
Browse files Browse the repository at this point in the history
  • Loading branch information
dtatarkin committed Dec 22, 2020
1 parent 0c36f95 commit 67ab18a
Show file tree
Hide file tree
Showing 6 changed files with 46 additions and 20 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ target/
.py2/
.py3/


# PyCharm
.idea
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ $ python setup.py install

```python
from braze.client import BrazeClient
client = BrazeClient(api_key='YOUR_API_KEY')
client = BrazeClient(api_key='YOUR_API_KEY', use_auth_header=True)

r = client.user_track(
attributes=[{
Expand Down
19 changes: 16 additions & 3 deletions braze/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,10 @@ class BrazeClient(object):
print r['errors']
"""

def __init__(self, api_key, api_url=None):
def __init__(self, api_key, api_url=None, use_auth_header=False):
self.api_key = api_key
self.api_url = api_url or DEFAULT_API_URL
self.use_auth_header = use_auth_header
self.session = requests.Session()
self.request_url = ""

Expand Down Expand Up @@ -187,7 +188,8 @@ def user_export(self, external_ids=None, email=None, fields_to_export=None):

def __create_request(self, payload):

payload["api_key"] = self.api_key
if not self.use_auth_header:
payload["api_key"] = self.api_key

response = {"errors": []}
r = self._post_request_with_retries(payload)
Expand Down Expand Up @@ -221,7 +223,18 @@ def _post_request_with_retries(self, payload):
:param dict payload:
:rtype: requests.Response
"""
r = self.session.post(self.request_url, json=payload, timeout=2)

headers = {}
# Prior to April 2020, API keys would be included as a part of the API request body or within the request URL
# as a parameter. Braze now has updated the way in which we read API keys. API keys are now set with the HTTP
# Authorization request header, making your API keys more secure.
# https://www.braze.com/docs/api/api_key/#how-can-i-use-it
if self.use_auth_header:
headers["Authorization"] = "Bearer {}".format(self.api_key)

r = self.session.post(
self.request_url, json=payload, timeout=2, headers=headers
)
# https://www.braze.com/docs/developer_guide/rest_api/messaging/#fatal-errors
if r.status_code == 429:
reset_epoch_s = float(r.headers.get("X-RateLimit-Reset", 0))
Expand Down
21 changes: 21 additions & 0 deletions tests/braze/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class TestBrazeClient(object):
def test_init(self, braze_client):
assert braze_client.api_key == "API_KEY"
assert braze_client.request_url == ""
assert braze_client.use_auth_header is True

def test_user_track(
self, braze_client, requests_mock, attributes, events, purchases
Expand Down Expand Up @@ -245,3 +246,23 @@ def test_standard_case(
assert expected_url == braze_client.request_url
assert response["status_code"] == 201
assert response["message"] == "success"

@pytest.mark.parametrize(
"use_auth_header",
[True, False],
)
def test_auth(self, requests_mock, attributes, use_auth_header):
braze_client = BrazeClient(api_key="API_KEY", use_auth_header=use_auth_header)
headers = {"Content-Type": "application/json"}
mock_json = {"message": "success", "errors": ""}
requests_mock.post(ANY, json=mock_json, status_code=200, headers=headers)

braze_client.user_track(attributes=attributes)
request = requests_mock.last_request
if use_auth_header:
assert "api_key" not in request.json()
assert "Authorization" in request.headers
assert request.headers["Authorization"].startswith("Bearer ")
else:
assert "api_key" in request.json()
assert "Authorization" not in request.headers
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

@pytest.fixture
def braze_client():
return BrazeClient(api_key="API_KEY")
return BrazeClient(api_key="API_KEY", use_auth_header=True)


@pytest.fixture(autouse=True)
Expand Down
19 changes: 4 additions & 15 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,13 @@ deps =
flake8-docstrings
flake8-comprehensions
flake8-bugbear
{[testenv:format]deps}
isort < 5.0.0
black

commands =
; Check style violations
flake8
flake8 --ignore=D202,D205
; Check that imports are sorted/formatted appropriately
isort --check-only --recursive
; Check formatting
black --check .


; Run isort and black on a particular file or directory
[testenv:format]
basepython = python3.7
skip_install = true
deps =
isort >= 4.2.14
black
commands =
; Default to the entire codebase
isort --recursive {posargs: --apply}
black {posargs: .}

0 comments on commit 67ab18a

Please sign in to comment.