Open cheslijones opened 4 years ago
Hi @cheslijones. Thanks for creating the issue.
Let's go by parts as there is a lot of information there. So backend should be what python-social-auth allows us to use, In my case I use backed=facebook
to authenticate against Facebook Auth API.
The client_id
and clint_secret
are the values auto-generated from the Application
you must create. Just go the Application in the admin and create a new Application there. With regards of these values:
SOCIAL_AUTH_AZUREAD_OAUTH2_KEY = '' SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET = ''
They must be retrieved from Azure. I myself never used Azure, but in order to authenticate with facebook I had to create a new facebook app. This new app provided me with a client key and client secret. Those must be set up in the settings.py.
the token
is indeed not clear. This is the token generated from the authentication against your backend. See this, once you authenticate using your username/email and password, your backend -- in my case Facebook -- will respond back with a token. In facebook this token may be generated for test purposes in this link: https://developers.facebook.com/tools/explorer/.
Therefore you need to find a test api for azure of authenticate against them.
Regarding your INSTALLED_APPS, you have the following:
INSTALLED_APPS = [
...
'drf_social_oauth2',
'oauth2_provider',
'rest_framework',
'social_django',
...
]
Please, replace with the following:
INSTALLED_APPS = [
...
'oauth2_provider', # this needs to be listed first.
'social_django', # social django uses oauth2_provider
'drf_social_oauth2', # this guy uses the two listed above
'rest_framework',
'rest_framework.authtoken', # see if you need this as well!
....
]
Otherwise, the migrations will not properly!
One more question, where did you see these values at the documentation:
backend=azuread_tenant
backend=AzureADTenantOAuth2
backend=azureadtenantoauth2
I could not see them.
Thanks for the quick reply.
Let's go by parts as there is a lot of information there. So backend should be what python-social-auth allows us to use, In my case I use
backed=facebook
to authenticate against Facebook Auth API.One more question, where did you see these values at the documentation: backend=azuread_tenant backend=AzureADTenantOAuth2 backend=azureadtenantoauth2 I could not see them.
These two go together. I'm honestly guessing what it should be. I don't see it notated anywhere what exactly are acceptable values for this. Based on the two Integration examples:
# Google OAuth2
'social_core.backends.google.GoogleOAuth2',
backend=google-oauth2
# Facebook OAuth2
'social_core.backends.facebook.FacebookAppOAuth2',
backend=facebook
So I figured for Azure it had to be one of the following:
backend=azuread_tenant
backend=AzureADTenantOAuth2
backend=azureadtenantoauth2
Based on...
'social_core.backends.azuread_tenant.AzureADTenantOAuth2',
I was apparently wrong in this assumption.
But the python-social-auth
documentation doesn't really say what acceptable values are, unless I'm overlooking it.
https://python-social-auth.readthedocs.io/en/latest/backends/azuread.html
Is this value supposed to correspond with the app name for the app created in Django Admin, by chance?
The client_id and client_secret are the values auto-generated from the Application you must create. Just go the Application in the admin and create a new Application there. With regards of these values:
SOCIAL_AUTH_AZUREAD_OAUTH2_KEY = '' SOCIAL_AUTH_AZUREAD_OAUTH2_SECRET = ''
They must be retrieved from Azure. I myself never used Azure, but in order to authenticate with facebook I had to create a new facebook app. This new app provided me with a client key and client secret. Those must be set up in the settings.py.
Thanks for confirming. That is what it sounded like to me.
the token is indeed not clear. This is the token generated from the authentication against your backend. See this, once you authenticate using your username/email and password, your backend -- in my case Facebook -- will respond back with a token. In facebook this token may be generated for test purposes in this link: https://developers.facebook.com/tools/explorer/. Therefore you need to find a test api for azure of authenticate against them.
Thanks, I'll look into this in Azure.
I reorganized my INSTALLED_APPS
per your suggestion and I'll keep testing.
Well, I finally guessed the backend=
correctly... backend=azuread-tenant-oauth2
. Now I'm getting:
[api] data = self.user_data(access_token, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/azuread_tenant.py", line 97, in user_data
[api] id_token = response.get('id_token')
[api] AttributeError: 'NoneType' object has no attribute 'get'
I still need to sort out the token=
in Azure, but at least progress.
I am happy that you managed to find the backend name. Look, what is the version of python-social-auth you're using? I have just visited this page: https://github.com/python-social-auth/social-core/blob/master/social_core/backends/azuread_tenant.py.
This is the core from the file where your had the error. I can't find the same code in line 97!
@cheslijones do you have any news about this?
@wagnerdelima Hi, I actually had to throw in the towel on this and went with a client-side ReactJS solution for handling it. So far it has worked as expected.
But to answer your question I was using 0.3.6 which I believe is the latest and it doesn't look like it has been updated in four years.
I am sorry you had a bad experience. I think you were using a very old version. The latest version is 3.4.0 and the last commit is from Jun 21, 2020.
That may be the reason!
Ok, I'm back with hopefully a better understanding of how I need to implement this.
My flow, as I understand it should be working between microservices, is the following:
domain.com/admin
which is my ReactJS FE.react-aad
.accessToken
.accessToken
needs to be sent to the Django/DRF API to also be validated because otherwise, any string could be passed from the FE and accepted--the API also needs to verify it.drf-social-oauth2
, oauth2_provider
, and social_django
are supposed to take this accessToken
, validate it against Azure AD from the API, and convert the token to one to be used between my ReactJS FE and the Django/DRF API.Is this correct?
If so, I have 1-3 working. Now I'm trying to send it to my API to also be validated for subsequent requests. This is where I'm struggling with the pretty much the same issues I had before.
Here is what I have:
// Authentication.js
// HAVING PARAMS IN THE FE IS A SECURITY ISSUE
// THIS WILL BE CHANGED AND MOVED TO THE BE
// WHEN THIS IS WORKING... THIS IS FOR TESTING ONLY
...
const testApiAuthentication = async () => {
let accessToken = await authProvider.getAccessToken();
setAccessToken(accessToken.accessToken);
if (accessToken) {
setAuthenticatingToken(true);
axios({
method: 'post',
url: '/api/auth/convert-token',
params: {
grant_type: 'convert_token',
client_id: '<client_id_from_django_admin>',
client_secret: '<client_secret_from_django_admin>',
backend: 'azuread-tenant-oauth2',
token: accessToken.accessToken
}
})
.then((response) => {
console.log(response);
})
.catch((error) => {
console.log(error);
});
}
};
...
# requirements.txt
asgiref==3.2.10
astroid==2.4.2
attrs==20.2.0
autobahn==20.7.1
Automat==20.2.0
certifi==2020.6.20
cffi==1.14.3
channels==2.4.0
chardet==3.0.4
constantly==15.1.0
cryptography==3.1.1
daphne==2.5.0
defusedxml==0.7.0rc1
Django==3.1.2
django-oauth-toolkit==1.3.3
djangorestframework==3.12.1
djangorestframework-simplejwt==4.4.0
drf-social-oauth2==1.0.8
hyperlink==20.0.1
idna==2.10
incremental==17.5.0
isort==5.6.4
lazy-object-proxy==1.4.3
mccabe==0.6.1
oauthlib==3.1.0
psycopg2-binary==2.8.6
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.20
PyHamcrest==2.0.2
PyJWT==1.7.1
pylint==2.6.0
pyOpenSSL==19.1.0
python3-openid==3.2.0
pytz==2020.1
requests==2.24.0
requests-oauthlib==1.3.0
service-identity==18.1.0
six==1.15.0
social-auth-app-django==4.0.0
social-auth-core==3.3.3
sqlparse==0.4.1
toml==0.10.1
Twisted==20.3.0
txaio==20.4.1
urllib3==1.25.11
whitenoise==5.2.0
wrapt==1.12.1
zope.interface==5.1.2
# settings.py
"""
Django settings for config project.
Generated by 'django-admin startproject' using Django 3.1.2.
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
import os
from datetime import timedelta
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 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 = os.environ['SECRET_KEY']
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = os.environ['DEBUG'] == 'True'
ALLOWED_HOSTS = [
os.environ['DOMAIN'],
'companyapp.local',
'.companyapp.com'
]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-party packages
'channels',
'rest_framework',
'oauth2_provider',
'social_django',
'drf_social_oauth2',
]
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',
'whitenoise.middleware.WhiteNoiseMiddleware',
]
ROOT_URLCONF = 'config.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',
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect',
],
},
},
]
WSGI_APPLICATION = 'config.wsgi.application'
ASGI_APPLICATION = "config.routing.application"
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.environ['PGDATABASE'],
'USER': os.environ['PGUSER'],
'PASSWORD': os.environ['PGPASSWORD'],
'HOST': os.environ['PGHOST'],
'PORT': 5432
}
}
# REST Framework settings
# https://www.django-rest-framework.org/
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
],
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
'oauth2_provider.contrib.rest_framework.OAuth2Authentication', # django-oauth-toolkit >= 1.0.0
'drf_social_oauth2.authentication.SocialAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
}
# SIMPLE_JWT Settings
# https://github.com/davesque/django-rest-framework-simplejwt
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=3),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'SIGNING_KEY': os.environ['SECRET_KEY'],
}
AUTHENTICATION_BACKENDS = (
'social_core.backends.azuread_tenant.AzureADTenantOAuth2',
'drf_social_oauth2.backends.DjangoOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
SOCIAL_AUTH_POSTGRES_JSONFIELD = True
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_KEY = '<client_id_from_azure_apps>'
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_SECRET = '<client_secret_from_azure_apps>'
SOCIAL_AUTH_AZUREAD_TENANT_OAUTH2_TENANT_ID = '<tenant_id_from_azure>'
# 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',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'America/Los_Angeles'
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/'
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/
# STATIC_URL = '/static/'
STATIC_URL = '/api/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# Define the upload and download directories
DOWNLOAD_ROOT = '/mnt/company-files/client-downloads/'
MEDIA_ROOT = '/mnt/company-files/client-submissions/'
# urls.py
from django.contrib import admin
from django.urls import path, include
from users.views import TestView
urlpatterns = [
path('api/admin/', admin.site.urls),
path('api/users/', TestView.as_view()),
path('api/auth/', include('drf_social_oauth2.urls', namespace='drf')),
]
And I get the following error:
[api] Internal Server Error: /api/auth/convert-token
[api] Traceback (most recent call last):
[api] File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
[api] response = get_response(request)
[api] File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response
[api] response = wrapped_callback(request, *callback_args, **callback_kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
[api] return view_func(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
[api] return self.dispatch(request, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
[api] return bound_method(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
[api] return view_func(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/views.py", line 37, in dispatch
[api] return super(CsrfExemptMixin, self).dispatch(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
[api] response = self.handle_exception(exc)
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
[api] self.raise_uncaught_exception(exc)
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
[api] raise exc
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
[api] response = handler(request, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/views.py", line 93, in post
[api] url, headers, body, status = self.create_token_response(request._request)
[api] File "/usr/local/lib/python3.8/site-packages/oauth2_provider/views/mixins.py", line 124, in create_token_response
[api] return core.create_token_response(request)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/oauth2_backends.py", line 30, in create_token_response
[api] return super(KeepRequestCore, self).create_token_response(request)
[api] File "/usr/local/lib/python3.8/site-packages/oauth2_provider/oauth2_backends.py", line 148, in create_token_response
[api] headers, body, status = self.server.create_token_response(uri, http_method, body,
[api] File "/usr/local/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/endpoints/base.py", line 116, in wrapper
[api] return f(endpoint, uri, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/oauth2_endpoints.py", line 91, in create_token_response
[api] return grant_type_handler.create_token_response(
[api] File "/usr/local/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py", line 60, in create_token_response
[api] self.validate_token_request(request)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/oauth2_grants.py", line 100, in validate_token_request
[api] user = backend.do_auth(access_token=request.token)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/utils.py", line 251, in wrapper
[api] return func(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/oauth.py", line 410, in do_auth
[api] data = self.user_data(access_token, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/azuread_tenant.py", line 97, in user_data
[api] id_token = response.get('id_token')
[api] AttributeError: 'NoneType' object has no attribute 'get'
I have verified the request is URL params are formed correctly. Reluctant to post the client_id
, client_secret
, and token
though.
Any suggestions as to what I'm doing wrong?
Yes, it seems you have figured out what you have to do. So, let's try to change the order of your installed apps to:
...
'oauth2_provider',
'social_django',
'drf_social_oauth2',
'rest_framework',
'rest_framework.authtoken',
Your DEFAULT_PERMISSION_CLASSES should also change (unrelated to your problem):
'DEFAULT_PERMISSION_CLASSES': ['rest_framework.permissions.IsAuthenticated'],
Please try switching the order of things on the installed apps. I really don't think this is the cause of your troubles. I personally don't have a clue why you're getting this. Please let me know if you managed so we can plan a debug otherwise.
Gave it a shot and still running into the same issue.
I was playing with DRFSO2_PROPRIETARY_BACKEND_NAME
and when I add:
DRFSO2_PROPRIETARY_BACKEND_NAME = 'azuread-tenant-oauth2'
I get a different error so I'm looking into that now:
[api] Internal Server Error: /api/auth/convert-token
[api] Traceback (most recent call last):
[api] File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
[api] response = get_response(request)
[api] File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 179, in _get_response
[api] response = wrapped_callback(request, *callback_args, **callback_kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
[api] return view_func(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
[api] return self.dispatch(request, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/utils/decorators.py", line 43, in _wrapper
[api] return bound_method(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
[api] return view_func(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/views.py", line 37, in dispatch
[api] return super(CsrfExemptMixin, self).dispatch(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch
[api] response = self.handle_exception(exc)
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception
[api] self.raise_uncaught_exception(exc)
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
[api] raise exc
[api] File "/usr/local/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch
[api] response = handler(request, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/views.py", line 93, in post
[api] url, headers, body, status = self.create_token_response(request._request)
[api] File "/usr/local/lib/python3.8/site-packages/oauth2_provider/views/mixins.py", line 124, in create_token_response
[api] return core.create_token_response(request)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/oauth2_backends.py", line 30, in create_token_response
[api] return super(KeepRequestCore, self).create_token_response(request)
[api] File "/usr/local/lib/python3.8/site-packages/oauth2_provider/oauth2_backends.py", line 148, in create_token_response
[api] headers, body, status = self.server.create_token_response(uri, http_method, body,
[api] File "/usr/local/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/endpoints/base.py", line 116, in wrapper
[api] return f(endpoint, uri, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/oauth2_endpoints.py", line 91, in create_token_response
[api] return grant_type_handler.create_token_response(
[api] File "/usr/local/lib/python3.8/site-packages/oauthlib/oauth2/rfc6749/grant_types/refresh_token.py", line 60, in create_token_response
[api] self.validate_token_request(request)
[api] File "/usr/local/lib/python3.8/site-packages/drf_social_oauth2/oauth2_grants.py", line 100, in validate_token_request
[api] user = backend.do_auth(access_token=request.token)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/utils.py", line 251, in wrapper
[api] return func(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/oauth.py", line 416, in do_auth
[api] return self.strategy.authenticate(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/social_django/strategy.py", line 107, in authenticate
[api] return authenticate(*args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/django/contrib/auth/__init__.py", line 73, in authenticate
[api] user = backend.authenticate(request, **credentials)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/base.py", line 80, in authenticate
[api] return self.pipeline(pipeline, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/base.py", line 83, in pipeline
[api] out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs)
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/base.py", line 113, in run_pipeline
[api] result = func(*args, **out) or {}
[api] File "/usr/local/lib/python3.8/site-packages/social_core/pipeline/social_auth.py", line 5, in social_details
[api] return {'details': dict(backend.get_user_details(response), **details)}
[api] File "/usr/local/lib/python3.8/site-packages/social_core/backends/base.py", line 173, in get_user_details
[api] raise NotImplementedError('Implement in subclass')
[api] NotImplementedError: Implement in subclass
Ok, I came across this looking at the subclass
issue:
https://github.com/RealmTeam/django-rest-framework-social-oauth2/issues/42
I implemented the class here and added it to my settings.py
per here. I did have to comment out the do_auth
method in mine and let the DjangoOAuth2
do that part.
At this point it is authenticating and retrieving user info, but creating users "weirdly" so I'll look into that tomorrow. I'll post what I end up doing to get this working.
Hey @cheslijones can you please show me what are these weirdly looking users?
@wagnerdelima Sorry, that was very unhelpful on my part. So it keeps creating a new user in the database even though it is the same user. There is probably a configuration I'm overlooking.
I got this far pretty late in the day and I'm being pulled in a different direction for the rest of the week, so will probably be Monday before I can take a deeper look at this again.
mm.. that's interesting. Integration Oauth2 with facebook generates users such as: NameSurname etc, instead of this unique id?
Perhaps this is something from Azure itself?
Did you manage to generate the token through the /convert-token endpoint though?
@cheslijones do you have any news on this?
Hi sorry, still working on another project for hopefully just another week and then I'll get back on this.
@cheslijones any feedback here?
@wagnerdelima Sorry, I've been on a whole bunch of other projects and hope to circle back to this in February.
@wagnerdelima Hi, I have the same problem. I see users with id-like usernames in the database. The only difference is that I'm trying to authenticate using GitHub. On top of this problem, I run to IntegrityError:
Traceback (most recent call last):
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
psycopg2.errors.UniqueViolation: duplicate key value violates unique constraint "social_auth_usersocialauth_provider_uid_e6b5e668_uniq"
DETAIL: Key (provider, uid)=(Django, None) already exists.
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/views.py", line 497, in dispatch
self.initial(request, *args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/views.py", line 414, in initial
self.perform_authentication(request)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/views.py", line 324, in perform_authentication
request.user
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/request.py", line 227, in user
self._authenticate()
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework/request.py", line 380, in _authenticate
user_auth_tuple = authenticator.authenticate(self)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/rest_framework_social_oauth2/authentication.py", line 63, in authenticate
user = backend.do_auth(access_token=token)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_core/utils.py", line 248, in wrapper
return func(*args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_core/backends/oauth.py", line 403, in do_auth
return self.strategy.authenticate(*args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_django/strategy.py", line 105, in authenticate
return authenticate(*args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/views/decorators/debug.py", line 42, in sensitive_variables_wrapper
return func(*func_args, **func_kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/contrib/auth/__init__.py", line 76, in authenticate
user = backend.authenticate(request, **credentials)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_core/backends/base.py", line 80, in authenticate
return self.pipeline(pipeline, *args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_core/backends/base.py", line 83, in pipeline
out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_core/backends/base.py", line 113, in run_pipeline
result = func(*args, **out) or {}
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_core/pipeline/social_auth.py", line 35, in associate_user
social = backend.strategy.storage.user.create_social_auth(
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/social_django/storage.py", line 138, in create_social_auth
social_auth = cls.objects.create(user=user, uid=uid, provider=provider)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/query.py", line 453, in create
obj.save(force_insert=True, using=self.db)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/base.py", line 739, in save
self.save_base(using=using, force_insert=force_insert,
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/base.py", line 776, in save_base
updated = self._save_table(
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/base.py", line 881, in _save_table
results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/base.py", line 919, in _do_insert
return manager._insert(
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/query.py", line 1270, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/models/sql/compiler.py", line 1416, in execute_sql
cursor.execute(sql, params)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/debug_toolbar/panels/sql/tracking.py", line 205, in execute
return self._record(self.cursor.execute, sql, params)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/debug_toolbar/panels/sql/tracking.py", line 140, in _record
return method(sql, params)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/backends/utils.py", line 98, in execute
return super().execute(sql, params)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/backends/utils.py", line 66, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/utils.py", line 90, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/Users/milan/.local/share/virtualenvs/backend-pyqZ1kNv/lib/python3.9/site-packages/django/db/backends/utils.py", line 84, in _execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: duplicate key value violates unique constraint "social_auth_usersocialauth_provider_uid_e6b5e668_uniq"
DETAIL: Key (provider, uid)=(Django, None) already exists.
Do you have any idea what can be causing this? Any help is highly appreciated.
Hi @MilanZiaran
This is due to a user having the same email in the database. Then, once the user signs on with social OAuth, the integrity error is raised. This is something I will work on in the near future.
@wagnerdelima Thank you very much for your quick response! Overriding the do_auth
and user_data
methods from the DjangoOAuth2 as shown here fixed this, I was wondering if there's maybe a cleaner solution for this.
@MilanZiaran thank you for commenting. There might be an issue with the azure tenant from the social python. Not necessarily with drf-social-oauth2 framework.
I will investigate this in the future for certain. I will leave this issue open.
@wagnerdelima indeed, I've gone through that problem using Django Python Social Auth only. Seems to be linked with this issue => https://github.com/python-social-auth/social-core/issues/70
I have been trying to implement Azure AD into my application, I get the access token from Microsoft however when I try to send the convert-token
request. I get the following error:
Error: token: Array [ "Ensure this field has no more than 2000 characters." ]
Request payload:
const payload: Credentials = {
backend: 'azuread-v2-tenant-oauth2',
grant_type: 'convert_token',
client_id: import.meta.env.PERI_CLIENT_ID,
client_secret: import.meta.env.PERI_CLIENT_SECRET,
token: response.accessToken,
}
I have tried backend: azuread-tenant-oauth2
and azuread-v2-tenant-oauth2
I have scanned as much of the internet and code as I can but I cannot seem to figure out a solution. Does anyone have any insight?
Would it be possible to extend the max_length of the token CharField as identified as below?
class ConvertTokenSerializer(Serializer):
grant_type = CharField(max_length=50)
backend = CharField(max_length=200)
client_id = CharField(max_length=200)
client_secret = CharField(max_length=500)
token = CharField(max_length=2000) <--- here
I appreciate your time and response. Thank you.
Just for clarity, the accessToken
that comes back from Microsoft is 2500 characters long. Can we increase the max_length
to a higher number, maybe something like 5000?
@wagnerdelima - Sorry to bother - I would very much appreciate your input here. I want to avoid creating a separate issues if possible as I believe the context to this issue is related to Azure specifically.
I can kindly create a PR to increase the ConvertTokenSerializer
token = CharField(max_length=2000)
to max_length=5000 if you deem this is a valid solution.
Thank you for your time and effort.
EDIT: After further thought, I deem this a separate issue: https://github.com/wagnerdelima/drf-social-oauth2/issues/214
Been struggling to get this implemented and find the documentation pretty vague. I'd like to help improve it if I can get this working.
Related documentaiton:
https://python-social-auth.readthedocs.io/en/latest/backends/azuread.html
And then this is what I get when I try to run a POST request in Postman:
I've tried:
backend=azuread_tenant
backend=AzureADTenantOAuth2
backend=azureadtenantoauth2
Really isn't clear what the values of
backend
need to be, what are acceptable, where they can be found, or if it is an arbitrary designation.Also, in these
curl
requests:It seems like
<client_id>
and<client_secret>
are the values for the App in Django Admin, and not for the backend you are using (those are declared in thesettings.py
).There isn't a
convert_token
type for the App in Django Admin, so it is unclear what theAuthorization grant type
should be set at for the App in there.Also,
token=
isn't really discussed at all in the "Integration Examples", nor in thepython-social-auth
, oroauth2-provider
... so not sure if this is a value that is supposed to be generated by the backend provider when you are setting up the integration on their platform, or if it is something that is returned from the OAuth2 request and the/convert-token
is a second step...At any rate, the primary issue is the
invalid backend parameter
.