Ejemplo n.º 1
0
    def _get_credentials(self, request):
        authorization = request.headers.get('Authorization', '')
        settings = request.registry.settings

        try:
            authmeth, auth = authorization.split(' ', 1)
            assert authmeth.lower() == 'bearer'
        except (AssertionError, ValueError):
            return None

        # Trace authentication type.
        request.auth_type = 'FxA'

        # Use PyFxa defaults if not specified
        server_url = settings['fxa-oauth.oauth_uri']
        scope = settings['fxa-oauth.scope']

        auth_client = OAuthClient(server_url=server_url, cache=self.cache)
        try:
            profile = auth_client.verify_token(token=auth, scope=scope)
            user_id = profile['user']
        except fxa_errors.OutOfProtocolError:
            raise httpexceptions.HTTPServiceUnavailable()
        except (fxa_errors.InProtocolError, fxa_errors.TrustError):
            return None

        return 'fxa_%s' % user_id
Ejemplo n.º 2
0
    def _get_credentials(self, request):
        authorization = request.headers.get('Authorization', '')

        try:
            authmeth, auth = authorization.split(' ', 1)
        except ValueError:
            return None

        if authmeth.lower() != 'bearer':
            return None

        # Use PyFxa defaults if not specified
        server_url = fxa_conf(request, 'oauth_uri')
        scope = aslist(fxa_conf(request, 'required_scope'))

        auth_cache = self._get_cache(request)
        auth_client = OAuthClient(server_url=server_url, cache=auth_cache)
        try:
            profile = auth_client.verify_token(token=auth, scope=scope)
            user_id = profile['user']
        except fxa_errors.OutOfProtocolError as e:
            logger.error(e)
            raise httpexceptions.HTTPServiceUnavailable()
        except (fxa_errors.InProtocolError, fxa_errors.TrustError) as e:
            logger.info(e)
            return None

        return user_id
Ejemplo n.º 3
0
def fxa_oauth_token(request):
    """Return OAuth token from authorization code.
    """
    state = request.validated['state']
    code = request.validated['code']

    # Require on-going session
    stored_redirect = request.cache.get(state)

    # Make sure we cannot try twice with the same code
    request.registry.cache.delete(state)
    if not stored_redirect:
        return authorization_required(request)

    # Trade the OAuth code for a longer-lived token
    auth_client = OAuthClient(server_url=fxa_conf(request, 'oauth_uri'),
                              client_id=fxa_conf(request, 'client_id'),
                              client_secret=fxa_conf(request, 'client_secret'))
    try:
        token = auth_client.trade_code(code)
    except fxa_errors.OutOfProtocolError:
        raise httpexceptions.HTTPServiceUnavailable()
    except fxa_errors.InProtocolError as error:
        logger.error(error)
        error_details = {
            'name': 'code',
            'location': 'querystring',
            'description': 'Firefox Account code validation failed.'
        }
        errors.raise_invalid(request, **error_details)

    return httpexceptions.HTTPFound(location='%s%s' % (stored_redirect, token))
Ejemplo n.º 4
0
class TestCachedClient(unittest.TestCase):
    server_url = TEST_SERVER_URL

    def setUp(self):
        self.client = Client(server_url=self.server_url)
        self.body = ('{"user": "******", "scope": ["profile"],'
                     '"client_id": "abc"}')
        responses.add(responses.POST,
                      'https://server/v1/verify',
                      body=self.body,
                      content_type='application/json')

    def test_has_default_cache(self):
        self.assertIsNotNone(self.client.cache)
        self.assertEqual(self.client.cache.ttl, 300)

    def test_can_change_default_cache(self):
        cache = MemoryCache(0.01)
        self.client = Client(cache=cache)
        self.assertEqual(self.client.cache, cache)
        self.assertEqual(self.client.cache.ttl, 0.01)

    def test_can_deactivate_cache(self):
        self.client = Client(cache=None)
        self.assertIsNone(self.client.cache)

    @responses.activate
    def test_client_verify_code_is_cached(self):
        with mock.patch.object(self.client.cache, 'set') as mocked_set:
            with mock.patch.object(self.client.cache, 'get',
                                   return_value=None):
                # First call
                verification = self.client.verify_token(token='abc')
                self.assertTrue(mocked_set.called)
                self.assertDictEqual(verification, json.loads(self.body))

    @responses.activate
    def test_client_verify_code_cached_value_is_used(self):
        with mock.patch.object(self.client.cache, 'set') as mocked_set:
            with mock.patch.object(self.client.cache, 'get',
                                   return_value=self.body):
                # Second call
                verification = self.client.verify_token(token='abc')
                self.assertFalse(mocked_set.called)
                self.assertDictEqual(verification, json.loads(self.body))

    @responses.activate
    def test_client_verify_code_cached_value_is_not_used_if_no_cache(self):
        self.client = Client(cache=None, server_url=self.server_url)
        # First call
        verification = self.client.verify_token(token='abc')
        self.assertDictEqual(verification, json.loads(self.body))

        # Second call
        verification = self.client.verify_token(token='abc')
        self.assertDictEqual(verification, json.loads(self.body))
