diff --git a/falcon/errors.py b/falcon/errors.py index d54369ba0..4ad95e757 100644 --- a/falcon/errors.py +++ b/falcon/errors.py @@ -50,18 +50,18 @@ class HTTPUnauthorized(HTTPError): title (str): Error title (e.g., 'Authentication Required'). description (str): Human-friendly description of the error, along with a helpful suggestion or two. - scheme (str): Authentication scheme to use as the value of the - WWW-Authenticate header in the response (default ``None``). + challenges (iterable of str): One or more authentication + challenges to use as the value of the WWW-Authenticate header in + the response. kwargs (optional): Same as for ``HTTPError``. """ - def __init__(self, title, description, **kwargs): + def __init__(self, title, description, challenges, **kwargs): headers = kwargs.setdefault('headers', {}) - scheme = kwargs.pop('scheme', None) - if scheme is not None: - headers['WWW-Authenticate'] = scheme + if challenges: + headers['WWW-Authenticate'] = ', '.join(challenges) super(HTTPUnauthorized, self).__init__(status.HTTP_401, title, description, **kwargs) diff --git a/tests/test_httperror.py b/tests/test_httperror.py index 438fd7d89..fd04bc044 100644 --- a/tests/test_httperror.py +++ b/tests/test_httperror.py @@ -74,14 +74,17 @@ class UnauthorizedResource: def on_get(self, req, resp): raise falcon.HTTPUnauthorized('Authentication Required', 'Missing or invalid token header.', - scheme='Token; UUID') + ['Basic realm="simple"']) + def on_post(self, req, resp): + raise falcon.HTTPUnauthorized('Authentication Required', + 'Missing or invalid token header.', + ['Newauth realm="apps"', + 'Basic realm="simple"']) -class UnauthorizedResourceSchemaless: - - def on_get(self, req, resp): + def on_put(self, req, resp): raise falcon.HTTPUnauthorized('Authentication Required', - 'Missing or invalid token header.') + 'Missing or invalid token header.', []) class NotFoundResource: @@ -454,15 +457,20 @@ def test_401(self): self.simulate_request('/401') self.assertEqual(self.srmock.status, falcon.HTTP_401) - self.assertIn(('www-authenticate', 'Token; UUID'), + self.assertIn(('www-authenticate', 'Basic realm="simple"'), self.srmock.headers) - def test_401_schemaless(self): - self.api.add_route('/401', UnauthorizedResourceSchemaless()) - self.simulate_request('/401') + self.simulate_request('/401', method='POST') + + self.assertEqual(self.srmock.status, falcon.HTTP_401) + self.assertIn(('www-authenticate', 'Newauth realm="apps", ' + 'Basic realm="simple"'), + self.srmock.headers) + + self.simulate_request('/401', method='PUT') self.assertEqual(self.srmock.status, falcon.HTTP_401) - self.assertNotIn(('www-authenticate', 'Token'), self.srmock.headers) + self.assertNotIn(('www-authenticate', []), self.srmock.headers) def test_404_without_body(self): self.api.add_route('/404', NotFoundResource())