Flask¶
Flask is probably the most popular Python web framework.
The Fief Python client provides tools to help you integrate Fief authentication in your Flask project. Let's see how to use them!
Install the client¶
Install the Fief client with the optional Flask dependencies:
API example¶
This is for you if...
- Your Flask backend will work as a pure REST API.
- You have a separate frontend, like a JavaScript or mobile app, that'll take care of the OAuth2 flow.
In this first example, we won't implement routes to perform the OAuth2 authentication. The goal here is just to show you how to protect your API route with a Fief access token.
from fief_client import Fief
from fief_client.integrations.flask import (
FiefAuth,
FiefAuthForbidden,
FiefAuthUnauthorized,
get_authorization_scheme_token,
)
from flask import Flask, g
fief = Fief(
"https://fief.mydomain.com",
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET",
)
auth = FiefAuth(fief, get_authorization_scheme_token())
app = Flask(__name__)
@app.errorhandler(FiefAuthUnauthorized)
def fief_unauthorized_error(e):
return "", 401
@app.errorhandler(FiefAuthForbidden)
def fief_forbidden_error(e):
return "", 403
@app.get("/authenticated")
@auth.authenticated()
def get_authenticated():
return g.access_token_info
@app.get("/authenticated-scope")
@auth.authenticated(scope=["openid", "required_scope"])
def get_authenticated_scope():
return g.access_token_info
@app.get("/authenticated-permissions")
@auth.authenticated(permissions=["castles:read"])
def get_authenticated_permissions():
return g.access_token_info
And that's about it!
Optional user¶
Sometimes, you need to have a route retrieving the user if there is one authenticated, but still working if there none. To do this, you can leverage the optional
parameter of the authenticated
decorator.
from fief_client import Fief
from fief_client.integrations.flask import (
FiefAuth,
FiefAuthForbidden,
FiefAuthUnauthorized,
get_authorization_scheme_token,
)
from flask import Flask, g
fief = Fief(
"https://fief.mydomain.com",
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET",
)
auth = FiefAuth(fief, get_authorization_scheme_token())
app = Flask(__name__)
@app.errorhandler(FiefAuthUnauthorized)
def fief_unauthorized_error(e):
return "", 401
@app.errorhandler(FiefAuthForbidden)
def fief_forbidden_error(e):
return "", 403
@app.get("/authenticated")
@auth.authenticated(optional=True)
def get_authenticated():
if g.access_token_info is None:
return {"message": "Anonymous user"}
return g.access_token_info
Web application example¶
This is for you if...
- Your Flask backend will render HTML pages.
- Your application is intended to be used in a browser.
Prerequisites
- Allow the following Redirect URI on your Fief Client:
http://localhost:8000/auth-callback
The examples we showed previously are working well in a pure REST API context: a frontend, like interactive documentation, a JavaScript application or a mobile app will take care of the OAuth2 authentication flow to retrieve an access token before making request to your API.
Another common context is traditional web application, where the server takes care of generating HTML pages before returning it to the browser. In this case, we'll need some routes to redirect the user to the Fief login page if they're not authenticated and take care of storing the access token somewhere. This is what'll show in this example.
Besides, we'll usually need the basic information about the authenticated user, like its email or the values of the custom user fields. We'll see how we can use it.
Basically, here's what we'll do:
- This time, we'll expect the access token to be passed through a traditional cookie instead of an HTTP header. Cookies are very convenient when designing web apps because they are handled automatically by the browser.
- If the cookie is not present, we'll redirect the user to the Fief login page. Once again, the browser will help us a lot here since it'll automatically follow the redirection.
- Upon successful login, Fief will automatically redirect the user to the callback route. This callback route will take care of setting a new cookie containing the access token. It means that the access token will be safely stored in the browser memory.
- Finally, the user is redirected back to the protected route. The browser will automatically send the cookie containing the access token: our request is now authenticated!
import uuid
from typing import Optional
from fief_client import Fief, FiefUserInfo
from fief_client.integrations.flask import (
FiefAuth,
FiefAuthForbidden,
FiefAuthUnauthorized,
get_cookie,
)
from flask import Flask, g, redirect, request, session, url_for
SESSION_COOKIE_NAME = "user_session"
SECRET_KEY = "SECRET"
fief = Fief(
"https://fief.mydomain.com",
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET",
)
def get_userinfo_cache(id: uuid.UUID) -> Optional[FiefUserInfo]:
return session.get(f"userinfo-{str(id)}")
def set_userinfo_cache(id: uuid.UUID, userinfo: FiefUserInfo) -> None:
session[f"userinfo-{str(id)}"] = userinfo
auth = FiefAuth(
fief,
get_cookie(SESSION_COOKIE_NAME), #
get_userinfo_cache=get_userinfo_cache,
set_userinfo_cache=set_userinfo_cache,
)
app = Flask(__name__)
app.secret_key = SECRET_KEY
@app.errorhandler(FiefAuthUnauthorized)
def fief_unauthorized_error(e):
redirect_uri = url_for("auth_callback", _external=True)
auth_url = fief.auth_url(redirect_uri, scope=["openid"])
return redirect(auth_url)
@app.errorhandler(FiefAuthForbidden)
def fief_forbidden_error(e):
return "", 403
@app.get("/auth-callback")
def auth_callback():
redirect_uri = url_for("auth_callback", _external=True)
code = request.args["code"]
tokens, userinfo = fief.auth_callback(code, redirect_uri)
response = redirect(url_for("protected"))
response.set_cookie(
SESSION_COOKIE_NAME,
tokens["access_token"],
max_age=tokens["expires_in"],
httponly=True,
secure=False,
)
set_userinfo_cache(uuid.UUID(userinfo["sub"]), userinfo)
return response
@app.get("/protected")
@auth.current_user()
def protected():
user = g.user
return f"<h1>You are authenticated. Your user email is {user['email']}</h1>"
That's it! If you run this application and go to http://localhost:8000/protected, you'll be redirected to the Fief login page and experience the authentication flow before getting back to this route with a proper authentication cookie.
current_user
can also check for scope and permissions
In a similar way as we shown in the API example, you can also require the access token to be granted a list of scopes or the user to be granted a list of permissions.
You can also optionally require the user
In a similar way as we shown in the API example, you can leverage the optional
parameter to make the route works even if no user is authenticated.