flavors / django-graphql-social-auth Goto Github PK
View Code? Open in Web Editor NEWPython Social Auth support for Graphene Django
Home Page: https://pypi.python.org/pypi/django-graphql-social-auth
License: MIT License
Python Social Auth support for Graphene Django
Home Page: https://pypi.python.org/pypi/django-graphql-social-auth
License: MIT License
Hey I was just thinking that rather than using the frontend to send the access token what if the access token stored in the backend is used to make the request to the Oauth server.
I think I should explain what I was trying to do here... I have a project built using django and I recently created a graphql API using django graphene. originally the project had python social auth and was using it to login thru google using the secrets stored in the backends' .env
file.
now since I have the grqphql API in place I want to just use a mutation to take the provider as an argument from my frontend and then the backend using the keys stored in the .env
file make all the network calls, like exchanging auth code for token etc., to the Oauth2.0 auth server and resource server to get back a JWT token (in my case I am using JWT tokens for auth) and then that mutation will return a response just like this project does.
From READM it is unclear whether we need to fully replace graphql_social_auth.relay.SocialAuth.Field()
by graphql_social_auth.relay.SocialAuthJWT
or just the part before .Field()
, so I tried both ways.
I tried the next code:
import graphene
import graphql_social_auth
class Mutations(graphene.ObjectType):
# Tried this
social_auth = graphql_social_auth.relay.SocialAuthJWT
# and this
social_auth = graphql_social_auth.relay.SocialAuthJWT.Field()
The first way (social_auth = graphql_social_auth.relay.SocialAuthJWT
) returns:
{
"errors": [
{
"message": "Cannot query field \"socialAuth\" on type \"Mutation\".",
"locations": [
{
"line": 2,
"column": 5
}
]
}
]
}
The second way (social_auth = graphql_social_auth.relay.SocialAuthJWT.Field()
) returns:
{
"errors": [
{
"message": "Unknown argument \"provider\" on field \"socialAuth\" of type \"Mutation\".",
"locations": [
{
"line": 2,
"column": 16
}
]
},
{
"message": "Unknown argument \"accessToken\" on field \"socialAuth\" of type \"Mutation\".",
"locations": [
{
"line": 2,
"column": 42
}
]
},
{
"message": "Field \"socialAuth\" argument \"input\" of type \"SocialAuthJWTInput!\" is required but not provided.",
"locations": [
{
"line": 2,
"column": 5
}
]
}
]
}
This query works with social_auth = graphql_social_auth.SocialAuthJWT.Field()
(no relay
)
Django 4 has deprecated ugettext_lazy and instead it is now gettext_lazy.
I am trying to use social auth in Django in my Django GraphQL API (an alternative to rest api). However, after I created the social auth I created a costume user which make things a bit complicated. Also, it worked at the begging very well but and it created the user called weplutus.1
very well, but later after I added the following settings
#settings.py
#without this code i will get `anonymouseUser`
GRAPHENE = {
# 'SCHEMA': 'api.schema.schema',
'MIDDLEWARE': [
# the problem is here
'graphql_jwt.middleware.JSONWebTokenMiddleware',
],
}
when new users register the social auth associate it with other an existed user even the email is new.
# settings.py
from pathlib import Path
import os
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '*****'
DEBUG = True
ALLOWED_HOSTS = ["*"] # TODO change this in preduction
CORS_ORIGIN_ALLOW_ALL = True # TODO change this in preduction
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'graphene_django',
'corsheaders',
'api',
'django_filters',
'social_django',
]
SITE_ID = 1
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'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',
]
GRAPHENE = {
# Note without this you will get anynomus user.
'MIDDLEWARE': [
'graphql_jwt.middleware.JSONWebTokenMiddleware',
],
}
AUTH_USER_MODEL = 'api.ExtendUser'
# TODO delete this, test if thes send email when creating a new user
# if so try to stop it.
# EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
ROOT_URLCONF = 'django_graphql.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'django_graphql.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
# When using PostgreSQL, it’s recommended to use the built-in JSONB field (# SOCIAL_AUTH_POSTGRES_JSONFIELD = True) to store the extracted extra_data. To enable it define the setting:
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# SOCIAL_AUTH_POSTGRES_JSONFIELD = True
SOCIAL_AUTH_JSONFIELD_ENABLED = True
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# AUTHENTICATION_BACKENDS can be found here http://docs.djangoproject.com/en/dev/ref/settings/?from=olddocs#authentication-backends
AUTHENTICATION_BACKENDS = [
'graphql_jwt.backends.JSONWebTokenBackend',
# provider google-oauth2
'social_core.backends.google.GoogleOAuth2',
# 'graphql_auth.backends.GraphQLAuthBackend',
'django.contrib.auth.backends.ModelBackend',
]
SOCIAL_AUTH_PIPELINE = [
# Get the information we can about the user and return it in a simple
# format to create the user instance later. On some cases the details are
# already part of the auth response from the provider, but sometimes this
# could hit a provider API.
'social_core.pipeline.social_auth.social_details',
# Get the social uid from whichever service we're authing thru. The uid is
# the unique identifier of the given user in the provider.
'social_core.pipeline.social_auth.social_uid',
# Verifies that the current auth process is valid within the current
# project, this is where emails and domains whitelists are applied (if
# defined).
'social_core.pipeline.social_auth.auth_allowed',
# Checks if the current social-account is already associated in the site.
'social_core.pipeline.social_auth.social_user',
# Make up a username for this person, appends a random string at the end if
# there's any collision.
'social_core.pipeline.user.get_username',
# Send a validation email to the user to verify its email address.
# Disabled by default.
# 'social_core.pipeline.mail.mail_validation',
# Associates the current social details with another user account with
# a similar email address. Disabled by default.
'social_core.pipeline.social_auth.associate_by_email',
# Create a user account if we haven't found one yet.
'social_core.pipeline.user.create_user',
# Create the record that associates the social account with the user.
'social_core.pipeline.social_auth.associate_user',
# Populate the extra_data field in the social record with the values
# specified by settings (and the default ones like access_token, etc).
'social_core.pipeline.social_auth.load_extra_data',
# Update the user record with any changed info from the auth service.
'social_core.pipeline.user.user_details',
]
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = "*******"
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = "*********"
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
MEDIA_URL = '/images/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/images')
AUTH_USER_MODEL = 'api.ExtendUser'
ACCOUNT_EMAIL_REQUIRED = False
#modeles.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User, Group, AbstractUser, AbstractBaseUser, BaseUserManager, PermissionsMixin
from django.conf import settings
from django.core.validators import RegexValidator
from django.contrib.auth import get_user_model
StyleTitleFormat = RegexValidator(r'^[^\s]+$', 'spaces not allowed')
# Create your models here.
# class ExtendUser(AbstractBaseUser):
class ExtendUser(AbstractUser, PermissionsMixin):
username = models.CharField(max_length=30, unique=True)
email = models.EmailField(max_length=250, unique=True)
first_name = models.CharField(max_length=30, blank=True, null=True)
last_name = models.CharField(max_length=30, blank=True, null=True)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
date_joined = models.DateTimeField(default=timezone.now)
receive_newsletter = models.BooleanField(default=False)
birth_date = models.DateTimeField(blank=True, null=True)
address = models.CharField(max_length=300, blank=True, null=True)
city = models.CharField(max_length=30, blank=True, null=True)
about_me = models.TextField(max_length=500, blank=True, null=True)
imageUrl = models.URLField(null=True, blank=True)
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = ['email', ]
class Style(models.Model):
title = models.CharField(max_length=200, validators=[StyleTitleFormat])
description = models.CharField(max_length=9999999)
added_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created_date = models.DateTimeField(default=timezone.now)
who_can_see = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name='style_user', blank=True)
who_can_edite = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name='who_can_edite_style', blank=True)
def __str__(self):
return self.title
class Post(models.Model):
# note: posts can have tables as well and they can save it in the description (JSONField)
# title = models.CharField(max_length=200)
description = models.CharField(max_length=9999999)
preBuildStyle = models.ManyToManyField(
Style, related_name='Styles', blank=True)
style = models.CharField(max_length=9999999, blank=True)
# type = ['Paper','post','template','comstume_component']
postType = models.CharField(max_length=50, blank=True)
added_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
created_date = models.DateTimeField(default=timezone.now)
who_can_see = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name='poster_user', blank=True)
who_can_edite = models.ManyToManyField(
settings.AUTH_USER_MODEL, related_name='who_can_edite_Component', blank=True)
# def __str__(self):
# return self.title
# TODO create teams/groups/classes each with admin(creator)+manager(subAdmins)+members
Please add the set_cookie_auth to social mutation also for setting cookie and refresh_token
UserSocialAuthFilterSet.filter_for_reverse_field
has been removed. UserSocialAuthFilterSet.filter_for_field
now generates filters for reverse fields. See: https://django-filter.readthedocs.io/en/master/guide/migration.html
I think the error is with django-filters. How to fix this?
The issue is I already done a normal email registeration with a username as pawan.
Now if some other user is doing social login and his facebook or gmail username is pawan, the new user is also getting username as "Pawan", which is a case sensitive check. But i dont dont want same usernames like "Pawan", "pawan" or "paWan". It should treat all as one and create a random username out of it.
I am using:
django-graphql-auth==0.3.16
django-graphql-jwt==0.3.0
django-graphql-social-auth==0.1.4
Django==3.2.4
`# Middleware
MIDDLEWARE = [
"corsheaders.middleware.CorsMiddleware",
"django.middleware.cache.UpdateCacheMiddleware",
"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.middleware.cache.FetchFromCacheMiddleware",
]`
Any plans to keep maintenance of this package?
Maybe using uniontype. I might implement it.
Can you provide a simple example of how to integrate this with an existing django application?
Hi @mongkok
In the readme
there is an example how to customize SocialAuth
. Could you please show me how can I do the same using SocialAuthJWT
, in order to let me use the next query:
mutation CustomSocialAuth($provider: String!, $accessToken: String!) {
socialAuth(provider: $provider, accessToken: $accessToken) {
social {
uid
}
token
somedata
}
}
I tried:
class CustomSocialAuth(SocialAuthJWT):
somedata = graphene.String()
@classmethod
def resolve(cls, root, info, social, **kwargs):
return cls(somedata='some_data')
But in this case query returns "token": null
.
Thank you.
I'm trying to use this library in conjunction with a React application and I'm implementing a LinkedIN login.
The issue I'm facing is that my redirect_uri is different from the default python social auth value. I'm receiving the authorization code on the front-end and triggering the mutation, however I'm receiving a graphql.error.located_error.GraphQLLocatedError: Your credentials aren't allowed
error, most probably because of the different redirect_uri values (LinkedIN is enforcing using the same redirect_uri).
Previously, I was using the django-rest-social-auth
package that provides for supplying a value for the redirect_uri
value. I was wondering if something similar is possible using django-graphql-social-auth
.
I'm building an app with Django, Graphene and React. I'd like to add Github OAuth login and this package seems awesome! I'm having some trouble understanding how everything wires together though. Based on the queries provided in the README it seems that my frontend will have to perform the entire OAuth flow and once it has an access token should trigger a mutation with the right provider.
What I'm hung up on is whether it's safe for my frontend to have the clientSecret
which is needed for the OAuth flow. Based on the github oauth docs I can see how the first GET
request from the frontend will work. I'm not sure how execute the POST
with the code
provided by Github to get the users access token.
Should react query the server for the clientSecret
to perform the POST
? Is that a security problem because anyone will be able to start a debugger, set a breakpoint just before the POST
and dump the secret?
Any type of integration supported / possible?
Google's docs for Android-based OAUTH2 authentication say that we should send an idToken rather than an accessToken to our backend server.
How do you do that using Graphene Social Auth?
The only docs/examples I've found use access tokens. For instance:
mutation SocialAuth($provider: String!, $accessToken: String!) {
socialAuth(provider: $provider, accessToken: $accessToken) {
social {
uid
extraData
}
}
}
I imagine I can use Graphene Social Auth as is, by exchanging my idTokens for accessTokens in my Android client, but I'd rather do that server-side as Google recommends, just by sending in the idToken to the server and then have Graphene Social Auth exchange it for an accessToken and then do its stuff.
thanks!
John
SocialAuthMutation
currently assumes that the social auth pipeline will either return a user instance or None
.
However, this fails rather ungracefully when a partial pipeline step (PSA docs) returns an HTTP redirect instead of a user instance:
Traceback (most recent call last):
File "…/site-packages/graphql/execution/executor.py", line 447, in resolve_or_error
return executor.execute(resolve_fn, source, info, **args)
File "…/site-packages/graphql/execution/executors/sync.py", line 16, in execute
return fn(*args, **kwargs)
File "…/site-packages/graphql_social_auth/decorators.py", line 28, in wrapper
login(info.context, user)
File "…/site-packages/django/contrib/auth/__init__.py", line 155, in login
request.session[SESSION_KEY] = user._meta.pk.value_to_string(user)
graphql.error.located_error.GraphQLLocatedError: 'HttpResponseRedirect' object has no attribute '_meta'
The relevant code is in psa
decorator (code link).
I'm not sure what the best approach is to deal with this, since there's probably not a universally useful way to handle HTTP redirects in this context, but checking for this and raising a better exception is probably the minimum that could happen, and having some supported and documented way for user code to customise and handle this in some application-specific way would probably be ideal.
I need to create the following custom class mutation , in order to create users if they are not aready exists. To do that I need to pas arguments from the forest end like username, password.....
So I tried this .
class SocialAuth(graphql_social_auth.SocialAuthJWT):
# class Arguments:
# id = graphene.Int(required=False)
user = graphene.Field(schema.Users)
@classmethod
def resolve(cls, root, info, social, **kwargs):
try:
user = models.ExtendUser.objects.get(email=social.uid)
return cls(user=user)
except:
# TODO use profileObj. get username email and all these and add them kwargs
user = models.ExtendUser.objects.create(
email=social.uid, username=social.uid[0:6], password="AAssppmm11", imageUrl="https://miro.medium.com/max/1200/1*mk1-6aYaf_Bes1E3Imhc0A.jpeg")
return cls(user=user)
But adding class Arguments:
make the arguments provider
and accessToken
disappear.
social_auth = graphql_social_auth.SocialAuthJWT.Field()
I wanted to skip the verification of newly added users. I have managed in the customized SocialAuth(GraphQL_social_auth.SocialAuthJWT) function. but the problem is the code is running every time the user is logging in or registering. add the functionality to have a setting variable for disabling the verification.
Thank you for this repo and for JWT!!! I have been waiting for something like this for the last 2 months :)
Regarding propositions, I would add the next info to the README:
provider
name can be taken from this source. Then you need to go to the provider you need and find the variable called name
. So, this is the exact name which must be provided to socialAuth(provider:"NAME_VAR_FROM_URL_ABOVE", accessToken:"xxxx")
. For example, for Google OAuth2 it is google-oauth2
(taken from here)
For those who are not good with OAuth system, accessToken
must be provided by the provider (ie Google, Facebook, so on). For example, for Google we can get it in FrontEnd using such code: gapi.auth2.getAuthInstance().currentUser.get().getAuthResponse().access_token
(of course gapi js client must be uploaded first and user must let you get access to his\her data before you are able to retrieve access_token
).
As I mentioned in Issue #3 :
From READM it is unclear whether we need to fully replace graphql_social_auth.relay.SocialAuth.Field() by graphql_social_auth.relay.SocialAuthJWT or just the part before .Field().
So I propose to make it a bit clearer.
I tried to get the refreshToken but I could not.
Hi!
In REDME there is no example or explanation how, after receiving JWT token, further requests should be authenticated.
In other repo https://django-graphql-jwt.domake.io/en/stable/authentication.html#http-header
there is example with providing header to each request with token:
POST / HTTP/1.1
Host: domake.io
Authorization: JWT <token>
Content-Type: application/json;
Is it possible to do with current package?
Also I am curious if it is possible to mix generation of token part from this repo with access decorators from django-graphql-jwt
mutation TwitterAuthetication{
social_auth(provider: "twitter", access_token: "1445260549-Y6i3s6luLmiJC9djgts90IuNCM09mtXfxV0zIAO")
social
uid
}
token
}
}
gives me the following error:
{
"errors": [
{
"message": "403 Customer error: Forbidden for url: https://api.twitter.com/1.1/account/verify_credentials.json?include_email=true",
"Places": [
{
"line": 2,
"Column": 3
}
],
"way": [
"social_auth"
]
}
],
"data": {
"social_auth": null
}
}
They're following all the conventions, instead with Facebook if it works!
UserSocialAuthFilterSet.filter_for_reverse_field
has been removed. UserSocialAuthFilterSet.filter_for_field
now generates filters for reverse fields. See: https://django-filter.readthedocs.io/en/master/guide/migration.html
I get these errors, what is the way out
Hi,
The issue i have discovered is that when i create a createsuperuser with incomplete information (firstname , lastname... missing) the social user creation just override the superuser account with his missing information.
Step to follow :
create a superuser with the django command :
$ manage.py createsuperuser
use the graphQL social auth to create your user :
mutation token_authentication { socialAuthToken(accessToken: "xxxxBiBQGGLzTUwZD", provider: "facebook") { social { id uid user { username email firstName lastName } provider extraData } token } }
Expected :
A User is created thanks to the social auth module.
So we should have 2 users in the database :
Real Behavior :
The social user is not created.
The information from the social user override the missing root user information.
Root user information before user social connection :
Root user information after user social connection :
The 2 accounts are merged
To reproduce it, you can find in attachment :
Thank you for your help
there is no option to limit specific domain email addresses to register and authenticate.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.