"""Authenticate with tokens""" from typing import Any, Optional from django.contrib.auth.backends import ModelBackend from django.http.request import HttpRequest from authentik.core.models import Token, TokenIntents, User from authentik.events.utils import cleanse_dict, sanitize_dict from authentik.flows.planner import FlowPlan from authentik.flows.views.executor import SESSION_KEY_PLAN from authentik.stages.password.stage import PLAN_CONTEXT_METHOD, PLAN_CONTEXT_METHOD_ARGS class InbuiltBackend(ModelBackend): """Inbuilt backend""" def authenticate( self, request: HttpRequest, username: Optional[str], password: Optional[str], **kwargs: Any ) -> Optional[User]: user = super().authenticate(request, username=username, password=password, **kwargs) if not user: return None self.set_method("password", request) return user def set_method(self, method: str, request: Optional[HttpRequest], **kwargs): """Set method data on current flow, if possbiel""" if not request: return # Since we can't directly pass other variables to signals, and we want to log the method # and the token used, we assume we're running in a flow and set a variable in the context flow_plan: FlowPlan = request.session[SESSION_KEY_PLAN] flow_plan.context[PLAN_CONTEXT_METHOD] = method flow_plan.context[PLAN_CONTEXT_METHOD_ARGS] = cleanse_dict(sanitize_dict(kwargs)) request.session[SESSION_KEY_PLAN] = flow_plan class TokenBackend(InbuiltBackend): """Authenticate with token""" def authenticate( self, request: HttpRequest, username: Optional[str], password: Optional[str], **kwargs: Any ) -> Optional[User]: try: user = User._default_manager.get_by_natural_key(username) except User.DoesNotExist: # Run the default password hasher once to reduce the timing # difference between an existing and a nonexistent user (#20760). User().set_password(password) return None tokens = Token.filter_not_expired( user=user, key=password, intent=TokenIntents.INTENT_APP_PASSWORD ) if not tokens.exists(): return None token = tokens.first() self.set_method("token", request, token=token) return token.user