Ejemplo n.º 5
0
class TestOAuthClientRedirectURL(unittest.TestCase):

    server_url = TEST_SERVER_URL

    @responses.activate
    def setUp(self):
        self.client = Client("abcdef", server_url=self.server_url)

    def test_redirect_url_with_default_arguments(self):
        redirect_url = urlparse(self.client.get_redirect_url())
        server_url = urlparse(self.server_url)
        self.assertEqual(redirect_url.hostname, server_url.hostname)
        self.assertEqual(redirect_url.path,
                         server_url.path + "/authorization")
        params = parse_qs(redirect_url.query, keep_blank_values=True)
        self.assertEqual(sorted(params.keys()), ["client_id", "state"])
        self.assertEqual(params["client_id"][0], self.client.client_id)
        self.assertEqual(params["state"][0], "")

    def test_redirect_url_takes_custom_client_id(self):
        redirect_url = urlparse(self.client.get_redirect_url(client_id="XX"))
        params = parse_qs(redirect_url.query, keep_blank_values=True)
        self.assertEqual(sorted(params.keys()), ["client_id", "state"])
        self.assertEqual(params["client_id"][0], "XX")

    def test_redirect_url_takes_custom_url_parameters(self):
        redirect_url = urlparse(self.client.get_redirect_url(
            state="applicationstate",
            redirect_uri="https://my.site/oauth",
            scope="profile profile:email",
            action="signup",
            email="*****@*****.**",
            code_challenge="challenge",
            code_challenge_method="S1234",
            access_type="offline",
            keys_jwk="MockJWK",
        ))
        server_url = urlparse(self.server_url)
        self.assertEqual(redirect_url.hostname, server_url.hostname)
        params = parse_qs(redirect_url.query, keep_blank_values=True)
        all_params = ["action", "email", "client_id", "redirect_uri",
                      "scope", "state", "access_type", "code_challenge",
                      "code_challenge_method", "keys_jwk"]
        self.assertEqual(sorted(params.keys()), sorted(all_params))
        self.assertEqual(params["client_id"][0], self.client.client_id)
        self.assertEqual(params["state"][0], "applicationstate")
        self.assertEqual(params["redirect_uri"][0], "https://my.site/oauth")
        self.assertEqual(params["scope"][0], "profile profile:email")
        self.assertEqual(params["action"][0], "signup")
        self.assertEqual(params["email"][0], "*****@*****.**")
        self.assertEqual(params["code_challenge"][0], "challenge")
        self.assertEqual(params["code_challenge_method"][0], "S1234")
        self.assertEqual(params["access_type"][0], "offline")
        self.assertEqual(params["keys_jwk"][0], "MockJWK")
Ejemplo n.º 6
0
def get_oauth_token(client_id=CLIENT_ID, oauth_server=OAUTH_SERVER,
                    auth_server=AUTH_SERVER, email=EMAIL, password=PASSWORD):

    if password is None:
        raise Exception('You must set FXA_PASSWORD')

    print('Getting an oauth token from FxA')
    oauth_client = OAuthClient(client_id, server_url=oauth_server)
    session = Client(server_url=auth_server).login(email, password=password)
    assertion = session.get_identity_assertion(oauth_server)

    return oauth_client.authorize_token(assertion, scope="profile")
