I've created a new Django Rest API thanks to Django Rest Framework and I want to use two type of authentication : TokenAuthentication AND SocialAuthentication with two providers Facebook and Google.
Token authentication is success (with this syntax : Authorization: Token <token>
. However, I can't get it to work with the SocialAuthentication. When I get the access_token from my POST in GoogleSocialLoginView, I can't use it to login in my others API call headers (I use an authenticated permissions for others CRUD calls). My syntax for social authentication is :
Authorization : Bearer <token>
So the users are registered successfully in database, but they can't authenticated us to the API after.
This is a part of my settings.py
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.sites',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework_swagger',
'rest_framework.authtoken',
"dj_rest_auth",
'dj_rest_auth.registration',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.google',
# Local apps we created
'api',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'thetiptop.urls'
AUTH_USER_MODEL = 'api.Users'
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
REST_FRAMEWORK = {
'DEFAULT_SCHEMA_CLASS':'rest_framework.schemas.coreapi.AutoSchema',
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
REST_AUTH_SERIALIZERS = {
"LOGIN_SERIALIZER": "api.serializers.CustomLoginSerializer",
}
REST_USE_JWT = True
ACCOUNT_LOGOUT_ON_GET = True
OLD_PASSWORD_FIELD_ENABLED = True
SITE_ID = 2
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
CORS_ALLOW_ALL_ORIGINS = True
SOCIALACCOUNT_QUERY_EMAIL = True
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_USERNAME_REQUIRED = False
SOCIALACCOUNT_PROVIDERS = {
'google': {
'SCOPE': ['email'],
'AUTH_PARAMS': { 'access_type': 'online' }
},
'facebook': {
'METHOD': 'oauth2',
'SCOPE': ['email'],
'AUTH_PARAMS': {'auth_type': 'reauthenticate'},
'INIT_PARAMS': {'cookie': True},
'LOCALE_FUNC': lambda request: 'en_US',
'EXCHANGE_TOKEN': True,
'VERIFIED_EMAIL': False,
'VERSION': 'v13.0'
}
}
My url.py file list this URL :
path('auth/login/', obtain_auth_token, name='login'),
path('auth/', include('dj_rest_auth.urls')),
path('auth/registration/', include('dj_rest_auth.registration.urls')),
path('auth/facebook/', FacebookLogin.as_view(), name='fb_login'),
path('auth/google/', GoogleLogin.as_view(), name='google_login'),
path('accounts/', include('allauth.urls')),
And finally, the GoogleLogin and FacebookLogin are the same as :
from allauth.socialaccount.providers.facebook.views import FacebookOAuth2Adapter
from allauth.socialaccount.providers.google.views import GoogleOAuth2Adapter
from allauth.socialaccount.providers.oauth2.client import OAuth2Client
class FacebookLogin(SocialLoginView):
adapter_class = FacebookOAuth2Adapter
class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client
Finally, I use this type of authentication for others CRUD calls :
from rest_framework import permissions
permission_classes = [permissions.IsAuthenticated]
Do you know a solution to use a token authentication and a allauth social authentication in the same User class ?
PS: I'm consuming the API from an Angular app.
It's important to keep in mind that there is a difference between the default authentication system in Django and the authentication system for DRF. In your DRF configuration, you have only specified two classes: rest_framework.authentication.SessionAuthentication
and rest_framework.authentication.TokenAuthentication
. The SessionAuthentication
is the only one that is compatible with the default Django session authentication backend (but that may not be what you want anyhow).
As covered in the DRF API guide if you are using session-based authentication, you must include a CSRF token for authenticated requests:
If you're using an AJAX-style API with SessionAuthentication, you'll need to make sure you include a valid CSRF token for any "unsafe" HTTP method calls, such as PUT, PATCH, POST or DELETE requests. [...]
CSRF validation in REST framework works slightly differently from standard Django due to the need to support both session and non-session based authentication to the same views. This means that only authenticated requests require CSRF tokens, and anonymous requests may be sent without CSRF tokens.
So, if you are using a user's session (assuming you've authenticated the user's session previously) you will need to pass CSRF tokens when calling the DRF API in addition to the session cookie.
You may want to consider using an authentication method that works directly with DRF such as one recommended in the DRF docs like drf-social-oauth2
. Then add it to your DRF authentication classes directly.