repo_description: "Magic Links" - URLs with authentication tokens for one-click login
repo_topics: authentication django python token-based-authentication
code: from urllib.parse import urlencode
  
  from django.contrib.auth import authenticate
  from django.utils import timezone
  
  from . import settings
  from .tokens import create_token
  
  __all__ = ["get_token", "get_parameters", "get_query_string", "get_user"]
  
  
  def get_token(user, scope=""):
      """
      Generate a signed token to authenticate ``user``.
  
      Set ``scope`` to create a :ref:`scoped token <Scoped tokens>`.
  
      Use this function to create a token that :func:`get_user` will accept.
  
      """
      return create_token(user, scope)
  
  
  def get_parameters(user, scope=""):
      """
      Generate a :class:`dict` of query string parameters to authenticate ``user``.
  
      Set ``scope`` to create a :ref:`scoped token <Scoped tokens>`.
  
      Use this function to add authentication to a URL that already contains a
      query string.
  
      """
      return {settings.TOKEN_NAME: create_token(user, scope)}
  
  
  def get_query_string(user, scope=""):
      """
      Generate a complete query string to authenticate ``user``.
  
      Set ``scope`` to create a :ref:`scoped token <Scoped tokens>`.
  
      Use this function to add authentication to a URL that doesn't contain a
      query string.
  
      """
      return "?" + urlencode({settings.TOKEN_NAME: create_token(user, scope)})
  
  
  def get_user(request_or_sesame, scope="", max_age=None, *, update_last_login=None):
      """
      Authenticate a user based on a signed token.
  
      ``request_or_sesame`` may be a :class:`~django.http.HttpRequest` containing
      a token in the URL or the token itself, created with :func:`get_token`. The
      latter supports use cases outside the HTTP request lifecycle.
  
      If a valid token is found, return the user. Else, return :obj:`None`.
  
      :func:`get_user` doesn't log the user in permanently.
  
      If ``scope`` is set, a :ref:`scoped token <Scoped tokens>` is expected.
  
      If ``max_age`` is set, override the :data:`SESAME_MAX_AGE` setting.
  
      If single-use tokens are enabled, :func:`get_user` invalidates the token by
      updating the user's last login date.
  
      Set ``update_last_login`` to :obj:`True` or :obj:`False` to always or never
      update the user's last login date, regardless of whether single-use tokens
      are enabled. Typically, if you're going to log the user in with
      :func:`~django.contrib.auth.login`, set ``update_last_login`` to
      :obj:`False` to avoid updating the last login date twice.
  
      """
      if isinstance(request_or_sesame, str):
          request = None
          sesame = request_or_sesame
      else:
          # request is expected to be a django.http.HttpRequest
          request = request_or_sesame
          try:
              sesame = request_or_sesame.GET.get(settings.TOKEN_NAME)
          except Exception:
              raise TypeError("get_user() expects an HTTPRequest or a token")
          if sesame is None:
              return None
  
      # Call authenticate() to set user.backend to the value expected by login(),
      # "sesame.backends.ModelBackend" or the dotted path to a subclass.
      user = authenticate(
          request,
          sesame=sesame,
          scope=scope,
          max_age=max_age,
      )
      if user is None:
          return None
  
      if update_last_login is None:
          update_last_login = settings.ONE_TIME
      if update_last_login:
          user.last_login = timezone.now()
          user.save(update_fields=["last_login"])
  
      return user
  