[QUESTION] OAuth2: how to store the access_token in a cookie
See original GitHub issueFirst check
- I used the GitHub search to find a similar issue and didn’t find it.
- I searched the FastAPI documentation, with the integrated search.
- I already searched in Google “How to X in FastAPI” and didn’t find any information.
Description
How can I save the access_token
in a cookie in case of OAuth2 ?
When we do return {"access_token": access_token, "token_type": "bearer"}
and
we use the OAuth2PasswordBearerWithCookie
as oauth2_scheme
everything works based on OpenAPI client.
@app.post("/token", response_model=Token)
async def login_for_access_token(
form_data: fastapi.security.OAuth2PasswordRequestForm = fastapi.Depends(),
):
user = authenticate_user(form_data.username, form_data.password)
if not user:
raise fastapi.HTTPException(
status_code=starlette.status.HTTP_400_BAD_REQUEST,
detail="Incorrect username or password",
)
access_token_expires = datetime.timedelta(
minutes=settings.JWT_ACCESS_TOKEN_EXPIRE_MINUTES
)
access_token = create_access_token(
data={
"sub": user.username,
"scopes": settings.OAUTH2_SCHEME_SCOPES, # form_data.scopes
},
expires_delta=access_token_expires,
)
# return {"access_token": access_token, "token_type": "bearer"}
response = starlette.responses.Response(
{"access_token": access_token, "token_type": "bearer"},
status_code=starlette.status.HTTP_200_OK,
)
response.set_cookie(
key="Authorization",
value="Bearer {}".format(fastapi.encoders.jsonable_encoder(access_token)),
httponly=True,
max_age=60 * 60,
expires=60 * 60,
domain="glass.ip-spotlight.xxx.xxx",
)
return response
Is it possible to set a cookie while returning {"access_token": access_token, "token_type": "bearer"}
so that I can test it with OpenAPI client ?
The above fails with:
File "/usr/local/lib/python3.6/site-packages/starlette/responses.py", line 42, in __init__
self.body = self.render(content)
File "/usr/local/lib/python3.6/site-packages/starlette/responses.py", line 54, in render
return content.encode(self.charset)
AttributeError: 'dict' object has no attribute 'encode'
Do you know perhaps why is so?
On top of that, when I return sth like:
response = starlette.responses.RedirectResponse(
url=app.url_path_for("looking_glass_applications")
)
response.set_cookie(
key="Authorization",
value="Bearer {}".format(fastapi.encoders.jsonable_encoder(access_token)),
httponly=True,
max_age=60 * 60,
expires=60 * 60,
domain="glass.ip-spotlight.aorta.net",
)
return response
and then visit:
@app.get("/apps/test")
async def lookup_bgp_network_prefix(
current_user: User = fastapi.Security(get_current_active_user, scopes=["bgp"])
):
return starlette.responses.JSONResponse(
status_code=starlette.status.HTTP_200_OK, content={"resp": "xxx"}
)
I get an error saying:
response = await func(request)
File "/usr/local/lib/python3.6/site-packages/fastapi/routing.py", line 119, in app
dependency_overrides_provider=dependency_overrides_provider,
File "/usr/local/lib/python3.6/site-packages/fastapi/dependencies/utils.py", line 490, in solve_dependencies
dependency_cache=dependency_cache,
File "/usr/local/lib/python3.6/site-packages/fastapi/dependencies/utils.py", line 490, in solve_dependencies
dependency_cache=dependency_cache,
File "/usr/local/lib/python3.6/site-packages/fastapi/dependencies/utils.py", line 507, in solve_dependencies
if sub_dependant.use_cache and sub_dependant.cache_key in dependency_cache:
TypeError: unhashable type: 'OAuth2PasswordBearerWithCookie'
Do you know what exactly triggers the above error ?
Could you please advise how to make OAuth2 to work based on a cookie (for example) ?
Additional context
I am using the class described here https://github.com/tiangolo/fastapi/issues/480 is an extension of the OAuth2PasswordBearer
class which now includes checking for an Authorization header and an Authorization cookie. This will use a header as preference and falls back to a cookie.
Furthermore, you have to create the object of OAuth2PasswordBearerWithCookie
and use this instead of the OAuth2PasswordBearer
class (https://github.com/tiangolo/fastapi/blob/master/fastapi/security/oauth2.py).
class OAuth2PasswordBearerWithCookie(fastapi.openapi.models.OAuth2):
def __init__(
self,
tokenUrl: str,
scheme_name: str = None,
scopes: dict = None,
auto_error: bool = True,
):
if not scopes:
scopes = {}
flows = fastapi.openapi.models.OAuthFlows(
password={"tokenUrl": tokenUrl, "scopes": scopes}
)
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
async def __call__(
self, request: starlette.requests.Request
) -> typing.Optional[str]:
header_authorization: str = request.headers.get("Authorization")
cookie_authorization: str = request.cookies.get("Authorization")
header_scheme, header_param = get_authorization_scheme_param(
header_authorization
)
cookie_scheme, cookie_param = get_authorization_scheme_param(
cookie_authorization
)
if header_scheme.lower() == "bearer":
authorization = True
scheme = header_scheme
param = header_param
elif cookie_scheme.lower() == "bearer":
authorization = True
scheme = cookie_scheme
param = cookie_param
else:
authorization = False
if not authorization or scheme.lower() != "bearer":
if self.auto_error:
raise HTTPException(
status_code=starlette.status.HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
else:
return None
return param
oauth2_scheme = OAuth2PasswordBearerWithCookie( # fastapi.security.OAuth2PasswordBearer
tokenUrl="/token",
scopes={
"me": "diagnostics about the current user",
"bgp": "capabilities about bgp route lookup",
},
)
Issue Analytics
- State:
- Created 4 years ago
- Comments:13 (5 by maintainers)
Top GitHub Comments
my code:
@nskalis Not an expert, but I played around a bit and it seems like importing
OAuth2
fromfastapi.security
instead offastapi.openapi.models
does not lead to the error messages you are seeing anymore.