Ejemplo n.º 7
0
    def _verify_token(self, token, request):
        """Verify the token extracted from the Authorization header.

        This method stores the result in two locations to avoid hitting the
        auth remote server as much as possible:

        - on the request object, in case the Pyramid authentication methods
          like `effective_principals()` or `authenticated_userid()` are called
          several times during the request cycle;

        - in the cache backend, to reuse validated token from one request to
          another (during ``cache_ttl_seconds`` seconds.)
        """
        # First check if this request was already verified.
        # `request.bound_data` is an attribute provided by Kinto to store
        # some data that is shared among sub-requests (e.g. default bucket
        # or batch requests)
        if REIFY_KEY not in request.bound_data:
            # Use PyFxa defaults if not specified
            server_url = fxa_conf(request, 'oauth_uri')
            auth_cache = self._get_cache(request)
            auth_client = OAuthClient(server_url=server_url, cache=auth_cache)

            user_id = None
            client_name = None

            for scope, client in request.registry._fxa_oauth_scope_routing.items():
                try:
                    profile = auth_client.verify_token(token=token, scope=aslist(scope))
                    user_id = profile['user']
                    scope = profile['scope']
                    client_name = client

                    # Make sure the bearer token scopes don't match multiple configs.
                    routing_scopes = request.registry._fxa_oauth_scope_routing
                    intersecting_scopes = [x for x in routing_scopes.keys()
                                           if x and set(x.split()).issubset(set(scope))]
                    if len(intersecting_scopes) > 1:
                        logger.warn("Invalid FxA token: {} matches multiple config" % scope)
                        return None, None

                    break
                except fxa_errors.OutOfProtocolError as e:
                    logger.exception("Protocol error")
                    raise httpexceptions.HTTPServiceUnavailable()
                except (fxa_errors.InProtocolError, fxa_errors.TrustError) as e:
                    logger.debug("Invalid FxA token: %s" % e)

            # Save for next call.
            request.bound_data[REIFY_KEY] = (user_id, client_name)

        return request.bound_data[REIFY_KEY]
Ejemplo n.º 8
0
class TestAuthClientAuthorizeToken(unittest.TestCase):

    server_url = TEST_SERVER_URL

    def setUp(self):
        self.client = Client("abc", "xyz", server_url=self.server_url)
        responses.add(responses.POST,
                      'https://server/v1/authorization',
                      body='{"access_token": "izatoken"}',
                      content_type='application/json')

    @responses.activate
    def test_authorize_token_with_default_arguments(self):
        assertion = "A_FAKE_ASSERTION"
        token = self.client.authorize_token(assertion)
        self.assertEquals(token, "izatoken")
        req_body = json.loads(responses.calls[0].request.body)
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": self.client.client_id,
            "state": "x",
            "response_type": "token",
        })

    @responses.activate
    def test_authorize_token_with_explicit_scope(self):
        assertion = "A_FAKE_ASSERTION"
        token = self.client.authorize_token(assertion, scope="storage")
        self.assertEquals(token, "izatoken")
        req_body = json.loads(responses.calls[0].request.body)
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": self.client.client_id,
            "state": "x",
            "response_type": "token",
            "scope": "storage",
        })

    @responses.activate
    def test_authorize_token_with_explicit_client_id(self):
        assertion = "A_FAKE_ASSERTION"
        token = self.client.authorize_token(assertion, client_id="cba")
        self.assertEquals(token, "izatoken")
        req_body = json.loads(responses.calls[0].request.body)
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": "cba",
            "state": "x",
            "response_type": "token",
        })
