diff --git a/google/auth/_helpers.py b/google/auth/_helpers.py index 860b82719..b32801a32 100644 --- a/google/auth/_helpers.py +++ b/google/auth/_helpers.py @@ -215,3 +215,20 @@ def padded_urlsafe_b64decode(value): b64string = to_bytes(value) padded = b64string + b'=' * (-len(b64string) % 4) return base64.urlsafe_b64decode(padded) + + +def unpadded_urlsafe_b64encode(value): + """Encodes base64 strings removing any padding characters. + + `rfc 7515`_ defines Base64url to NOT include any padding + characters, but the stdlib doesn't do that by default. + + _rfc7515: https://tools.ietf.org/html/rfc7515#page-6 + + Args: + value (Union[str|bytes]): The bytes-like value to encode + + Returns: + Union[str|bytes]: The encoded value + """ + return base64.urlsafe_b64encode(value).rstrip(b'=') diff --git a/google/auth/jwt.py b/google/auth/jwt.py index 3805f3716..bea70adf3 100644 --- a/google/auth/jwt.py +++ b/google/auth/jwt.py @@ -40,7 +40,6 @@ """ -import base64 import collections import copy import datetime @@ -86,13 +85,19 @@ def encode(signer, payload, header=None, key_id=None): header['kid'] = key_id segments = [ - base64.urlsafe_b64encode(json.dumps(header).encode('utf-8')), - base64.urlsafe_b64encode(json.dumps(payload).encode('utf-8')), + _helpers.unpadded_urlsafe_b64encode( + json.dumps(header).encode('utf-8') + ), + _helpers.unpadded_urlsafe_b64encode( + json.dumps(payload).encode('utf-8') + ), ] signing_input = b'.'.join(segments) signature = signer.sign(signing_input) - segments.append(base64.urlsafe_b64encode(signature)) + segments.append( + _helpers.unpadded_urlsafe_b64encode(signature) + ) return b'.'.join(segments) diff --git a/tests/test__helpers.py b/tests/test__helpers.py index b067faabf..79bdef3d7 100644 --- a/tests/test__helpers.py +++ b/tests/test__helpers.py @@ -167,3 +167,15 @@ def test_padded_urlsafe_b64decode(): for case, expected in cases: assert _helpers.padded_urlsafe_b64decode(case) == expected + + +def test_unpadded_urlsafe_b64encode(): + cases = [ + (b'', b''), + (b'a', b'YQ'), + (b'aa', b'YWE'), + (b'aaa', b'YWFh'), + ] + + for case, expected in cases: + assert _helpers.unpadded_urlsafe_b64encode(case) == expected