Secure Links From Flask Security
So, Flask-Security is a really great tool1!
I use it extensively for all things authentication, authorization, role management, user management,… you name it, Flask-Security has it!
One of the features I really like is, that Flask-Security makes it easy to setup a registration flow for flask apps. It provides ready made endpoint for registration, login, recover password out of the box and the templates provide the most necessary elements. It’s even possible to require confirmation via e-mail after registering a new account. I.e. Flask-Security will send out a one time confirmation link to the provided e-mail. If it is not clicked/opened the account will remain in an inactive state.
It’s as simple as setting these variables for your flask app:
1SECURITY_REGISTERABLE = True # enables the registration endpoint
2SECURITY_CONFIRMABLE = True # sends out confirmation link to provided e-mail
However, how this link is generated is not determined by a Flask-Security setting. Simply using these two values, will enable the flow as described, but the link sent out to the new user will be a http:// (without the ’s’!) link. Of course, the production server should be set up to redirect http:// to its secure form https://. This may cause some problems, when the recipient is behind an (over-eager?) spam filter2.
The solution is not found solely within Flask-Security. Making Flask-Security and therefore flask generate secure links is based on a flask variable! The variable in question is PREFERRED_URL_SCHEME. As of publication of this post, the official flask documentation states:
PREFERRED_URL_SCHEME
Use this scheme for generating external URLs when not in a request context.
Default: ‘http’
Setting it like this in a flask app’s environment will trigger Flask-Security confirmation links to be https://, that is secure links:
1SECURITY_REGISTERABLE = True # enables the registration endpoint
2SECURITY_CONFIRMABLE = True # sends out confirmation link to provided e-mail
3PREFERRED_URL_SCHEME = "https" # generated links will use https
Flask-Security is leveraging Flask’s own url_for() function.
You can see this in action in its flask-security/confirmable.py module:
1def generate_confirmation_link(user):
2 token = generate_confirmation_token(user)
3 return url_for_security("confirm_email", token=token, _external=True), token
See: flask-security/confirmable.py Line 27
The return value is actually returning a function: url_for_security() (we’re getting closer and closer to the source!)
1def url_for_security(endpoint: str, **values: t.Any) -> str:
2 """Return a URL for the security blueprint
3
4 :param endpoint: the endpoint of the URL (name of the function)
5 :param values: the variable arguments of the URL rule
6 :param _external: if set to `True`, an absolute URL is generated. Server
7 address can be changed via `SERVER_NAME` configuration variable which
8 defaults to `localhost`.
9 :param _anchor: if provided, this is added as anchor to the URL.
10 :param _method: if provided, this explicitly specifies an HTTP method.
11 """
12 endpoint = get_security_endpoint_name(endpoint)
13 # mypy is complaining about this - but I think it's wrong?
14 return url_for(endpoint, **values) # type: ignore
See: flask-security/utils.py Line 662
See that return value? It’s url_for() from Flask! You can see the full function over at github: flask/src/flask/helpers.py Line 187