Codebase list python-flask-httpauth / upstream/latest
New upstream version 3.2.4 Martín Ferrari 5 years ago
11 changed file(s) with 105 addition(s) and 72 deletion(s). Raw diff Collapse all Expand all
00 language: python
1 env:
2 - TOXENV=flake8
3 - TOXENV=py26
4 - TOXENV=py27
5 - TOXENV=py33
6 - TOXENV=py34
7 - TOXENV=py35
8 - TOXENV=pypy
9 - TOXENV=docs
1 matrix:
2 include:
3 - python: 3.6
4 env: TOXENV=flake8
5 - python: 2.7
6 env: TOXENV=py27
7 - python: 3.4
8 env: TOXENV=py34
9 - python: 3.5
10 env: TOXENV=py35
11 - python: 3.6
12 env: TOXENV=py36
13 - python: pypy
14 env: TOXENV=pypy
15 - python: pypy3
16 env: TOXENV=pypy3
17 - python: 3.6
18 env: TOXENV=docs
1019 install:
1120 - pip install tox
1221 script:
00 # Flask-HTTPAuth Change Log
11
2 ## Unreleased
2 ## Release 3.2.2 - 2017-01-29
3
4 - Validate authorization header in multi auth ([#51](https://github.com/miguelgrinberg/Flask-HTTPAuth/issues/51))
5
6 ## Release 3.2.1 - 2016-09-04
7
8 - Added `__version__` to top-level package
9 - Added readme and license files to package
10
11 ## Release 3.2.0 - 2016-08-20
12
13 - Changed license to MIT
14 - Fix TCP Connection reset by peer error ([#39](https://github.com/miguelgrinberg/Flask-HTTPAuth/pull/39))
315
416 ## Release 3.1.2 - 2016-04-20
517
00 include README.md LICENSE
1 recursive-include tests *.py
2 recursive-include docs *
7777 Resources
7878 ---------
7979
80 - [Documentation](http://pythonhosted.org/Flask-HTTPAuth)
81 - [pypi](https://pypi.python.org/pypi/Flask-HTTPAuth)
80 - [Documentation](http://flask-httpauth.readthedocs.io/en/latest/)
81 - [PyPI](https://pypi.org/project/Flask-HTTPAuth)
8282 - [Change log](https://github.com/miguelgrinberg/Flask-HTTPAuth/blob/master/CHANGELOG.md)
5151
5252 @auth.hash_password
5353 def hash_pw(username, password):
54 get_salt(username)
54 salt = get_salt(username)
5555 return hash(password, salt)
5656
5757 For the most degree of flexibility the `get_password` and `hash_password` callbacks can be replaced with `verify_password`::
9696 Security Concerns with Digest Authentication
9797 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9898
99 The digest authentication algorihtm requires a *challenge* to be sent to the client for use in encrypting the password for transmission. This challenge needs to be used again when the password is decoded at the server, so the challenge information needs to be stored so that it can be recalled later.
99 The digest authentication algorithm requires a *challenge* to be sent to the client for use in encrypting the password for transmission. This challenge needs to be used again when the password is decoded at the server, so the challenge information needs to be stored so that it can be recalled later.
100100
101101 By default, Flask-HTTPAuth stores the challenge data in the Flask session. To make the authentication flow secure when using session storage, it is required that server-side sessions are used instead of the default Flask cookie based sessions, as this ensures that the challenge data is not at risk of being captured as it moves in a cookie between server and client. The Flask-Session and Flask-KVSession extensions are both very good options to implement server-side sessions.
102102
166166 Using Multiple Authentication Schemes
167167 -------------------------------------
168168
169 Applications sometimes need to support a combination of authentication methods. For example, a web application could be authenticating by sending client id and secret over basic authentication, while third party API clients use a JWT bearer token. The `MultiAuth` class allows you to protect a route with more than one authentication object. To grant access to the endpoint, one of the authentication methods must validate.
169 Applications sometimes need to support a combination of authentication methods. For example, a web application could be authenticated by sending client id and secret over basic authentication, while third party API clients use a JWT bearer token. The `MultiAuth` class allows you to protect a route with more than one authentication object. To grant access to the endpoint, one of the authentication methods must validate.
170170
171171 In the examples directory you can find a complete example that uses basic and token authentication.
172172
212212
213213 @auth.hash_password
214214 def hash_pw(username, password):
215 get_salt(username)
215 salt = get_salt(username)
216216 return hash(password, salt)
217217
218218 .. method:: verify_password(verify_password_callback)
240240
241241 .. method:: login_required(view_function_callback)
242242
243 This callback function will be called when authentication is succesful. This will typically be a Flask view function. Example::
243 This callback function will be called when authentication is successful. This will typically be a Flask view function. Example::
244244
245245 @app.route('/private')
246246 @auth.login_required
3434
3535
3636 if __name__ == '__main__':
37 app.run()
37 app.run(debug=True, host='0.0.0.0')
4646 g.user = None
4747 try:
4848 data = jwt.loads(token)
49 except:
49 except: # noqa: E722
5050 return False
5151 if 'username' in data:
5252 g.user = data['username']
11 """Token authentication example
22
33 This example demonstrates how to protect Flask endpoints with token
4 authentication, using JWT tokens.
4 authentication, using tokens.
55
66 When this application starts, a token is generated for each of the two users.
77 To gain access, you can use a command line HTTP client such as curl, passing
99
1010 curl -X GET -H "Authorization: Bearer <jwt-token>" http://localhost:5000/
1111
12 The response should include the username, which is obtained from the JWT token.
12 The response should include the username, which is obtained from the token.
1313 """
1414 from flask import Flask, g
1515 from flask_httpauth import HTTPTokenAuth
16 from itsdangerous import TimedJSONWebSignatureSerializer as JWT
16 from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
1717
1818
1919 app = Flask(__name__)
2020 app.config['SECRET_KEY'] = 'top secret!'
21 jwt = JWT(app.config['SECRET_KEY'], expires_in=3600)
21 token_serializer = Serializer(app.config['SECRET_KEY'], expires_in=3600)
2222
2323 auth = HTTPTokenAuth('Bearer')
2424
2525
2626 users = ['john', 'susan']
2727 for user in users:
28 token = jwt.dumps({'username': user})
28 token = token_serializer.dumps({'username': user}).decode('utf-8')
2929 print('*** token for {}: {}\n'.format(user, token))
3030
3131
3333 def verify_token(token):
3434 g.user = None
3535 try:
36 data = jwt.loads(token)
37 except:
36 data = token_serializer.loads(token)
37 except: # noqa: E722
3838 return False
3939 if 'username' in data:
4040 g.user = data['username']
1313 from flask import request, make_response, session
1414 from werkzeug.datastructures import Authorization
1515
16 __version__ = '3.2.1'
16 __version__ = '3.2.4'
1717
1818
1919 class HTTPAuth(object):
5353 def authenticate_header(self):
5454 return '{0} realm="{1}"'.format(self.scheme, self.realm)
5555
56 def get_auth(self):
57 auth = request.authorization
58 if auth is None and 'Authorization' in request.headers:
59 # Flask/Werkzeug do not recognize any authentication types
60 # other than Basic or Digest, so here we parse the header by
61 # hand
62 try:
63 auth_type, token = request.headers['Authorization'].split(
64 None, 1)
65 auth = Authorization(auth_type, {'token': token})
66 except ValueError:
67 # The Authorization header is either empty or has no token
68 pass
69
70 # if the auth type does not match, we act as if there is no auth
71 # this is better than failing directly, as it allows the callback
72 # to handle special cases, like supporting multiple auth types
73 if auth is not None and auth.type.lower() != self.scheme.lower():
74 auth = None
75
76 return auth
77
78 def get_auth_password(self, auth):
79 password = None
80
81 if auth and auth.username:
82 password = self.get_password_callback(auth.username)
83
84 return password
85
5686 def login_required(self, f):
5787 @wraps(f)
5888 def decorated(*args, **kwargs):
59 auth = request.authorization
60 if auth is None and 'Authorization' in request.headers:
61 # Flask/Werkzeug do not recognize any authentication types
62 # other than Basic or Digest, so here we parse the header by
63 # hand
64 try:
65 auth_type, token = request.headers['Authorization'].split(
66 None, 1)
67 auth = Authorization(auth_type, {'token': token})
68 except ValueError:
69 # The Authorization header is either empty or has no token
70 pass
71
72 # if the auth type does not match, we act as if there is no auth
73 # this is better than failing directly, as it allows the callback
74 # to handle special cases, like supporting multiple auth types
75 if auth is not None and auth.type.lower() != self.scheme.lower():
76 auth = None
89 auth = self.get_auth()
7790
7891 # Flask normally handles OPTIONS requests on its own, but in the
7992 # case it is configured to forward those to the application, we
8093 # need to ignore authentication headers and let the request through
8194 # to avoid unwanted interactions with CORS.
8295 if request.method != 'OPTIONS': # pragma: no cover
83 if auth and auth.username:
84 password = self.get_password_callback(auth.username)
85 else:
86 password = None
96 password = self.get_auth_password(auth)
97
8798 if not self.authenticate(auth, password):
8899 # Clear TCP receive buffer of any pending data
89100 request.data
256267 def decorated(*args, **kwargs):
257268 selected_auth = None
258269 if 'Authorization' in request.headers:
259 scheme, creds = request.headers['Authorization'].split(None, 1)
260 for auth in self.additional_auth:
261 if auth.scheme == scheme:
262 selected_auth = auth
263 break
270 try:
271 scheme, creds = request.headers['Authorization'].split(
272 None, 1)
273 except ValueError:
274 # malformed Authorization header
275 pass
276 else:
277 for auth in self.additional_auth:
278 if auth.scheme == scheme:
279 selected_auth = auth
280 break
264281 if selected_auth is None:
265282 selected_auth = self.main_auth
266283 return selected_auth.login_required(f)(*args, **kwargs)
8080 self.assertTrue('WWW-Authenticate' in response.headers)
8181 self.assertEqual(response.headers['WWW-Authenticate'],
8282 'Basic realm="Authentication Required"')
83
84 def test_multi_malformed_header(self):
85 response = self.client.get(
86 '/protected', headers={'Authorization': 'token-without-scheme'})
87 self.assertEqual(response.status_code, 401)
00 [tox]
1 envlist=flake8,py27,py33,py34,py35,pypy,docs,coverage
1 envlist=flake8,py27,py34,py35,py36,pypy,docs,coverage
22 skip_missing_interpreters=True
33
44 [testenv]
66 coverage run --branch --include=flask_httpauth.py setup.py test
77 coverage report --show-missing
88 coverage erase
9 deps=
10 coverage
911
1012 [testenv:flake8]
1113 basepython=python
1618
1719 [testenv:py26]
1820 basepython=python2.6
19 deps=
20 coverage
2121
2222 [testenv:py27]
2323 basepython=python2.7
24 deps=
25 coverage
26
27 [testenv:py33]
28 basepython=python3.3
29 deps=
30 coverage
3124
3225 [testenv:py34]
3326 basepython=python3.4
34 deps=
35 coverage
3627
3728 [testenv:py35]
3829 basepython=python3.5
39 deps=
40 coverage
30
31 [testenv:py36]
32 basepython=python3.6
4133
4234 [testenv:pypy]
4335 basepython=pypy
44 deps=
45 coverage
4636
4737 [testenv:docs]
4838 basepython=python2.7
5646
5747 [testenv:coverage]
5848 basepython=python
59 deps=
60 coverage
6149 commands=
6250 coverage run --branch --source=flask_httpauth.py setup.py test
6351 coverage html