fief_client.integrations.flask
Flask integration.
1"""Flask integration.""" 2import uuid 3from functools import wraps 4from typing import Callable, List, Optional 5 6from flask import g, request 7 8from fief_client import ( 9 Fief, 10 FiefAccessTokenACRTooLow, 11 FiefAccessTokenExpired, 12 FiefAccessTokenInfo, 13 FiefAccessTokenInvalid, 14 FiefAccessTokenMissingScope, 15 FiefACR, 16 FiefUserInfo, 17) 18from fief_client.client import FiefAccessTokenMissingPermission 19 20 21class FiefAuthError(Exception): 22 """ 23 Base error for FiefAuth integration. 24 """ 25 26 27class FiefAuthUnauthorized(FiefAuthError): 28 """ 29 Request unauthorized error. 30 31 This error is raised when using the `authenticated` or `current_user` decorator 32 but the request is not authenticated. 33 34 You should implement an `errorhandler` to define the behavior of your server when 35 this happens. 36 37 **Example:** 38 39 ```py 40 @app.errorhandler(FiefAuthUnauthorized) 41 def fief_unauthorized_error(e): 42 return "", 401 43 ``` 44 """ 45 46 47class FiefAuthForbidden(FiefAuthError): 48 """ 49 Request forbidden error. 50 51 This error is raised when using the `authenticated` or `current_user` decorator 52 but the access token doesn't match the list of scopes, permissions or minimum ACR level. 53 54 You should implement an `errorhandler` to define the behavior of your server when 55 this happens. 56 57 **Example:** 58 59 ```py 60 @app.errorhandler(FiefAuthForbidden) 61 def fief_forbidden_error(e): 62 return "", 403 63 ``` 64 """ 65 66 67TokenGetter = Callable[[], Optional[str]] 68"""Type of a function that can be used to retrieve a token.""" 69 70UserInfoCacheGetter = Callable[[uuid.UUID], Optional[FiefUserInfo]] 71""" 72Type of a function that can be used to retrieve user information from a cache. 73 74Read more: https://docs.fief.dev/integrate/python/flask/#web-application-example 75""" 76 77UserInfoCacheSetter = Callable[[uuid.UUID, FiefUserInfo], None] 78""" 79Type of a function that can be used to store user information in a cache. 80 81Read more: https://docs.fief.dev/integrate/python/flask/#web-application-example 82""" 83 84 85def get_authorization_scheme_token(*, scheme: str = "bearer") -> TokenGetter: 86 """ 87 Return a `TokenGetter` function to retrieve a token from the `Authorization` header of an HTTP request. 88 89 :param scheme: Scheme of the token. Defaults to `bearer`. 90 """ 91 92 def _get_authorization_scheme_token(): 93 authorization = request.headers.get("Authorization") 94 if authorization is None: 95 return None 96 parts = authorization.split() 97 if len(parts) != 2 or parts[0].lower() != scheme.lower(): 98 return None 99 return parts[1] 100 101 return _get_authorization_scheme_token 102 103 104def get_cookie(cookie_name: str) -> TokenGetter: 105 """ 106 Return a `TokenGetter` function to retrieve a token from a `Cookie` of an HTTP request. 107 108 :param cookie_name: Name of the cookie. 109 """ 110 111 def _get_cookie(): 112 return request.cookies.get(cookie_name) 113 114 return _get_cookie 115 116 117class FiefAuth: 118 """ 119 Helper class to integrate Fief authentication with Flask. 120 121 **Example:** 122 123 ```py 124 from fief_client import Fief 125 from fief_client.integrations.flask import ( 126 FiefAuth, 127 get_authorization_scheme_token, 128 ) 129 from flask import Flask, g 130 131 fief = Fief( 132 "https://example.fief.dev", 133 "YOUR_CLIENT_ID", 134 "YOUR_CLIENT_SECRET", 135 ) 136 137 auth = FiefAuth(fief, get_authorization_scheme_token()) 138 139 app = Flask(__name__) 140 ``` 141 """ 142 143 def __init__( 144 self, 145 client: Fief, 146 token_getter: TokenGetter, 147 *, 148 get_userinfo_cache: Optional[UserInfoCacheGetter] = None, 149 set_userinfo_cache: Optional[UserInfoCacheSetter] = None, 150 ) -> None: 151 """ 152 :param client: Instance of a `fief_client.Fief` client. 153 :param token_getter: Function to retrieve a token. 154 It should follow the `TokenGetter` type. 155 :param get_userinfo_cache: Optional function to retrieve user information from a cache. 156 Otherwise, the Fief API will always be reached when requesting user information. 157 It should follow the `UserInfoCacheGetter` type. 158 :param set_userinfo_cache: Optional function to store user information in a cache. 159 It should follow the `UserInfoCacheSetter` type. 160 """ 161 self.client = client 162 self.token_getter = token_getter 163 self.get_userinfo_cache = get_userinfo_cache 164 self.set_userinfo_cache = set_userinfo_cache 165 166 def authenticated( 167 self, 168 *, 169 optional: bool = False, 170 scope: Optional[List[str]] = None, 171 acr: Optional[FiefACR] = None, 172 permissions: Optional[List[str]] = None, 173 ): 174 """ 175 Decorator to check if a request is authenticated. 176 177 If the request is authenticated, the `g` object will have an `access_token_info` property, 178 of type `fief_client.FiefAccessTokenInfo`. 179 180 :param optional: If `False` and the request is not authenticated, 181 a `FiefAuthUnauthorized` error will be raised. 182 :param scope: Optional list of scopes required. 183 If the access token lacks one of the required scope, a `FiefAuthForbidden` error will be raised. 184 :param acr: Optional minimum ACR level required. 185 If the access token doesn't meet the minimum level, a `FiefAuthForbidden` error will be raised. 186 Read more: https://docs.fief.dev/going-further/acr/ 187 :param permissions: Optional list of permissions required. 188 If the access token lacks one of the required permission, a `FiefAuthForbidden` error will be raised. 189 190 **Example** 191 192 ```py 193 @app.get("/authenticated") 194 @auth.authenticated() 195 def get_authenticated(): 196 return g.access_token_info 197 ``` 198 """ 199 200 def _authenticated(f): 201 @wraps(f) 202 def decorated_function(*args, **kwargs): 203 token = self.token_getter() 204 if token is None: 205 if optional: 206 g.access_token_info = None 207 return f(*args, **kwargs) 208 raise FiefAuthUnauthorized() 209 210 try: 211 info = self.client.validate_access_token( 212 token, 213 required_scope=scope, 214 required_acr=acr, 215 required_permissions=permissions, 216 ) 217 except (FiefAccessTokenInvalid, FiefAccessTokenExpired) as e: 218 if optional: 219 g.access_token_info = None 220 return f(*args, **kwargs) 221 raise FiefAuthUnauthorized() from e 222 except ( 223 FiefAccessTokenMissingScope, 224 FiefAccessTokenACRTooLow, 225 FiefAccessTokenMissingPermission, 226 ) as e: 227 raise FiefAuthForbidden() from e 228 229 g.access_token_info = info 230 231 return f(*args, **kwargs) 232 233 return decorated_function 234 235 return _authenticated 236 237 def current_user( 238 self, 239 *, 240 optional: bool = False, 241 scope: Optional[List[str]] = None, 242 acr: Optional[FiefACR] = None, 243 permissions: Optional[List[str]] = None, 244 refresh: bool = False, 245 ): 246 """ 247 Decorator to check if a user is authenticated. 248 249 If the request is authenticated, the `g` object will have a `user` property, 250 of type `fief_client.FiefUserInfo`. 251 252 :param optional: If `False` and the request is not authenticated, 253 a `FiefAuthUnauthorized` error will be raised. 254 :param scope: Optional list of scopes required. 255 If the access token lacks one of the required scope, a `FiefAuthForbidden` error will be raised. 256 :param acr: Optional minimum ACR level required. 257 If the access token doesn't meet the minimum level, a `FiefAuthForbidden` error will be raised. 258 Read more: https://docs.fief.dev/going-further/acr/ 259 :param permissions: Optional list of permissions required. 260 If the access token lacks one of the required permission, a `FiefAuthForbidden` error will be raised. 261 :param refresh: If `True`, the user information will be refreshed from the Fief API. 262 Otherwise, the cache will be used. 263 264 **Example** 265 266 ```py 267 @app.get("/current-user") 268 @auth.current_user() 269 def get_current_user(): 270 user = g.user 271 return f"<h1>You are authenticated. Your user email is {user['email']}</h1>" 272 ``` 273 """ 274 275 def _current_user(f): 276 @wraps(f) 277 @self.authenticated( 278 optional=optional, scope=scope, acr=acr, permissions=permissions 279 ) 280 def decorated_function(*args, **kwargs): 281 access_token_info: Optional[FiefAccessTokenInfo] = g.access_token_info 282 283 if access_token_info is None and optional: 284 g.user = None 285 return f(*args, **kwargs) 286 287 assert access_token_info is not None 288 289 userinfo = None 290 if self.get_userinfo_cache is not None: 291 userinfo = self.get_userinfo_cache(access_token_info["id"]) 292 293 if userinfo is None or refresh: 294 userinfo = self.client.userinfo(access_token_info["access_token"]) 295 296 if self.set_userinfo_cache is not None: 297 self.set_userinfo_cache(access_token_info["id"], userinfo) 298 299 g.user = userinfo 300 301 return f(*args, **kwargs) 302 303 return decorated_function 304 305 return _current_user 306 307 308__all__ = [ 309 "FiefAuth", 310 "FiefAuthError", 311 "FiefAuthUnauthorized", 312 "FiefAuthForbidden", 313 "TokenGetter", 314 "UserInfoCacheGetter", 315 "UserInfoCacheSetter", 316 "get_authorization_scheme_token", 317 "get_cookie", 318]
118class FiefAuth: 119 """ 120 Helper class to integrate Fief authentication with Flask. 121 122 **Example:** 123 124 ```py 125 from fief_client import Fief 126 from fief_client.integrations.flask import ( 127 FiefAuth, 128 get_authorization_scheme_token, 129 ) 130 from flask import Flask, g 131 132 fief = Fief( 133 "https://example.fief.dev", 134 "YOUR_CLIENT_ID", 135 "YOUR_CLIENT_SECRET", 136 ) 137 138 auth = FiefAuth(fief, get_authorization_scheme_token()) 139 140 app = Flask(__name__) 141 ``` 142 """ 143 144 def __init__( 145 self, 146 client: Fief, 147 token_getter: TokenGetter, 148 *, 149 get_userinfo_cache: Optional[UserInfoCacheGetter] = None, 150 set_userinfo_cache: Optional[UserInfoCacheSetter] = None, 151 ) -> None: 152 """ 153 :param client: Instance of a `fief_client.Fief` client. 154 :param token_getter: Function to retrieve a token. 155 It should follow the `TokenGetter` type. 156 :param get_userinfo_cache: Optional function to retrieve user information from a cache. 157 Otherwise, the Fief API will always be reached when requesting user information. 158 It should follow the `UserInfoCacheGetter` type. 159 :param set_userinfo_cache: Optional function to store user information in a cache. 160 It should follow the `UserInfoCacheSetter` type. 161 """ 162 self.client = client 163 self.token_getter = token_getter 164 self.get_userinfo_cache = get_userinfo_cache 165 self.set_userinfo_cache = set_userinfo_cache 166 167 def authenticated( 168 self, 169 *, 170 optional: bool = False, 171 scope: Optional[List[str]] = None, 172 acr: Optional[FiefACR] = None, 173 permissions: Optional[List[str]] = None, 174 ): 175 """ 176 Decorator to check if a request is authenticated. 177 178 If the request is authenticated, the `g` object will have an `access_token_info` property, 179 of type `fief_client.FiefAccessTokenInfo`. 180 181 :param optional: If `False` and the request is not authenticated, 182 a `FiefAuthUnauthorized` error will be raised. 183 :param scope: Optional list of scopes required. 184 If the access token lacks one of the required scope, a `FiefAuthForbidden` error will be raised. 185 :param acr: Optional minimum ACR level required. 186 If the access token doesn't meet the minimum level, a `FiefAuthForbidden` error will be raised. 187 Read more: https://docs.fief.dev/going-further/acr/ 188 :param permissions: Optional list of permissions required. 189 If the access token lacks one of the required permission, a `FiefAuthForbidden` error will be raised. 190 191 **Example** 192 193 ```py 194 @app.get("/authenticated") 195 @auth.authenticated() 196 def get_authenticated(): 197 return g.access_token_info 198 ``` 199 """ 200 201 def _authenticated(f): 202 @wraps(f) 203 def decorated_function(*args, **kwargs): 204 token = self.token_getter() 205 if token is None: 206 if optional: 207 g.access_token_info = None 208 return f(*args, **kwargs) 209 raise FiefAuthUnauthorized() 210 211 try: 212 info = self.client.validate_access_token( 213 token, 214 required_scope=scope, 215 required_acr=acr, 216 required_permissions=permissions, 217 ) 218 except (FiefAccessTokenInvalid, FiefAccessTokenExpired) as e: 219 if optional: 220 g.access_token_info = None 221 return f(*args, **kwargs) 222 raise FiefAuthUnauthorized() from e 223 except ( 224 FiefAccessTokenMissingScope, 225 FiefAccessTokenACRTooLow, 226 FiefAccessTokenMissingPermission, 227 ) as e: 228 raise FiefAuthForbidden() from e 229 230 g.access_token_info = info 231 232 return f(*args, **kwargs) 233 234 return decorated_function 235 236 return _authenticated 237 238 def current_user( 239 self, 240 *, 241 optional: bool = False, 242 scope: Optional[List[str]] = None, 243 acr: Optional[FiefACR] = None, 244 permissions: Optional[List[str]] = None, 245 refresh: bool = False, 246 ): 247 """ 248 Decorator to check if a user is authenticated. 249 250 If the request is authenticated, the `g` object will have a `user` property, 251 of type `fief_client.FiefUserInfo`. 252 253 :param optional: If `False` and the request is not authenticated, 254 a `FiefAuthUnauthorized` error will be raised. 255 :param scope: Optional list of scopes required. 256 If the access token lacks one of the required scope, a `FiefAuthForbidden` error will be raised. 257 :param acr: Optional minimum ACR level required. 258 If the access token doesn't meet the minimum level, a `FiefAuthForbidden` error will be raised. 259 Read more: https://docs.fief.dev/going-further/acr/ 260 :param permissions: Optional list of permissions required. 261 If the access token lacks one of the required permission, a `FiefAuthForbidden` error will be raised. 262 :param refresh: If `True`, the user information will be refreshed from the Fief API. 263 Otherwise, the cache will be used. 264 265 **Example** 266 267 ```py 268 @app.get("/current-user") 269 @auth.current_user() 270 def get_current_user(): 271 user = g.user 272 return f"<h1>You are authenticated. Your user email is {user['email']}</h1>" 273 ``` 274 """ 275 276 def _current_user(f): 277 @wraps(f) 278 @self.authenticated( 279 optional=optional, scope=scope, acr=acr, permissions=permissions 280 ) 281 def decorated_function(*args, **kwargs): 282 access_token_info: Optional[FiefAccessTokenInfo] = g.access_token_info 283 284 if access_token_info is None and optional: 285 g.user = None 286 return f(*args, **kwargs) 287 288 assert access_token_info is not None 289 290 userinfo = None 291 if self.get_userinfo_cache is not None: 292 userinfo = self.get_userinfo_cache(access_token_info["id"]) 293 294 if userinfo is None or refresh: 295 userinfo = self.client.userinfo(access_token_info["access_token"]) 296 297 if self.set_userinfo_cache is not None: 298 self.set_userinfo_cache(access_token_info["id"], userinfo) 299 300 g.user = userinfo 301 302 return f(*args, **kwargs) 303 304 return decorated_function 305 306 return _current_user
Helper class to integrate Fief authentication with Flask.
Example:
from fief_client import Fief
from fief_client.integrations.flask import (
FiefAuth,
get_authorization_scheme_token,
)
from flask import Flask, g
fief = Fief(
"https://example.fief.dev",
"YOUR_CLIENT_ID",
"YOUR_CLIENT_SECRET",
)
auth = FiefAuth(fief, get_authorization_scheme_token())
app = Flask(__name__)
144 def __init__( 145 self, 146 client: Fief, 147 token_getter: TokenGetter, 148 *, 149 get_userinfo_cache: Optional[UserInfoCacheGetter] = None, 150 set_userinfo_cache: Optional[UserInfoCacheSetter] = None, 151 ) -> None: 152 """ 153 :param client: Instance of a `fief_client.Fief` client. 154 :param token_getter: Function to retrieve a token. 155 It should follow the `TokenGetter` type. 156 :param get_userinfo_cache: Optional function to retrieve user information from a cache. 157 Otherwise, the Fief API will always be reached when requesting user information. 158 It should follow the `UserInfoCacheGetter` type. 159 :param set_userinfo_cache: Optional function to store user information in a cache. 160 It should follow the `UserInfoCacheSetter` type. 161 """ 162 self.client = client 163 self.token_getter = token_getter 164 self.get_userinfo_cache = get_userinfo_cache 165 self.set_userinfo_cache = set_userinfo_cache
Parameters
- client: Instance of a
fief_client.Fief
client. - token_getter: Function to retrieve a token.
It should follow the
TokenGetter
type. - get_userinfo_cache: Optional function to retrieve user information from a cache.
Otherwise, the Fief API will always be reached when requesting user information.
It should follow the
UserInfoCacheGetter
type. - set_userinfo_cache: Optional function to store user information in a cache.
It should follow the
UserInfoCacheSetter
type.
167 def authenticated( 168 self, 169 *, 170 optional: bool = False, 171 scope: Optional[List[str]] = None, 172 acr: Optional[FiefACR] = None, 173 permissions: Optional[List[str]] = None, 174 ): 175 """ 176 Decorator to check if a request is authenticated. 177 178 If the request is authenticated, the `g` object will have an `access_token_info` property, 179 of type `fief_client.FiefAccessTokenInfo`. 180 181 :param optional: If `False` and the request is not authenticated, 182 a `FiefAuthUnauthorized` error will be raised. 183 :param scope: Optional list of scopes required. 184 If the access token lacks one of the required scope, a `FiefAuthForbidden` error will be raised. 185 :param acr: Optional minimum ACR level required. 186 If the access token doesn't meet the minimum level, a `FiefAuthForbidden` error will be raised. 187 Read more: https://docs.fief.dev/going-further/acr/ 188 :param permissions: Optional list of permissions required. 189 If the access token lacks one of the required permission, a `FiefAuthForbidden` error will be raised. 190 191 **Example** 192 193 ```py 194 @app.get("/authenticated") 195 @auth.authenticated() 196 def get_authenticated(): 197 return g.access_token_info 198 ``` 199 """ 200 201 def _authenticated(f): 202 @wraps(f) 203 def decorated_function(*args, **kwargs): 204 token = self.token_getter() 205 if token is None: 206 if optional: 207 g.access_token_info = None 208 return f(*args, **kwargs) 209 raise FiefAuthUnauthorized() 210 211 try: 212 info = self.client.validate_access_token( 213 token, 214 required_scope=scope, 215 required_acr=acr, 216 required_permissions=permissions, 217 ) 218 except (FiefAccessTokenInvalid, FiefAccessTokenExpired) as e: 219 if optional: 220 g.access_token_info = None 221 return f(*args, **kwargs) 222 raise FiefAuthUnauthorized() from e 223 except ( 224 FiefAccessTokenMissingScope, 225 FiefAccessTokenACRTooLow, 226 FiefAccessTokenMissingPermission, 227 ) as e: 228 raise FiefAuthForbidden() from e 229 230 g.access_token_info = info 231 232 return f(*args, **kwargs) 233 234 return decorated_function 235 236 return _authenticated
Decorator to check if a request is authenticated.
If the request is authenticated, the g
object will have an access_token_info
property,
of type fief_client.FiefAccessTokenInfo
.
Parameters
- optional: If
False
and the request is not authenticated, aFiefAuthUnauthorized
error will be raised. - scope: Optional list of scopes required.
If the access token lacks one of the required scope, a
FiefAuthForbidden
error will be raised. - acr: Optional minimum ACR level required.
If the access token doesn't meet the minimum level, a
FiefAuthForbidden
error will be raised. Read more: https://docs.fief.dev/going-further/acr/ - permissions: Optional list of permissions required.
If the access token lacks one of the required permission, a
FiefAuthForbidden
error will be raised.
Example
@app.get("/authenticated")
@auth.authenticated()
def get_authenticated():
return g.access_token_info
238 def current_user( 239 self, 240 *, 241 optional: bool = False, 242 scope: Optional[List[str]] = None, 243 acr: Optional[FiefACR] = None, 244 permissions: Optional[List[str]] = None, 245 refresh: bool = False, 246 ): 247 """ 248 Decorator to check if a user is authenticated. 249 250 If the request is authenticated, the `g` object will have a `user` property, 251 of type `fief_client.FiefUserInfo`. 252 253 :param optional: If `False` and the request is not authenticated, 254 a `FiefAuthUnauthorized` error will be raised. 255 :param scope: Optional list of scopes required. 256 If the access token lacks one of the required scope, a `FiefAuthForbidden` error will be raised. 257 :param acr: Optional minimum ACR level required. 258 If the access token doesn't meet the minimum level, a `FiefAuthForbidden` error will be raised. 259 Read more: https://docs.fief.dev/going-further/acr/ 260 :param permissions: Optional list of permissions required. 261 If the access token lacks one of the required permission, a `FiefAuthForbidden` error will be raised. 262 :param refresh: If `True`, the user information will be refreshed from the Fief API. 263 Otherwise, the cache will be used. 264 265 **Example** 266 267 ```py 268 @app.get("/current-user") 269 @auth.current_user() 270 def get_current_user(): 271 user = g.user 272 return f"<h1>You are authenticated. Your user email is {user['email']}</h1>" 273 ``` 274 """ 275 276 def _current_user(f): 277 @wraps(f) 278 @self.authenticated( 279 optional=optional, scope=scope, acr=acr, permissions=permissions 280 ) 281 def decorated_function(*args, **kwargs): 282 access_token_info: Optional[FiefAccessTokenInfo] = g.access_token_info 283 284 if access_token_info is None and optional: 285 g.user = None 286 return f(*args, **kwargs) 287 288 assert access_token_info is not None 289 290 userinfo = None 291 if self.get_userinfo_cache is not None: 292 userinfo = self.get_userinfo_cache(access_token_info["id"]) 293 294 if userinfo is None or refresh: 295 userinfo = self.client.userinfo(access_token_info["access_token"]) 296 297 if self.set_userinfo_cache is not None: 298 self.set_userinfo_cache(access_token_info["id"], userinfo) 299 300 g.user = userinfo 301 302 return f(*args, **kwargs) 303 304 return decorated_function 305 306 return _current_user
Decorator to check if a user is authenticated.
If the request is authenticated, the g
object will have a user
property,
of type fief_client.FiefUserInfo
.
Parameters
- optional: If
False
and the request is not authenticated, aFiefAuthUnauthorized
error will be raised. - scope: Optional list of scopes required.
If the access token lacks one of the required scope, a
FiefAuthForbidden
error will be raised. - acr: Optional minimum ACR level required.
If the access token doesn't meet the minimum level, a
FiefAuthForbidden
error will be raised. Read more: https://docs.fief.dev/going-further/acr/ - permissions: Optional list of permissions required.
If the access token lacks one of the required permission, a
FiefAuthForbidden
error will be raised. - refresh: If
True
, the user information will be refreshed from the Fief API. Otherwise, the cache will be used.
Example
@app.get("/current-user")
@auth.current_user()
def get_current_user():
user = g.user
return f"<h1>You are authenticated. Your user email is {user['email']}</h1>"
Base error for FiefAuth integration.
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- args
48class FiefAuthForbidden(FiefAuthError): 49 """ 50 Request forbidden error. 51 52 This error is raised when using the `authenticated` or `current_user` decorator 53 but the access token doesn't match the list of scopes, permissions or minimum ACR level. 54 55 You should implement an `errorhandler` to define the behavior of your server when 56 this happens. 57 58 **Example:** 59 60 ```py 61 @app.errorhandler(FiefAuthForbidden) 62 def fief_forbidden_error(e): 63 return "", 403 64 ``` 65 """
Request forbidden error.
This error is raised when using the authenticated
or current_user
decorator
but the access token doesn't match the list of scopes, permissions or minimum ACR level.
You should implement an errorhandler
to define the behavior of your server when
this happens.
Example:
@app.errorhandler(FiefAuthForbidden)
def fief_forbidden_error(e):
return "", 403
Inherited Members
- builtins.Exception
- Exception
- builtins.BaseException
- with_traceback
- args
Type of a function that can be used to retrieve a token.
Type of a function that can be used to retrieve user information from a cache.
Read more: https://docs.fief.dev/integrate/python/flask/#web-application-example
Type of a function that can be used to store user information in a cache.
Read more: https://docs.fief.dev/integrate/python/flask/#web-application-example