Ejemplo n.º 9
0
class TestAuthClientAuthorizeCode(unittest.TestCase):

    server_url = TEST_SERVER_URL

    def setUp(self):
        self.client = Client("abc", "xyz", server_url=self.server_url)
        body = '{"redirect": "https://relier/page?code=qed&state=blah"}'
        responses.add(responses.POST,
                      'https://server/v1/authorization',
                      body=body,
                      content_type='application/json')

    @responses.activate
    def test_authorize_code_with_default_arguments(self):
        assertion = "A_FAKE_ASSERTION"
        code = self.client.authorize_code(assertion)
        self.assertEquals(code, "qed")
        req_body = json.loads(responses.calls[0].request.body)
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": self.client.client_id,
            "state": "x",
        })

    @responses.activate
    def test_authorize_code_with_explicit_scope(self):
        assertion = "A_FAKE_ASSERTION"
        code = self.client.authorize_code(assertion, scope="profile:email")
        self.assertEquals(code, "qed")
        req_body = json.loads(responses.calls[0].request.body)
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": self.client.client_id,
            "state": "x",
            "scope": "profile:email",
        })

    @responses.activate
    def test_authorize_code_with_explicit_client_id(self):
        assertion = "A_FAKE_ASSERTION"
        code = self.client.authorize_code(assertion, client_id="cba")
        self.assertEquals(code, "qed")
        req_body = json.loads(responses.calls[0].request.body)
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": "cba",
            "state": "x",
        })
Ejemplo n.º 10
0
 def setUp(self):
     self.client = Client("abc", "xyz", server_url=self.server_url)
     body = '{"redirect": "https://relier/page?code=qed&state=blah"}'
     responses.add(responses.POST,
                   'https://server/v1/authorization',
                   body=body,
                   content_type='application/json')
Ejemplo n.º 11
0
 def setUp(self):
     self.client = Client(server_url=self.server_url)
     self.body = ('{"user": "******", "scope": ["profile"],'
                  '"client_id": "abc"}')
     responses.add(responses.POST,
                   'https://server/v1/verify',
                   body=self.body,
                   content_type='application/json')
Ejemplo n.º 12
0
    def test_client_verify_code_cached_value_is_not_used_if_no_cache(self):
        self.client = Client(cache=None, server_url=self.server_url)
        # First call
        verification = self.client.verify_token(token='abc')
        self.assertDictEqual(verification, json.loads(self.body))

        # Second call
        verification = self.client.verify_token(token='abc')
        self.assertDictEqual(verification, json.loads(self.body))
Ejemplo n.º 13
0
class TestAuthClientVerifyCode(unittest.TestCase):

    server_url = TEST_SERVER_URL

    @responses.activate
    def setUp(self):
        self.client = Client(server_url=self.server_url)

        body = '{"user": "******", "scope": ["profile"], "client_id": "abc"}'
        responses.add(responses.POST,
                      'https://server/v1/verify',
                      body=body,
                      content_type='application/json')

        self.verification = self.client.verify_token(token='abc')
        self.response = responses.calls[0]

    def test_reaches_server_on_verify_url(self):
        self.assertEqual(self.response.request.url,
                         'https://server/v1/verify')

    def test_posts_token_to_server(self):
        body = json.loads(_decoded(self.response.request.body))
        expected = {
            "token": "abc",
        }
        self.assertEqual(body, expected)

    def test_returns_response_given_by_server(self):
        expected = {
            "user": "******",
            "scope": ["profile"],
            "client_id": "abc"
        }
        self.assertEqual(self.verification, expected)

    @responses.activate
    def test_raises_error_if_some_attributes_are_not_returned(self):
        responses.add(responses.POST,
                      'https://server/v1/verify',
                      body='{"missing": "attributes"}',
                      content_type='application/json')
        self.assertRaises(fxa.errors.OutOfProtocolError,
                          self.client.verify_token,
                          token='1234')

    @responses.activate
    def test_raises_error_if_scopes_do_not_match(self):
        body = '{"user": "******", "scope": ["files"], "client_id": "abc"}'
        responses.add(responses.POST,
                      'https://server/v1/verify',
                      body=body,
                      content_type='application/json')
        self.assertRaises(fxa.errors.ScopeMismatchError,
                          self.client.verify_token,
                          token='1234',
                          scope='readinglist')
Ejemplo n.º 14
0
    def setUp(self):
        self.client = Client('abc', 'cake', self.server_url)

        body = '{"access_token": "yeah"}'
        responses.add(responses.POST,
                      'https://server/v1/token',
                      body=body,
                      content_type='application/json')

        self.tokens = self.client.trade_code('1234')
        self.response = responses.calls[0]
