Skip to content

Commit

Permalink
Fix ID token verification (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jon Wayne Parrott committed Nov 17, 2016
1 parent 7bd15ac commit 97eb870
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 6 deletions.
17 changes: 17 additions & 0 deletions google/auth/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

"""Helper functions for commonly used utilities."""

import base64
import calendar
import datetime

Expand Down Expand Up @@ -194,3 +195,19 @@ def string_to_scopes(scopes):
return []

return scopes.split(' ')


def padded_urlsafe_b64decode(value):
"""Decodes base64 strings lacking padding characters.
Google infrastructure tends to omit the base64 padding characters.
Args:
value (Union[str, bytes]): The encoded value.
Returns:
bytes: The decoded value
"""
b64string = to_bytes(value)
padded = b64string + b'=' * (-len(b64string) % 4)
return base64.urlsafe_b64decode(padded)
4 changes: 2 additions & 2 deletions google/auth/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def encode(signer, payload, header=None, key_id=None):

def _decode_jwt_segment(encoded_section):
"""Decodes a single JWT segment."""
section_bytes = base64.urlsafe_b64decode(encoded_section)
section_bytes = _helpers.padded_urlsafe_b64decode(encoded_section)
try:
return json.loads(section_bytes.decode('utf-8'))
except ValueError:
Expand Down Expand Up @@ -124,7 +124,7 @@ def _unverified_decode(token):

encoded_header, encoded_payload, signature = token.split(b'.')
signed_section = encoded_header + b'.' + encoded_payload
signature = base64.urlsafe_b64decode(signature)
signature = _helpers.padded_urlsafe_b64decode(signature)

# Parse segments
header = _decode_jwt_segment(encoded_header)
Expand Down
2 changes: 1 addition & 1 deletion google/oauth2/id_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _fetch_certs(request, certs_url):
Mapping[str, str]: A mapping of public key ID to x.509 certificate
data.
"""
response = request('GET', certs_url)
response = request(certs_url, method='GET')

if response.status != http_client.OK:
raise exceptions.TransportError(
Expand Down
7 changes: 4 additions & 3 deletions tests/oauth2/test_id_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import pytest

from google.auth import exceptions
import google.auth.transport
from google.oauth2 import id_token


Expand All @@ -28,7 +29,7 @@ def make_request(status, data=None):
if data is not None:
response.data = json.dumps(data).encode('utf-8')

return mock.Mock(return_value=response)
return mock.Mock(return_value=response, spec=google.auth.transport.Request)


def test__fetch_certs_success():
Expand All @@ -37,7 +38,7 @@ def test__fetch_certs_success():

returned_certs = id_token._fetch_certs(request, mock.sentinel.cert_url)

request.assert_called_once_with('GET', mock.sentinel.cert_url)
request.assert_called_once_with(mock.sentinel.cert_url, method='GET')
assert returned_certs == certs


Expand All @@ -47,7 +48,7 @@ def test__fetch_certs_failure():
with pytest.raises(exceptions.TransportError):
id_token._fetch_certs(request, mock.sentinel.cert_url)

request.assert_called_once_with('GET', mock.sentinel.cert_url)
request.assert_called_once_with(mock.sentinel.cert_url, method='GET')


@mock.patch('google.auth.jwt.decode', autospec=True)
Expand Down
16 changes: 16 additions & 0 deletions tests/test__helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,19 @@ def test_string_to_scopes():

for case, expected in cases:
assert _helpers.string_to_scopes(case) == expected


def test_padded_urlsafe_b64decode():
cases = [
('YQ==', b'a'),
('YQ', b'a'),
('YWE=', b'aa'),
('YWE', b'aa'),
('YWFhYQ==', b'aaaa'),
('YWFhYQ', b'aaaa'),
('YWFhYWE=', b'aaaaa'),
('YWFhYWE', b'aaaaa'),
]

for case, expected in cases:
assert _helpers.padded_urlsafe_b64decode(case) == expected

0 comments on commit 97eb870

Please sign in to comment.