Answer the question
In order to leave comments, you need to log in
Cookies not set when calling set cookie method?
Hello, due to the fact that there is no Starlette or FastAPI tag among the Habr tags, I tagged the question with the Python tag.
My problem is that after logging in, cookies are incorrectly set to the created object of the Response class, or rather, they are not even set at all.
My authentication manager, written in order not to produce code in views (the problematic part is highlighted)
class Authentication:
def __init__(
self,
*,
cookie_key: str,
cookie_domain: str,
cookie_httponly: bool,
cookie_max_age: int,
crud: ConsumersCRUD,
credentials_exception_class: Type[Exception],
secret: str,
algorithm: str
) -> None:
self.cookie_key = cookie_key
self.cookie_domain = cookie_domain
self.cookie_httponly = cookie_httponly
self.cookie_max_age = cookie_max_age
self.crud = crud
self.credentials_exception_class = credentials_exception_class
self.secret = secret
self.algorithm = algorithm
async def _open_session(self, response: Response, username: str) -> None:
cookie_data = self.__dict__.copy()
pattern = 'cookie_'
for parameter in cookie_data.copy():
if not parameter.startswith(pattern):
cookie_data.pop(parameter)
else:
parameter_without_prefix = parameter.removeprefix(pattern)
cookie_data[parameter_without_prefix] = cookie_data.pop(parameter)
cookie_data['value'] = _generate_token(
username, self.cookie_max_age, self.secret, self.algorithm
)
return response.set_cookie(**cookie_data)
async def authorize(self, response: Response, credentials: OAuth2PasswordRequestForm) -> None:
consumer = await self.crud.get('username', credentials.username)
password_match = False
if consumer:
password_match = _check_password(
credentials.password, consumer.get('password')
)
if not password_match:
raise self.credentials_exception_class('Incorrectly entered login or password.')
return await self._open_session(response, credentials.username)
async def _open_session(self, response: Response, username: str) -> None:
token = _generate_token(username, self.cookie_max_age, self.secret, self.algorithm)
response.set_cookie(key=self.cookie_key, domain=self.cookie_domain,
value=token, max_age=self.cookie_max_age, httponly=self.cookie_httponly)
_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
def _check_password(password: str, encrypted_password: str) -> bool:
return _context.verify(password, encrypted_password)
def _generate_token(username: str, expires: int, secret: str, algorithm: str) -> str:
payload = {
'sub': username,
'exp': datetime.utcnow() + timedelta(seconds=expires)
}
return jwt.encode(payload, secret, algorithm).decode('utf-8')
auth = fastapi.APIRouter()
auth_templates = Jinja2Templates(directory=settings.CONSUMERS_FRONTEND)
consumers = ConsumersCRUD(database, consumers)
auth_manager = Authentication(
**settings.AUTH_MANAGER_SETTINGS,
crud=consumers,
credentials_exception_class=CredentialsValidationException
)
async def _convenient_display_template(template: str, request: fastapi.Request, **context):
# The request parameter might be required only in the context,
# but since it is required for display, it is handed down separately.
context['request'] = request
return auth_templates.TemplateResponse(template, context)
@auth.get('/sign-in')
async def sign_in(request: fastapi.Request):
return await _convenient_display_template('sign-in.html', request=request)
@auth.post('/sign-in')
async def sign_in(
request: fastapi.Request,
credentials: OAuth2PasswordRequestForm = fastapi.Depends(OAuth2PasswordRequestForm)
):
try:
response = RedirectResponse(settings.REDIRECT_AFTER_SIGN_IN, status_code=303)
await auth_manager.authorize(response, credentials)
return response
except CredentialsValidationException as error_details:
return await _convenient_display_template(
'sign-in.html', request, error=error_details
)
REDIRECT_AFTER_SIGN_UP = "/sign-in"
REDIRECT_AFTER_SIGN_IN = "/"
COOKIE_KEY = 'Authorization'
COOKIE_DOMAIN = 'localhost'
COOKIE_HTTPONLY = True
ACCESS_TOKEN_EXPIRES = 1800
SECRET = 'sdksjkajkjaJKDJKjkdjks3984984mjkdjksjdk'
ALGORITHM = 'HS256'
AUTH_MANAGER_SETTINGS = {
'cookie_key': COOKIE_KEY,
'cookie_domain': COOKIE_DOMAIN,
'cookie_httponly': COOKIE_HTTPONLY,
'cookie_max_age': ACCESS_TOKEN_EXPIRES,
'secret': SECRET,
'algorithm': ALGORITHM
}
async def __call__(self, request: Request) -> str:
"""
"""
print(request.headers)
print(request.cookies)
cookie_header = request.cookies.get(self.cookie_key)
cookie_scheme, cookie_param = get_authorization_scheme_param(cookie_header)
print('Header:', cookie_header)
print('Scheme:', cookie_scheme)
print('Param:', cookie_param)
return 'dssd'
@app.get('/')
async def homepage(auth_key: str = Depends(auth_manager)):
pass
Headers({'host': '127.0.0.1:8000', 'connection': 'keep-alive', 'cache-control': 'max-age=0', 'upgrade-insecure-requests': '1' , 'user-agent': '', 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q =0.8,application
/signed-exchange;v=b3;q=0.9', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'navigate', 'sec-fetch- user': '?1', 'sec-fetch-dest': 'document', 'referer': ' 127.0.0.1:8000/c
onsumers/sign-in', 'accept-encoding': 'gzip, deflate, br', 'accept-language': 'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7', 'cookie': '= aa308bd8-pga4_session c10a-4d98-9bab-5656c6e9a405 7b kPpPJ + +
! NpVfHuhW4sB3YtatMs = '})
{' pga4_session ':' aa308bd8-c10a-4d98-9bab-5656c6e9a405 7b kPpPJ + + NpVfHuhW4sB3YtatMs = '}!
Header: None
Scheme:
Param:
Answer the question
In order to leave comments, you need to log in
The problem turned out to be that instead of localhost, the domain parameter needs a value corresponding to the domain itself. That is, if you run on the locale and your value is 127.0.0.1, then the value sent to the set_cookie method must match it.
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question