Ejemplo n.º 15
0
    def setUp(self):
        self.client = Client(server_url=self.server_url)

        body = '{"user": "******", "scope": ["profile"], "client_id": "abc"}'
        responses.add(responses.POST,
                      'https://server/v1/verify',
                      body=body,
                      content_type='application/json')

        self.verification = self.client.verify_token(token='abc')
        self.response = responses.calls[0]
Ejemplo n.º 16
0
 def setUp(self):
     self.client = Client("abc", "xyz", server_url=self.server_url)
     responses.add(responses.POST,
                   'https://server/v1/authorization',
                   body='{"access_token": "izatoken"}',
                   content_type='application/json')
Ejemplo n.º 17
0
 def test_without_prefix_added_prefix(self):
     client = Client('abc', 'cake', "https://server")
     self.assertEqual(client.apiclient.server_url, TEST_SERVER_URL)
Ejemplo n.º 18
0
 def test_trailing_slash_with_prefix(self):
     client = Client('abc', 'cake', "https://server/v1/")
     self.assertEqual(client.apiclient.server_url, TEST_SERVER_URL)
Ejemplo n.º 19
0
 def test_can_deactivate_cache(self):
     self.client = Client(cache=None)
     self.assertIsNone(self.client.cache)
Ejemplo n.º 20
0
 def test_can_change_default_cache(self):
     cache = MemoryCache(0.01)
     self.client = Client(cache=cache)
     self.assertEqual(self.client.cache, cache)
     self.assertEqual(self.client.cache.ttl, 0.01)
Ejemplo n.º 21
0
class TestClientTradeCode(unittest.TestCase):

    server_url = TEST_SERVER_URL

    @responses.activate
    def setUp(self):
        self.client = Client('abc', 'cake', self.server_url)

        body = '{"access_token": "yeah"}'
        responses.add(responses.POST,
                      'https://server/v1/token',
                      body=body,
                      content_type='application/json')

        self.tokens = self.client.trade_code('1234')
        self.response = responses.calls[0]

    def _get_request_body(self):
        return json.loads(_decoded(responses.calls[0].request.body))

    def test_reaches_server_on_token_url(self):
        self.assertEqual(self.response.request.url,
                         'https://server/v1/token')

    def test_posts_code_to_server(self):
        body = json.loads(_decoded(self.response.request.body))
        expected = {
            "client_secret": "cake",
            "code": "1234",
            "client_id": "abc"
        }
        self.assertEqual(body, expected)

    def test_returns_access_token_given_by_server(self):
        self.assertEqual(self.tokens["access_token"], "yeah")

    @responses.activate
    def test_raises_error_if_access_token_not_returned(self):
        responses.add(responses.POST,
                      'https://server/v1/token',
                      body='{"missing": "token"}',
                      content_type='application/json')
        self.assertRaises(fxa.errors.OutOfProtocolError,
                          self.client.trade_code,
                          client_id='abc',
                          client_secret='cake',
                          code='1234')

    @responses.activate
    def test_trade_token_can_take_client_credentials_as_arguments(self):
        responses.add(responses.POST,
                      'https://server/v1/token',
                      body='{"access_token": "tokay"}',
                      content_type='application/json')
        # As positional arguments.
        tokens = self.client.trade_code('1234', 'abc', 'cake2')
        self.assertEqual(tokens, {"access_token": "tokay"})
        self.assertEqual(self._get_request_body(), {
          'client_id': 'abc',
          'client_secret': 'cake2',
          'code': '1234',
        })
        # As keyword arguments.
        tokens = self.client.trade_code(
            code='1234',
            client_id='abc',
            client_secret='cake2'
        )
        self.assertEqual(tokens, {"access_token": "tokay"})
        self.assertEqual(self._get_request_body(), {
          'client_id': 'abc',
          'client_secret': 'cake2',
          'code': '1234',
        })

    @responses.activate
    def test_trade_token_can_take_pkce_verifier_as_argument(self):
        responses.add(responses.POST,
                      'https://server/v1/token',
                      body='{"access_token": "tokay"}',
                      content_type='application/json')
        tokens = self.client.trade_code(
            code='1234',
            code_verifier='verifyme',
        )
        self.assertEqual(tokens, {"access_token": "tokay"})
        self.assertEqual(self._get_request_body(), {
          'client_id': 'abc',
          'client_secret': 'cake',
          'code': '1234',
          'code_verifier': 'verifyme',
        })
