nrbnlulu / strawberry-django-auth

Authentication system for django using strawberry
https://nrbnlulu.github.io/strawberry-django-auth/
MIT License
68 stars 31 forks source link

Issue in using email or both username and email for log in #561

Open jbdura opened 4 months ago

jbdura commented 4 months ago

Prerequisites

Feel free to just say what you want and remove this template.

For more information, see the CONTRIBUTING guide.

Description

tokenAuth mutation works fine when I use username. When I change the option to email, the package fails

Description of the bug/feature/question

Steps to Reproduce

If we need to reproduce and you don't provide steps for it, it will be closed. Alternatively, you can link a repo with the code to run your issue.

  1. [First Step]
    
    from gqlauth.settings_type import GqlAuthSettings, email_field, username_field

AUTH_USER_MODEL = 'users.CustomUser'

GQL_AUTH = GqlAuthSettings( LOGIN_FIELDS={email_field}, -> when I enable this, it throws an error , see below

LOGIN_FIELDS = {username_field}, -> when I uncomment this line, it works fine

)

users/schema.py

import strawberry from gqlauth.user.queries import UserQueries from gqlauth.core.middlewares import JwtSchema from gqlauth.user import arg_mutations as mutations from django.contrib.auth import get_user_model from strawberry.extensions import QueryDepthLimiter, AddValidationRules from gallery.schema import ImageType from gallery.models import Image import typing from graphql.validation import NoSchemaIntrospectionCustomRule

@strawberry.django.type(model=get_user_model()) class CustomUserType: id: strawberry.auto username: strawberry.auto email: strawberry.auto first_name: strawberry.auto last_name: strawberry.auto profile_image: typing.Optional[ImageType]

@strawberry.type class Query(UserQueries): @strawberry.field def whoami(self, info) -> CustomUserType: user = info.context.request.user if user.is_anonymous: raise Exception("Authentication Failure: You must be signed in") return user

@strawberry.field
def users(self, info) -> list[CustomUserType]:
    user = info.context.request.user
    if not user.is_authenticated:
        raise Exception("Authentication Failure: You must be signed in")
    return get_user_model().objects.all()

@strawberry.type class Mutation: register = mutations.Register.field verify_token = mutations.VerifyToken.field update_account = mutations.UpdateAccount.field archive_account = mutations.ArchiveAccount.field delete_account = mutations.DeleteAccount.field password_change = mutations.PasswordChange.field token_auth = mutations.ObtainJSONWebToken.field verify_account = mutations.VerifyAccount.field resend_activation_email = mutations.ResendActivationEmail.field send_password_reset_email = mutations.SendPasswordResetEmail.field password_reset = mutations.PasswordReset.field password_set = mutations.PasswordSet.field refresh_token = mutations.RefreshToken.field revoke_token = mutations.RevokeToken.field

@strawberry.mutation
def set_profile_image(self, info, image_id: int) -> CustomUserType:
    user = info.context.request.user
    if user.is_anonymous:
        raise Exception("Authentication Failure: You must be signed in")

    try:
        image = Image.objects.get(id=image_id)
        user.profile_image = image
        user.save()
        return user
    except Image.DoesNotExist:
        raise Exception("Image not found")

schema = JwtSchema( query=Query, mutation=Mutation, extensions=[ QueryDepthLimiter(max_depth=7),

AddValidationRules([NoSchemaIntrospectionCustomRule]),

]

)


2. [Second Step]
![Screenshot from 2024-07-08 09-55-23](https://github.com/nrbnlulu/strawberry-django-auth/assets/99818207/98263d78-6cea-44f9-a78b-920f730b6d46)

4. [and so on...]

## Expected behavior

What you expected to happen

## Actual behavior

What actually happened

# Requirements

Paste the packages you are using, you can get this information from executing `pip freeze`.

graphql-core              3.2.3
graphql-relay             3.2.0
django-cors-headers       4.3.1
django-environ            0.11.2
django-filter             23.5
django-ratelimit          4.1.0
django-stubs              4.2.7
django-stubs-ext          5.0.2
strawberry-django-auth    0.376.11
strawberry-graphql        0.235.0
strawberry-graphql-django 0.44.2
nrbnlulu commented 4 months ago

can you provide a traceback?

jbdura commented 4 months ago
[08/Jul/2024 08:52:12] "GET /users/graphql/ HTTP/1.1" 200 4359
[08/Jul/2024 08:52:16] "POST /users/graphql/ HTTP/1.1" 200 47820
'ObtainJSONWebTokenInput' object has no attribute 'username'

GraphQL request:2:3
1 | mutation {
2 |   tokenAuth(email: "user1@example.com", password: "SuperSecureP@sw0rd") {
  |   ^
3 |     success
Traceback (most recent call last):
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/graphql/execution/execute.py", line 528, in await_result
    return_type, field_nodes, info, path, await result
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/strawberry_django/fields/field.py", line 223, in async_resolver
    resolved = await result  # type: ignore
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/asgiref/sync.py", line 468, in __call__
    ret = await asyncio.shield(exec_coro)
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/asgiref/current_thread_executor.py", line 40, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/asgiref/sync.py", line 522, in thread_handler
    return func(*args, **kwargs)
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/strawberry_django/resolvers.py", line 105, in async_resolver
    return sync_resolver(*args, **kwargs)
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/strawberry_django/resolvers.py", line 83, in sync_resolver
    retval = resolver(*args, **kwargs)
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/strawberry/types/fields/resolver.py", line 203, in __call__
    return self.wrapped_func(*args, **kwargs)
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/gqlauth/core/mixins.py", line 24, in field
    return cls.resolve_mutation(info, input_type(**kwargs))  # type: ignore
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/gqlauth/user/resolvers.py", line 376, in resolve_mutation
    return ObtainJSONWebTokenType.authenticate(info, input_)
  File "/home/jean/Development/proj/work/mine/AgriSocial/agri2/api/v6/.venv/lib/python3.10/site-packages/gqlauth/jwt/types_.py", line 166, in authenticate
    USER_MODEL.USERNAME_FIELD: getattr(input_, USER_MODEL.USERNAME_FIELD),
AttributeError: 'ObtainJSONWebTokenInput' object has no attribute 'username'
[08/Jul/2024 08:52:20] "POST /users/graphql/ HTTP/1.1" 200 16
nrbnlulu commented 4 months ago

your user model should have USERNAME_FIELD="email"

jbdura commented 4 months ago

I managed to fix it by doing this

    USERNAME_FIELD = "email"  # to log in with email
    EMAIL_FIELD = "email"
    REQUIRED_FIELDS = [] # disabled default features
from gqlauth.settings_type import GqlAuthSettings, email_field

AUTH_USER_MODEL = 'users.CustomUser'

GQL_AUTH = GqlAuthSettings(
    LOGIN_REQUIRE_CAPTCHA=False,
    REGISTER_REQUIRE_CAPTCHA=False,
    JWT_TIME_FORMAT="%Y-%m-%dT%H:%M:%S",  # A valid 'strftime' string for the token payload
    JWT_LONG_RUNNING_REFRESH_TOKEN=True,  # Enable refresh tokens
    JWT_EXPIRATION_DELTA=timedelta(minutes=60),  # Token expiration time, e.g., 30 minutes
    JWT_REFRESH_EXPIRATION_DELTA=timedelta(days=7),
    EXPIRATION_ACTIVATION_TOKEN=timedelta(minutes=60),
    LOGIN_FIELDS={email_field},

)

can this be added to documentation for simplicity?

nrbnlulu commented 4 months ago

Go for it 😁