def _check_jwt_claims(jwt_claims): """Checks whether the JWT claims should be accepted. Specifically, this method checks the "exp" claim and the "nbf" claim (if present), and raises UnauthenticatedException if 1) the current time is before the time identified by the "nbf" claim, or 2) the current time is equal to or after the time identified by the "exp" claim. Args: jwt_claims: the JWT claims whose expiratio to be checked. Raises: UnauthenticatedException: When the "exp" claim is malformed or the JWT has already expired. """ current_time = time.time() expiration = jwt_claims["exp"] if not isinstance(expiration, (int, long)): raise suppliers.UnauthenticatedException( 'Malformed claim: "exp" must be an integer') if current_time >= expiration: raise suppliers.UnauthenticatedException( "The auth token has already expired") if "nbf" not in jwt_claims: return not_before_time = jwt_claims["nbf"] if not isinstance(not_before_time, (int, long)): raise suppliers.UnauthenticatedException( 'Malformed claim: "nbf" must be an integer') if current_time < not_before_time: raise suppliers.UnauthenticatedException( 'Current time is less than the "nbf" time')
def authenticate(self, auth_token, auth_info, service_name): """Authenticates the current auth token. Args: auth_token: the auth token. auth_info: the auth configurations of the API method being called. service_name: the name of this service. Returns: A constructed UserInfo object representing the identity of the caller. Raises: UnauthenticatedException: When * the issuer is not allowed; * the audiences are not allowed; * the auth token has already expired. """ try: jwt_claims = self.get_jwt_claims(auth_token) except Exception as error: raise suppliers.UnauthenticatedException( "Cannot decode the auth token", error) _check_jwt_claims(jwt_claims) user_info = UserInfo(jwt_claims) issuer = user_info.issuer if issuer not in self._issuers_to_provider_ids: raise suppliers.UnauthenticatedException("Unknown issuer: " + issuer) provider_id = self._issuers_to_provider_ids[issuer] if not auth_info.is_provider_allowed(provider_id): raise suppliers.UnauthenticatedException( "The requested method does not " "allow provider id: " + provider_id) # Check the audiences decoded from the auth token. The auth token is # allowed when 1) an audience is equal to the service name, or 2) at least # one audience is allowed in the method configuration. audiences = user_info.audiences has_service_name = service_name in audiences allowed_audiences = auth_info.get_allowed_audiences(provider_id) intersected_audiences = set(allowed_audiences).intersection(audiences) if not has_service_name and not intersected_audiences: raise suppliers.UnauthenticatedException("Audiences not allowed") return user_info
def test_malformed_authorization_header(self): auth_app = AuthMiddleware(self.UserInfoWsgiApp(), self._mock_authenticator) environ = { "HTTP_AUTHORIZATION": "malformed-auth-token", wsgi.EnvironmentMiddleware.METHOD_INFO: mock.MagicMock(), wsgi.EnvironmentMiddleware.SERVICE_NAME: "service-name" } self._mock_authenticator.authenticate.side_effect = suppliers.UnauthenticatedException( ) self.assertIsNone(auth_app(environ, _dummy_start_response))
def _decode_and_verify(): jwt_claims = jwt.JWT().unpack(auth_token).payload() _verify_required_claims_exist(jwt_claims) issuer = jwt_claims["iss"] keys = self._jwks_supplier.supply(issuer) try: return jws.JWS().verify_compact(auth_token, keys) except (jwkest.BadSignature, jws.NoSuitableSigningKeys, jws.SignerAlgError) as exception: raise suppliers.UnauthenticatedException( "Signature verification failed", exception)
def _verify_required_claims_exist(jwt_claims): """Verifies that the required claims exist. Args: jwt_claims: the JWT claims to be verified. Raises: UnauthenticatedException: if some claim doesn't exist. """ for claim_name in ["aud", "exp", "iss", "sub"]: if claim_name not in jwt_claims: raise suppliers.UnauthenticatedException('Missing "%s" claim' % claim_name)