Ejemplo n.º 22
0
 def setUp(self):
     self.client = Client("abc", "xyz", server_url=self.server_url)
     responses.add(responses.POST,
                   'https://server/v1/authorization',
                   body='{"access_token": "izatoken"}',
                   content_type='application/json')
Ejemplo n.º 23
0
class TestAuthClientAuthorizeCode(unittest.TestCase):

    server_url = TEST_SERVER_URL

    def setUp(self):
        self.client = Client("abc", "xyz", server_url=self.server_url)
        body = '{"redirect": "https://relier/page?code=qed&state=blah"}'
        responses.add(responses.POST,
                      'https://server/v1/authorization',
                      body=body,
                      content_type='application/json')

    @responses.activate
    def test_authorize_code_with_default_arguments(self):
        assertion = "A_FAKE_ASSERTION"
        code = self.client.authorize_code(assertion)
        self.assertEquals(code, "qed")
        req_body = json.loads(_decoded(responses.calls[0].request.body))
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": self.client.client_id,
            "state": "x",
        })

    @responses.activate
    def test_authorize_code_with_explicit_scope(self):
        assertion = "A_FAKE_ASSERTION"
        code = self.client.authorize_code(assertion, scope="profile:email")
        self.assertEquals(code, "qed")
        req_body = json.loads(_decoded(responses.calls[0].request.body))
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": self.client.client_id,
            "state": "x",
            "scope": "profile:email",
        })

    @responses.activate
    def test_authorize_code_with_explicit_client_id(self):
        assertion = "A_FAKE_ASSERTION"
        code = self.client.authorize_code(assertion, client_id="cba")
        self.assertEquals(code, "qed")
        req_body = json.loads(_decoded(responses.calls[0].request.body))
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": "cba",
            "state": "x",
        })

    @responses.activate
    def test_authorize_code_with_pkce_challenge(self):
        assertion = "A_FAKE_ASSERTION"
        challenge, verifier = self.client.generate_pkce_challenge()
        self.assertEqual(sorted(challenge),
                         ["code_challenge", "code_challenge_method"])
        self.assertEqual(sorted(verifier),
                         ["code_verifier"])
        code = self.client.authorize_code(assertion, **challenge)
        self.assertEquals(code, "qed")
        req_body = json.loads(_decoded(responses.calls[0].request.body))
        self.assertEquals(req_body, {
            "assertion": assertion,
            "client_id": self.client.client_id,
            "state": "x",
            "code_challenge": challenge["code_challenge"],
            "code_challenge_method": challenge["code_challenge_method"],
        })

    @responses.activate
    def test_authorize_code_with_session_object(self):
        session = mock.Mock()
        session.get_identity_assertion.return_value = "IDENTITY"
        code = self.client.authorize_code(session)
        session.get_identity_assertion.assert_called_once_with(
            audience=TEST_SERVER_URL,
            service=self.client.client_id
        )
        self.assertEquals(code, "qed")
        req_body = json.loads(_decoded(responses.calls[0].request.body))
        self.assertEquals(req_body, {
            "assertion": "IDENTITY",
            "client_id": self.client.client_id,
            "state": "x",
        })
Ejemplo n.º 24
0
 def setUp(self):
     self.client = Client("abcdef", server_url=self.server_url)
Ejemplo n.º 25
0
def verify_oauth_token(token, scope='profile', oauth_server=OAUTH_SERVER,
                       client_id=CLIENT_ID):
    oauth_client = OAuthClient(client_id, server_url=oauth_server)
    return oauth_client.verify_token(token, scope=scope)
Ejemplo n.º 26
0
 def setUp(self):
     self.client = Client("abcdef", server_url=self.server_url)