I am fairly new to the rest api. I am trying to use dj-rest-auth package with simple-jwt for auth handling. Everything works fine in my project. The registration/login etc. But in my django admin site there is a model registered Token which is every time empty. What is the purpose of this model Token? How tokens are managed with dj-rest-auth and simple jwt package ?
settings.py
installed_apps= [
..
'rest_framework',
'rest_framework.authtoken',
'dj_rest_auth',
'django.contrib.sites',
'allauth',
'allauth.account',
'allauth.socialaccount',
'dj_rest_auth.registration',
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
REST_USE_JWT = True
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7),
}
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = 1
ACCOUNT_AUTHENTICATION_METHOD = "username"
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = None
urls.py
path('user/', include('dj_rest_auth.urls')),
path('user/register/', include('dj_rest_auth.registration.urls')),
path('confirm/email/', CustomVerifyEmailView.as_view(), name='account_email_verification_sent'),
You have Token model in admin because you added rest_framework.authtoken to your installed apps. This model is for basic token (stored in db) authentication: https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
JWT (JSON Web Tokens) tokens are stateless and are not stored in db. If you want to read more about JWT I recommend: https://jwt.io/introduction
Related
I've been struggling for over a week with this issue.
This is definitely an authentication problem. I implemented social google login with dj-rest-autj, all-auth and simple JWT. Credential flow is, first, getting access_token from google which is done in frontend. Second, sendind a request to an endpoint 'api/user-google' with the access_token, which returns JWT access_token and refresh_token, which is working fine. Finally sending a request to get user detail, but returned the error message.
Here is the request to get user detail.
await axios.get( `/api/user/${UID}`,{
withCredentials: true,
headers: {
"Authorization": 'JWT ' + `${access_token}`,
"Content-Type":"aplication/json"
}
})
below are relevant source codes.
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user',
'rest_framework.authtoken',
'django.contrib.sites',
'dj_rest_auth',
'dj_rest_auth.registration',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
'rest_framework',
'rest_framework.authtoken'
]
# allauth google provisder config
SOCIALACCOUNT_PROVIDER = {
'google': {
'SCOPE': [
'profile',
'email'
],
'AUTH_PARAMS': {
'access_type': 'online'
},
}
}
SOCIALACCOUNT_EMAIL_VARIFICATION = 'none'
SOCIALACCOUNT_EMAIL_REQUIRED = False
# dj-rest-auth config
SITE_ID = 1
JWT_AUTH_SECURE = True
REST_USE_JWT = True
REST_AUTH_SERIALIZER = {
'USER_DETAILS_SERIALIZER' :'user.serializers.UserSerializer',
}
JWT_AUTH_COOKIE = 'access_token'
JWT_AUTH_REFRESH_COOKIE = 'refresh-token'
# auth user model confi
AUTH_USER_MODEL = 'user.User'
# simple_JWT config
SIMPLE_JWT = {
'AUTH_HEADER_TYPES': ('JWT'),
'ACCESS_TOKEN_LIFETIME': timedelta(days=7),
'ROTATE_REFRESH_TOKENS' : True,
'BLACKLIST_AFTER_ROTATION': True,
'UPDATE_LAST_LOGIN': True,
'USER_ID_FIELD': 'UID',
'USER_ID_CLAIM': 'user_id'
}
# rest framework config
REST_FRAMEWORK = {
'DEFAULT_AUTENTICATION_CLASSES' : (
'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
),
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DATETIME_FORMAT': "%Y-%m-%d %H:%M",
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE':8,
}
user/urls.py
from django.urls import path
from user.apis import UserList, GoogleLogin, UserDetail
urlpatterns = [
path('user/', UserList.as_view()),
path('user-google/', GoogleLogin.as_view()),
path('user/<UID>', UserDetail.as_view()),
]
user/views.py
class GoogleLogin(SocialLoginView):
adapter_class = GoogleOAuth2Adapter
client_class = OAuth2Client
class UserDetail(generics.RetrieveUpdateAPIView ):
pagination_class = None
queryset = User.objects.all()
serializer_class = UserStrageSerializer
lookup_field = 'UID'
user/serializers.py
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = "__all__"
def create(self, validated_data):
user = User.objects.create_user(
validated_data["username"],
validated_data['email'],
validated_data['password']
)
return user
core.urls.py
from django.contrib import admin
from django.urls import path,include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('api/',include('user.urls')),
path('api/auth/',include('dj_rest_auth.urls')),
path('api/auth/registration/',include('dj_rest_auth.registration.urls')),
]+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
My understanding is that
'DEFAULT_AUTENTICATION_CLASSES' : (
'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
),
the above in settings means if request headers include access_token, the user will be isAuthenticated which trigger the 'rest_framework.permissions.IsAuthenticated'
like below
'DEFAULT_PERMISSION_CLASSES' : [
'rest_framework.permissions.IsAuthenticated'
],
So hitting endpoint without access_token returns error "Authentication credentials were not provided", but with access_token should return values from DB, but it is not like this in my case.
any suggestion, please?
**
If I set authentication_classes into UserDetail view directory, returns the expected data.
from dj_rest_auth.jwt_auth import JWTCookieAuthentication as JWT
class UserDetail(generics.RetrieveUpdateAPIView ):
pagination_class = None
queryset = User.objects.all()
authentication_classes = ([JWT]) #added
serializer_class = UserStrageSerializer
lookup_field = 'UID'
but this doesn't solve the isAthenticated problem.
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.
Allauth Github is working without any problem, however, Twitter is not.
When clicking on https://0.0.0.0:9000/accounts/twitter/login/ nothing happens and yet there's no error. Everything is 200 ok.
I'm using SSL in dev environement using django-sslserver.
settings.py
INSTALLED_APPS = [
...
'django.contrib.sites', # new
'allauth', # new
'allauth.account', # new
'allauth.socialaccount', # new
'allauth.socialaccount.providers.github', # new
'allauth.socialaccount.providers.twitter', # new
'sslserver',
]
SOCIALACCOUNT_PROVIDERS = {'github': {}, 'twitter':{}}
AUTHENTICATION_BACKENDS = (
"django.contrib.auth.backends.ModelBackend",
"allauth.account.auth_backends.AuthenticationBackend",
)
SITE_ID = 1
LOGIN_REDIRECT_URL = '/'
I use example.com in my hosts' file:
/etc/hosts
0.0.0.0 example.com
And in the Twitter app, I use these configurations:
This is the social app configuration:
And the site configuration:
Do you see any problem?
The callback URL (can and) should be https://127.0.0.1:9000/accounts/twitter/login/callback/
This is how I solved this problem.
I've set up social-auth-app-django in production. But after facebook redirect I got AuthCanceled and still unable to get it work.
In my user model email address used to sign up. This is my user model:
class User(AbstractUser):
email = models.EmailField(_('email address'), unique=True)
avatar = models.ImageField(blank=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
in settings.py:
MIDDLEWARE = [
...
'social_django.middleware.SocialAuthExceptionMiddleware',
]
TEMPLATES = [
...
'context_processors': [
...
'social_django.context_processors.backends',
'social_django.context_processors.login_redirect',
],
]
AUTHENTICATION_BACKENDS = (
'social_core.backends.facebook.FacebookOAuth2',
'accounts.backends.ModelBackend'
)
LOGIN_URL = '/'
LOGOUT_URL = '/'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
SOCIAL_AUTH_FACEBOOK_KEY = '..' # App ID
SOCIAL_AUTH_FACEBOOK_SECRET = '...' # App Secret
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'id,name,email',
}
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
)
Valid OAuth redirect URIs in facebook login app: https://example.com/oauth/complete/facebook/
Thank you for your time and help.
For those landing on this question, after discussing the topic with OP via email, the problem was caused by an incorrect build of redirect_uri when exchanging the code for an access_token.
The incorrect build of this URI was caused because the Django backend sits behind Nginx, but the proper headers were not passed to it (X-Forwarded-For and Host), so Django was unaware of the right host it was services requests as, in the end, the URLs were pointing to localhost instead of the intended domain.
For me everything is working like a charm: Facebook, Google, Twitter and github.
This may be a configuration problem.
Here's how I've setup everything (note: I'm in developer mode in Facebook, but it works fine with my 2 accounts), I hope this can help:
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'social_django',
'compressor',
'app',
)
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware',
'social_django.middleware.SocialAuthExceptionMiddleware',
)
AUTHENTICATION_BACKENDS = (
'social_core.backends.google.GoogleOAuth2',
'social_core.backends.facebook.FacebookOAuth2',
'social_core.backends.twitter.TwitterOAuth',
'social_core.backends.github.GithubOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
# https://simpleisbetterthancomplex.com/
# tutorial/2016/10/24/how-to-add-social-login-to-django.html
SOCIAL_AUTH_GITHUB_KEY = 'xxx'
SOCIAL_AUTH_GITHUB_SECRET = 'xxx'
SOCIAL_AUTH_TWITTER_KEY = 'xxx'
SOCIAL_AUTH_TWITTER_SECRET = 'xx'
SOCIAL_AUTH_FACEBOOK_KEY = 'xxxx'
SOCIAL_AUTH_FACEBOOK_SECRET = 'xxx'
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
'fields': 'name, email, age_range'
}
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'xxx'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'xxx'
i dont know also what the difference between
https://example.com/oauth/complete/facebook/
and
https://example.com/social-auth/complete/facebook/
in my case i included both and it worked
I searched many days to solve this problem. My solution is that the authentication need to SSL, so you should install ssl and use runsslserver instead of runserver. if you are already using https or after made, you need to add this commend - SOCIAL_AUTH_REDIRECT_IS_HTTPS = True in settings.py. In my case, I'm using https already, so I solved this problem just adding that code.
I am using Djando rest_auth.registration.
My corresponding entry in urls.py is
url(r'^rest-auth/registration/', include('rest_auth.registration.urls'))
My authentication class is rest_framework.authentication.TokenAuthentication
This rest API call works perfectly well.
When I register via this API I get the below response.
{
"key": "3735f13cd69051579156f98ffda338a2d7a89bb5"
}
I also want to include the user_id field in the response. How do I go about doing that. I tried extending the method get_response_data from class RegisterView(CreateAPIView): but unable to do so. Can someone please advise the best practice to achieve this. Code would be ideal. Thanks.
I want to use the rest-auth/registration/ url provided out of box by rest_auth.registration. I do not want to create a separate new URL for this.
My Settings.py as follows
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'sdAPI.apps.SdapiConfig',
'rest_framework',
'rest_framework.authtoken',
'rest_auth',
'rest_framework_swagger',
'rest_auth.registration',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
'allauth.socialaccount.providers.google',
'django_extensions',
]
# auth and allauth settings
LOGIN_REDIRECT_URL = '/'
SOCIALACCOUNT_QUERY_EMAIL = True
SOCIALACCOUNT_PROVIDERS = {
'facebook': {
'SCOPE': ['email', 'publish_stream'],
'METHOD': 'oauth2' # instead of 'oauth2'
}
}
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
SITE_ID = 1
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
REST_SESSION_LOGIN = False
My urls.py as follows
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^user/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
url(r'^rest-auth/', include('rest_auth.urls')),
url(r'^rest-auth/registration/',include('rest_auth.registration.urls')),
]
I think you only need to override the TOKEN_SERIALIZER option in your REST_AUTH_SERIALIZERS configuration.
from rest_framework.authtoken.models import Token
class TokenSerializer(serializers.ModelSerializer):
class Meta:
model = Token
fields = ('key', 'user')
Then, set it in your settings.py as shown in the docs,
REST_AUTH_SERIALIZERS = {
'LOGIN_SERIALIZER': 'path.to.custom.LoginSerializer',
'TOKEN_SERIALIZER': 'path.to.custom.TokenSerializer',
...
}