PedroBern / django-graphql-auth

Django registration and authentication with GraphQL.
https://django-graphql-auth.readthedocs.io/en/latest/
MIT License
329 stars 106 forks source link

Not Authenticate user in other query #37

Open miladjobs opened 4 years ago

miladjobs commented 4 years ago

Hello I have problem with authenticate. At first i called "me" query i got null user. At second i wrote one authentication backend and me query fixed but when i write one query and inside it use info.context.user i get Anonymous user. when i debug it the code never go to backend authentication but in me query gone.

Somethings that i have done but not fixed:

  1. delete my backend authentication
  2. change graphql_auth.backends.GraphQLAuthBackend to graphql_jwt.backends.JSONWebTokenBackend(but i got this error "User is disabled": graphql.error.located_error.GraphQLLocatedError: User is disabled)

With these codes me query work well but own query not

MyCustom User:

class MyUser(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(unique=True)
    is_man = models.BooleanField(default=True)
    user_type = models.CharField(max_length=3, choices=USER_TYPE_CHOICES, default='STD')
    phone_number = models.CharField(max_length=11, unique=True)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    is_active = models.BooleanField(default=False)
    username = None
    objects = UserManager()
    USERNAME_FIELD = 'email'
    EMAIL_FIELD = 'email'
    REQUIRED_FIELDS = ['phone_number']

    def __str__(self):
        return self.email 

MyCustom UserManager :

 class UserManager(BaseUserManager):
    use_in_migrations = True

    def create_user(self, email, is_man, is_student, phone_number, first_name, last_name, password=None):
        if not email:
            raise ValueError('Email is not provided.')

        user = self.model(
            email=email,
            is_man=is_man,
            phone_number=phone_number,
            first_name=first_name,
            last_name=last_name
        )

        user.set_password(password)
        user.save(using=self.db)

        return user

    def create_superuser(self, email, password, **extra_fields):
        """
        Create and save a SuperUser with the given email and password.
        """
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)
        extra_fields.setdefault('is_active', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must have is_staff=True.'))
        if extra_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser=True.'))
        return self.create_user(email, password, **extra_fields) 

My Custom Authentication Backend :

 class EmailBackend:
    def authenticate(request, username=None, password=None):
        if username is not None:
            user_model = get_user_model()

            try:
                user = user_model.objects.get(email__iexact=username)
            except user_model.DoesNotExist:
                """Not found, try another backend"""
            else:
                if user.check_password(password):
                    return user
        if request.user is not None:
            user_model = get_user_model()
            try:
                token = request.headers._store['authorization'][1][4:]
                play_load = get_payload(token=token)
                user = user_model.objects.get(email__iexact=play_load['email'])
                return user
            except user_model.DoesNotExist:
                """Not found, try another backend"""
        return None

My Own Qury :

class UsersQuery(graphene.ObjectType):
    student = graphene.relay.Node.Field(StudentNode)
    all_students = DjangoFilterConnectionField(StudentNode)

    def resolve_all_students(self, info, **kwargs):
        if not info.context.user.is_authenticated:
            raise ObjectDoesNotExist

My settings:

AUTHENTICATION_BACKENDS = [
    "graphql_auth.backends.GraphQLAuthBackend",
    'users.models.EmailBackend',
]
GRAPHENE = {
    'SCHEMA': 'dormify.schema.schema',
    'MIDDLEWARE': [
        'graphql_jwt.middleware.JSONWebTokenMiddleware',
    ],
}
GRAPHQL_AUTH = {
    'LOGIN_ALLOWED_FIELDS': ['email'],
    'ALLOW_LOGIN_NOT_VERIFIED': True,  # Change This to false to add email confirmation
    'ALLOW_LOGIN_WITH_SECONDARY_EMAIL': False,
    'REGISTER_MUTATION_FIELDS': {
        "email": "String",
        "is_man": "Boolean",
        "user_type": "String",
        "phone_number": "String",
        "first_name": "String",
        "last_name": "String"
    },
    'USER_NODE_FILTER_FIELDS': {
        "email": ["exact", ],
        "first_name": ["exact", "icontains", "istartswith"],
        "last_name": ["exact", "icontains", "istartswith"],
        "is_active": ["exact"],
        "user_type": ["exact"],
    },
    'USER_NODE_EXCLUDE_FIELDS': ["is_superuser", "password"],
    'EMAIL_SUBJECT_ACTIVATION': "email/activation_subject.txt",
    'EMAIL_SUBJECT_ACTIVATION_RESEND': "email/activation_subject.txt",
    'EMAIL_SUBJECT_PASSWORD_RESET': "email/password_reset_subject.txt",
    'EMAIL_TEMPLATE_ACTIVATION': "email/activation_email.html",
    'EMAIL_TEMPLATE_ACTIVATION_RESEND': "email/activation_email.html",
    'EMAIL_TEMPLATE_PASSWORD_RESET': "email/password_reset_email.html",
}
GRAPHQL_JWT = {
    "JWT_VERIFY_EXPIRATION": True,
    'JWT_EXPIRATION_DELTA': timedelta(minutes=60),
    'JWT_REFRESH_EXPIRATION_DELTA': timedelta(days=7),
    # optional
    "JWT_LONG_RUNNING_REFRESH_TOKEN": True,
    "JWT_ALLOW_ANY_CLASSES": [
        "graphql_auth.relay.Register",
        "graphql_auth.relay.VerifyAccount",
        "graphql_auth.relay.ResendActivationEmail",
        "graphql_auth.relay.SendPasswordResetEmail",
        "graphql_auth.relay.PasswordReset",
        "graphql_auth.relay.ObtainJSONWebToken",
        "graphql_auth.relay.VerifyToken",
        "graphql_auth.relay.RefreshToken",
        "graphql_auth.relay.RevokeToken",
        "graphql_auth.relay.VerifySecondaryEmail",
    ],
}
PedroBern commented 4 years ago

Hi @miladjobs is it all the information that I need to replicate it? I will try to replicate and come back here, but this week I do not have time, maybe next week.

But first please print your packages here (use pip freeze).

miladjobs commented 4 years ago

Thank you very much . requirements:

aniso8601==7.0.0 
asgiref==3.2.7
backcall==0.1.0
certifi==2020.4.5.1
chardet==3.0.4
decorator==4.4.2
Django==3.0.4
django-cors-headers==3.2.1
django-filter==2.2.0
django-graphql-auth==0.3.6
django-graphql-jwt==0.3.0
graphene==2.1.8
graphene-django==2.9.0
graphql-core==2.3.1
graphql-relay==2.0.1
gunicorn==19.6.0
idna==2.9
ipython==7.13.0
ipython-genutils==0.2.0
jedi==0.16.0
parso==0.6.2
pexpect==4.8.0
pickleshare==0.7.5
promise==2.3
prompt-toolkit==3.0.5
psycopg2==2.8.4
ptyprocess==0.6.0
Pygments==2.6.1
PyJWT==1.7.1
python-dateutil==2.8.1
pytz==2019.3
requests==2.23.0
Rx==1.6.1
singledispatch==3.4.0.3
six==1.14.0
sqlparse==0.3.1
traitlets==4.3.3
urllib3==1.25.8
wcwidth==0.1.9

some other settings :

AUTH_USER_MODEL = 'users.MyUser'

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'corsheaders.middleware.CorsMiddleware',
]

