1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355
|
.. Flask-HTTPAuth documentation master file, created by
sphinx-quickstart on Fri Jul 26 14:48:13 2013.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to Flask-HTTPAuth's documentation!
==========================================
**Flask-HTTPAuth** is a simple extension that simplifies the use of HTTP authentication with Flask routes.
Basic authentication example
----------------------------
The following example application uses HTTP Basic authentication to protect route ``'/'``::
from flask import Flask
from flask_httpauth import HTTPBasicAuth
app = Flask(__name__)
auth = HTTPBasicAuth()
users = {
"john": "hello",
"susan": "bye"
}
@auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
@app.route('/')
@auth.login_required
def index():
return "Hello, %s!" % auth.username()
if __name__ == '__main__':
app.run()
The ``get_password`` callback needs to return the password associated with the username given as argument. Flask-HTTPAuth will allow access only if ``get_password(username) == password``.
If the passwords are stored hashed in the user database then an additional callback is needed::
@auth.hash_password
def hash_pw(password):
return md5(password).hexdigest()
When the ``hash_password`` callback is provided access will be granted when ``get_password(username) == hash_password(password)``.
If the hashing algorithm requires the username to be known then the callback can take two arguments instead of one::
@auth.hash_password
def hash_pw(username, password):
salt = get_salt(username)
return hash(password, salt)
For the most degree of flexibility the `get_password` and `hash_password` callbacks can be replaced with `verify_password`::
@auth.verify_password
def verify_pw(username, password):
return call_custom_verify_function(username, password)
In the examples directory you can find an example called `basic_auth.py` that shows how a `verify_password` callback can be used to securely work with hashed passwords.
Digest authentication example
-----------------------------
The following example is similar to the previous one, but HTTP Digest authentication is used::
from flask import Flask
from flask_httpauth import HTTPDigestAuth
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret key here'
auth = HTTPDigestAuth()
users = {
"john": "hello",
"susan": "bye"
}
@auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
@app.route('/')
@auth.login_required
def index():
return "Hello, %s!" % auth.username()
if __name__ == '__main__':
app.run()
Security Concerns with Digest Authentication
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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.
As an alternative to using server-side sessions, an application can implement its own generation and storage of challenge data. To do this, there are four callback functions that the application needs to implement::
@auth.generate_nonce
def generate_nonce():
"""Return the nonce value to use for this client."""
pass
@auth.generate_opaque
def generate_opaque():
"""Return the opaque value to use for this client."""
pass
@auth.verify_nonce
def verify_nonce(nonce):
"""Verify that the nonce value sent by the client is correct."""
pass
@auth.verify_opaque
def verify_opaque(opaque):
"""Verify that the opaque value sent by the client is correct."""
pass
For information of what the ``nonce`` and ``opaque`` values are and how they are used in digest authentication, consult `RFC 2617 <http://tools.ietf.org/html/rfc2617#section-3.2.1>`_.
Token Authentication Scheme Example
-----------------------------------
The following example application uses a custom HTTP authentication scheme to protect route ``'/'`` with a token::
from flask import Flask, g
from flask_httpauth import HTTPTokenAuth
app = Flask(__name__)
auth = HTTPTokenAuth(scheme='Token')
tokens = {
"secret-token-1": "john",
"secret-token-2": "susan"
}
@auth.verify_token
def verify_token(token):
if token in tokens:
g.current_user = tokens[token]
return True
return False
@app.route('/')
@auth.login_required
def index():
return "Hello, %s!" % g.current_user
if __name__ == '__main__':
app.run()
The ``HTTPTokenAuth`` is a generic authentication handler that can be used with non-standard authentication schemes, with the scheme name given as an argument in the constructor. In the above example, the ``WWW-Authenticate`` header provided by the server will use ``Token`` as scheme::
WWW-Authenticate: Token realm="Authentication Required"
The ``verify_token`` callback receives the authentication credentials provided by the client on the ``Authorization`` header. This can be a simple token, or can contain multiple arguments, which the function will have to parse and extract from the string.
In the examples directory you can find a complete example that uses JWT tokens.
Using Multiple Authentication Schemes
-------------------------------------
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.
In the examples directory you can find a complete example that uses basic and token authentication.
Deployment Considerations
-------------------------
Be aware that some web servers do not pass the ``Authorization`` headers to the WSGI application by default. For example, if you use Apache with mod_wsgi, you have to set option ``WSGIPassAuthorization On`` as `documented here <https://code.google.com/p/modwsgi/wiki/ConfigurationDirectives#WSGIPassAuthorization/>`_.
API Documentation
-----------------
.. module:: flask_httpauth
.. class:: HTTPBasicAuth
This class handles HTTP Basic authentication for Flask routes.
.. method:: __init__(scheme=None, realm=None)
Create a basic authentication object.
If the optional ``scheme`` argument is provided, it will be used instead of the standard "Basic" scheme in the ``WWW-Authenticate`` response. A fairly common practice is to use a custom scheme to prevent browsers from prompting the user to login.
The ``realm`` argument can be used to provide an application defined realm with the ``WWW-Authenticate`` header.
.. method:: get_password(password_callback)
This callback function will be called by the framework to obtain the password for a given user. Example::
@auth.get_password
def get_password(username):
return db.get_user_password(username)
.. method:: hash_password(hash_password_callback)
If defined, this callback function will be called by the framework to apply a custom hashing algorithm to the password provided by the client. If this callback isn't provided the password will be checked unchanged. The callback can take one or two arguments. The one argument version receives the password to hash, while the two argument version receives the username and the password in that order. Example single argument callback::
@auth.hash_password
def hash_password(password):
return md5(password).hexdigest()
Example two argument callback::
@auth.hash_password
def hash_pw(username, password):
salt = get_salt(username)
return hash(password, salt)
.. method:: verify_password(verify_password_callback)
If defined, this callback function will be called by the framework to verify that the username and password combination provided by the client are valid. The callback function takes two arguments, the username and the password and must return ``True`` or ``False``. Example usage::
@auth.verify_password
def verify_password(username, password):
user = User.query.filter_by(username).first()
if not user:
return False
return passlib.hash.sha256_crypt.verify(password, user.password_hash)
If this callback is defined, it is also invoked when the request does not have the ``Authorization`` header with user credentials, and in this case both the ``username`` and ``password`` arguments are set to empty strings. The client can opt to return ``True`` and that will allow anonymous users access to the route. The callback function can indicate that the user is anonymous by writing a state variable to ``flask.g``, which the route can then check to generate an appropriate response.
Note that when a ``verify_password`` callback is provided the ``get_password`` and ``hash_password`` callbacks are not used.
.. method:: error_handler(error_callback)
If defined, this callback function will be called by the framework when it is necessary to send an authentication error back to the client. The return value from this function can be the body of the response as a string or it can also be a response object created with ``make_response``. If this callback isn't provided a default error response is generated. Example::
@auth.error_handler
def auth_error():
return "<h1>Access Denied</h1>"
.. method:: login_required(view_function_callback)
This callback function will be called when authentication is successful. This will typically be a Flask view function. Example::
@app.route('/private')
@auth.login_required
def private_page():
return "Only for authorized people!"
.. method:: username()
A view function that is protected with this class can access the logged username through this method. Example::
@app.route('/')
@auth.login_required
def index():
return "Hello, %s!" % auth.username()
.. class:: flask_httpauth.HTTPDigestAuth
This class handles HTTP Digest authentication for Flask routes. The ``SECRET_KEY`` configuration must be set in the Flask application to enable the session to work. Flask by default stores user sessions in the client as secure cookies, so the client must be able to handle cookies. To support clients that are not web browsers or that cannot handle cookies a `session interface <http://flask.pocoo.org/docs/api/#flask.Flask.session_interface>`_ that writes sessions in the server must be used.
.. method:: __init__(self, scheme=None, realm=None, use_ha1_pw=False)
Create a digest authentication object.
If the optional ``scheme`` argument is provided, it will be used instead of the "Digest" scheme in the ``WWW-Authenticate`` response. A fairly common practice is to use a custom scheme to prevent browsers from prompting the user to login.
The ``realm`` argument can be used to provide an application defined realm with the ``WWW-Authenticate`` header.
If ``use_ha1_pw`` is False, then the ``get_password`` callback needs to return the plain text password for the given user. If ``use_ha1_pw`` is True, the ``get_password`` callback needs to return the HA1 value for the given user. The advantage of setting ``use_ha1_pw`` to ``True`` is that it allows the application to store the HA1 hash of the password in the user database.
.. method:: generate_ha1(username, password)
Generate the HA1 hash that can be stored in the user database when ``use_ha1_pw`` is set to True in the constructor.
.. method:: generate_nonce(nonce_making_callback)
If defined, this callback function will be called by the framework to
generate a nonce. If this is defined, ``verify_nonce`` should
also be defined.
This can be used to use a state storage mechanism other than the session.
.. method:: verify_nonce(nonce_verify_callback)
If defined, this callback function will be called by the framework to
verify that a nonce is valid. It will be called with a single argument:
the nonce to be verified.
This can be used to use a state storage mechanism other than the session.
.. method:: generate_opaque(opaque_making_callback)
If defined, this callback function will be called by the framework to
generate an opaque value. If this is defined, ``verify_opaque`` should
also be defined.
This can be used to use a state storage mechanism other than the session.
.. method:: verify_opaque(opaque_verify_callback)
If defined, this callback function will be called by the framework to
verify that an opaque value is valid. It will be called with a single
argument: the opaque value to be verified.
This can be used to use a state storage mechanism other than the session.
.. method:: get_password(password_callback)
See basic authentication for documentation and examples.
.. method:: error_handler(error_callback)
See basic authentication for documentation and examples.
.. method:: login_required(view_function_callback)
See basic authentication for documentation and examples.
.. method:: username()
See basic authentication for documentation and examples.
.. class:: HTTPTokenAuth
This class handles HTTP authentication with custom schemes for Flask routes.
.. method:: __init__(scheme, realm=None)
Create a token authentication object.
The ``scheme`` argument must be provided to be used in the ``WWW-Authenticate`` response.
The ``realm`` argument can be used to provide an application defined realm with the ``WWW-Authenticate`` header.
.. method:: verify_token(verify_token_callback)
This callback function will be called by the framework to verify that the credentials sent by the client with the ``Authorization`` header are valid. The callback function takes one argument, the username and the password and must return ``True`` or ``False``. Example usage::
@auth.verify_token
def verify_token(token):
g.current_user = User.query.filter_by(token=token).first()
return g.current_user is not None
Note that a ``verify_token`` callback is required when using this class.
.. method:: error_handler(error_callback)
See basic authentication for documentation and examples.
.. method:: login_required(view_function_callback)
See basic authentication for documentation and examples.
|