Student Node :

class StudentNode(DjangoObjectType):
    class Meta:
        model = Student
        filter_fields = {
            'user__first_name': ['exact', 'icontains', 'istartswith'],
            'user__last_name': ['exact', 'icontains', 'istartswith'],
            'std_id': ['exact', 'icontains', 'istartswith'],
            'room__number': ['exact'],
            'room__floor__number': ['exact'],
            'room__floor__block__number': ['exact'],
            'room__floor__block__dormitory__name': ['exact', 'icontains', 'istartswith'],
            'grade': ['exact'],
        }
        interfaces = (graphene.relay.Node,)

Student Model:

class Student(models.Model):
    user = models.OneToOneField(MyUser, on_delete=models.CASCADE, primary_key=True, related_name='student')
    std_id = models.CharField(max_length=8, unique=True)

Note : Student register in another mutation api and connect to user

sirb0rab0g1 commented 4 years ago

i was thinking it is because you are using 'JWT_EXPIRATION_DELTA': timedelta(minutes=60),

PedroBern commented 4 years ago

Did you made any progress on it?

If you need help to setup JWT on the client, here is a good resource.

miladjobs commented 4 years ago

@sirb0rab0g1 I got fresh token with tokenauth and few sconds later with postman i call query API with token on header and get anonymous user. But after my own query i called "me" query and get back true authenticated user. If expiration have problem , "me" query must also have problem

miladjobs commented 4 years ago

I have found that after return of "mutate_and_get_playload" or "resolve query" user being authenticated but must be called before call of those methods. Do you have any idea why this is so? @PedroBern

PedroBern commented 4 years ago

@miladjobs didn't understand what you mean, could you give an example?

patrykKlimczak commented 4 years ago

I have the same problem with info.context.user

image

I think problem with this :/

My query

    user = graphene.Field(UserType, token=graphene.String(required=True))
    users = graphene.List(UserType)

    @login_required
    def resolve_user(self, info, **kwargs):
        print(info.context.user.id, 'my user schema id')
        return info.context.user

    @login_required
    def resolve_users(self, info):
        return get_user_model().objects.all()

this views I have good id on console image

but when I go to other schema and model

def resolve_posts_author(self, info):
        print(info.context.user.id, 'my user author id')
        return Post.objects.order_by('-updated_at').filter(author_id=info.context.user.id)

and print mi nothing or id sessions log admin panel image

Instrumedley commented 3 years ago

@miladjobs did you have any luck with not getting anonymous user ?