Allauth will not return the desired token payload - django-rest-framework

I have a django-rest-auth project called merchant. within it I have implemented django-restauth and allauth packages with JWT.
Everything works OK. However, I wish to return additional fields in the JWT token and here's my implementation of it.
In app.views.py
def jwt_response_payload_handler(token, user=None, request=None):
return {
'token': token,
'user': User_Serializer(user, context={'request':request}).data
}
serializers.py
class User_Serializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['email', 'username', 'is_staff']
settings.py
JWT_AUTH = {
'JWT_RESPONSE_PAYLOAD_HANDLER': 'merchant.coin_app.views.jwt_response_payload_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=10),
'JWT_AUTH_HEADER_PREFIX': 'JWT'
}
The payload returned does not contain email/is_staff. Perhaps I am missing something.

The registered handler in the JWT_RESPONSE_PAYLOAD_HANDLER setting option is invoked after the JWT token is generated.
The handler requiring customization is JWT_PAYLOAD_HANDLER which creates the payload object that is tokenized and not JWT_RESPONSE_PAYLOAD_HANDLER.
In your project settings, configure
JWT_AUTH = {
'JWT_PAYLOAD_HANDLER': 'merchant.coin_app.views.jwt_payload_handler',
'JWT_EXPIRATION_DELTA': datetime.timedelta(minutes=10),
'JWT_AUTH_HEADER_PREFIX': 'JWT'
}
Then in your view, extend the result of rest_framework_jwt.utils.jwt_payload_handler
import rest_framework_jwt.utils.jwt_payload_handler as base_jwt_payload_handler
def jwt_response_payload_handler(user):
payload = base_jwt_payload_handler(user)
payload['user'] = User_Serializer(user).data
return payload

Related

How to differentiate my views in DRF swagger (yasg)?

I have an api made with djangorestframework and I want to add a swagger. It works well. In my api I have some views that require authentication with Basic or JWT so I set this up in my settings. The problem is that in my swagger gui I do not manage to tell which view is not requiring authentication. Do you know how to do that.
#api/urls.py
from drf_yasg import openapi
schema_view = get_schema_view(
openapi.Info(
title="Snippets API",
default_version='v1',
description="Test description",
terms_of_service="https://www.google.com/policies/terms/",
contact=openapi.Contact(email="contact#snippets.local"),
license=openapi.License(name="BSD License"),
),
public=True,
permission_classes=[permissions.AllowAny],
)
urlpatterns = [
path('mesures/', views.get_all),
path('mesure-add/', views.add_mesure),
path('token/', TokenObtainPairView.as_view(), name='obtain_tokens'),
path('token/refresh/', TokenRefreshView.as_view(), name='refresh_token'),
path('api-auth/', include('rest_framework.urls')),
path('register/', views.register_user),
path('redoc/', schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'),
]
In my settings.py
# in my settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(days=int(os.getenv("TOKEN_DAYS_LIFE"))),
}
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS': {
'Basic': {
'type': 'basic'
},
'Bearer': {
'type': 'apiKey',
'name': 'Authorization',
'in': 'header'
}
}
}
In my views.py
#views.py
#swagger_auto_schema(methods=['post'], request_body=MesureSerializer)
#api_view(['POST'])
#permission_classes([IsAuthenticated])
def add_mesure(request):
serializer = MesureSerializer(data=request.data, context={'request':request})
# this is to provide the user automatically from token auth
# no need to provide it from the post request
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
else:
return Response(serializer._errors)
#swagger_auto_schema(methods=['post'], request_body=UserSerializer)
#api_view(['POST'])
def register_user(request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
as you can see my view register_user should not show a authentication in my swagger but this is not the case.
It is ok, I just needed to use the security=[] in #swagger_auto_schema for the specific view I want to tell there is no authentication required.
#swagger_auto_schema(methods=['post'], request_body=UserSerializer, security=[])

Authentication credentials were not provided - Django

I'm having trouble with this error. Tried almost all available solutions but nothing working for me. At frontend side I am using Angular 6 and I am pretty sure it's not error from it. Hoping for a response soon and thanks in advance guys.
register/url.py
from django.urls import path, include
from rest_framework import routers
from . import views
from rest_framework.authtoken.views import ObtainAuthToken
router = routers.DefaultRouter()
router.register('users', views.UserViewSet)
# Wire up our API using automatic URL routing.
# Additionally, we include login URLs for the browsable API.
urlpatterns = [
path('', include(router.urls)),
#path('auth/', include('rest_framework.urls', namespace='rest_framework')),
path('auth/', ObtainAuthToken.as_view()),
]
serialier.py
from django.contrib.auth.models import User
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email', 'password')
extra_kwargs = { 'password' : { 'write_only' : True , 'required':True } }
def create(self, validated_data):
user = User.objects.create_user(**validated_data)
return user
view.py
from django.contrib.auth.models import User
from rest_framework import viewsets
from .serializers import UserSerializer
from rest_framework.permissions import IsAuthenticated
class UserViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows users to be viewed or edited.
"""
queryset = User.objects.all().order_by('-date_joined')
serializer_class = UserSerializer
authentication_classes = (TokenAuthentication, SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
setting.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'rest_framework.authtoken',
'register',
'corsheaders',
]
The following error is displayed in the browser's console:
{“detail”:“Authentication credentials were not provided.”}
Your viewset has the IsAuthenticated permission class. In other words, an user must be authenticated in order to retrieve, update or even create an instance. Make sure that the appropriate headers are included in your requests.
For example, for a token authentication, as stated by Django Rest Framework documentation
For clients to authenticate, the token key should be included in the
Authorization HTTP header. The key should be prefixed by the string literal
"Token", with whitespace separating the two strings. For example:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
In the particular case of an account creation, I'm not sure that it is intended for your application to require an user authentication.

Grails spring security oauth2 provider request for resource with correct bearer token redirects to login

As the title implies, I have a controller method protected by the oAuth2 plugin, but when I send a request to it including a correct Authorization: Bearer <token> (using Postman), the response I get is the HTML for the login page.
Method in question:
#Secured(["ROLE_USER", "#oauth2.clientHasAnyRole('ROLE_CLIENT', 'ROLE_TRUSTED_CLIENT')"])
def getUserData(){
response.setContentType("application/json")
User u = springSecurityService.currentUser
println u
render u.mseUserInfo
}
Config.groovy:
// Added by the Spring Security Core plugin:
grails.plugin.springsecurity.auth.loginFormUrl = '/mse/login'
grails.plugin.springsecurity.userLookup.userDomainClassName = 'cz.improvisio.MSEauthProvider.user.User'
grails.plugin.springsecurity.userLookup.authorityJoinClassName = 'cz.improvisio.MSEauthProvider.user.UserRole'
grails.plugin.springsecurity.authority.className = 'cz.improvisio.MSEauthProvider.user.Role'
grails.plugin.springsecurity.controllerAnnotations.staticRules = [
'/oauth/authorize.dispatch':[
"ROLE_USER",
"isFullyAuthenticated()"
],
'/oauth/token.dispatch':[
"ROLE_USER",
"isFullyAuthenticated()"
],
'/mse/login':["permitAll"],
'/mse/':["permitAll"],
'/**':["permitAll"]]
// Added by the Spring Security OAuth2 Provider plugin:
grails.plugin.springsecurity.oauthProvider.clientLookup.className = 'cz.improvisio.MSEauthProvider.user.Client'
grails.plugin.springsecurity.oauthProvider.authorizationCodeLookup.className = 'cz.improvisio.MSEauthProvider.user.AuthCode'
grails.plugin.springsecurity.oauthProvider.accessTokenLookup.className = 'cz.improvisio.MSEauthProvider.user.AccessToken'
grails.plugin.springsecurity.oauthProvider.refreshTokenLookup.className = 'cz.improvisio.MSEauthProvider.user.RefreshToken'
grails.plugin.springsecurity.filterChain.chainMap = [
'/oauth/token': 'JOINED_FILTERS,-oauth2ProviderFilter,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-exceptionTranslationFilter',
'/securedOAuth2Resources/**': 'JOINED_FILTERS,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-oauth2BasicAuthenticationFilter,-exceptionTranslationFilter',
'/**': 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter,-oauth2ProviderFilter,-clientCredentialsTokenEndpointFilter,-oauth2BasicAuthenticationFilter,-oauth2ExceptionTranslationFilter'
]
This is the client creation from Bootstrap.groovy:
new Client(
clientId: 'testClient',
authorizedGrantTypes: [
'authorization_code',
'refresh_token',
'implicit',
'password',
'client_credentials'
],
authorities: ['ROLE_CLIENT'],
scopes: ['read', 'write'],
redirectUris: ['http://test.com']).save(flush: true)
And one more slightly related question: I couldnt find a way to get the User to whose resources the access token should be linked to, so I assumed Id be able to get it through springSecurityService. Is this the correct way of doing so? Or do I need to pass the userId to the method (and will OpenAM do it?)?
Turns out I didnt have the proper filter chain set up for my action. Changing config to
grails.plugin.springsecurity.filterChain.chainMap = [
'/oauth/token': 'JOINED_FILTERS,-oauth2ProviderFilter,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-exceptionTranslationFilter',
'/securedOAuth2Resources/**': 'JOINED_FILTERS,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-oauth2BasicAuthenticationFilter,-exceptionTranslationFilter',
'/myController/getUserData': 'JOINED_FILTERS,-securityContextPersistenceFilter,-logoutFilter,-authenticationProcessingFilter,-rememberMeAuthenticationFilter,-oauth2BasicAuthenticationFilter,-exceptionTranslationFilter',
'/**': 'JOINED_FILTERS,-statelessSecurityContextPersistenceFilter,-oauth2ProviderFilter,-clientCredentialsTokenEndpointFilter,-oauth2BasicAuthenticationFilter,-oauth2ExceptionTranslationFilter'
]
fixed it.

Grails Spring Security Rest - 403 Forbidden

So I just started learning Grails, and I am trying incorporate the Spring Security REST plugin into my app, the plugin is installed in addition to spring security core which is working. In my REST client, when I hit "api/login" I am able to get an access token and it says I have the role of "ROLE_ADMIN", but then when I try to hit something using that, I keep getting a 403 Forbidden. In Postman, the REST client I am using, I have my Authorization header with "Bearer {key}", with my url of "http://localhost:8080/test/api/secret" and it gives the 403 error. I am trying to setup the log4j logging to see any other issues, but does anyone know what I should look into, any help would be appreciated. I provided my classes below if that helps, I generally used default values for everything such as the UrlMappings.
RandomController.groovy
package test
import grails.plugin.springsecurity.annotation.Secured
#Secured(['IS_AUTHENTICATED_ANONYMOUSLY'])
class MyController {
#Secured(['ROLE_ADMIN'])
def secret() {
render "You have ACCESS!!!"
}
}
Bootstrap.groovy
class BootStrap {
def init = { servletContext ->
def adminRole = new SecRole(authority: 'ROLE_ADMIN').save(flush: true)
def testUser = new SecUser(username: 'bob', password: 'test')
testUser.save(flush: true)
SecUserSecRole.create testUser, adminRole, true
}
def destroy = {
}
}
UrlMappings.groovy
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?(.$format)?"{
constraints {
// apply constraints here
}
}
"/api/$controller/$action?/$id?(.$format)?"{ constraints { // apply constraints here
} }
"/"(view:"/index")
"500"(view:'/error')
}
}
For what I can see from the code you posted, if you invoke url http://localhost:8080/test/api/secret, it should execute default action (maybe index) in SecretController but the controller you posted is called MyController.
To investigate further, you should enable more verbose logging using log4j configuration as suggested in the doc http://alvarosanchez.github.io/grails-spring-security-rest/1.5.1/docs/guide/debugging.html

How to access OAuth2 client_id in django rest framework?

I have my django rest framework API protected by Oauth2 toolkit, but I don't know how to get the client_id of the current authorized requests.
class RequestTransactionView(APIView):
def post(self, request, format=None):
transaction = self.parse_dictionary(request.DATA)
return Response(str(transaction.goid))
I have inspected the request object, which gave:
['DATA', 'FILES', 'QUERY_PARAMS', '_CONTENTTYPE_PARAM', '_CONTENT_PARAM', '_METHOD_PARAM', '__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattr__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_auth', '_authenticate', '_authenticator', '_content_type', '_data', '_default_negotiator', '_files', '_load_data_and_files', '_load_method_and_content_type', '_load_stream', '_method', '_not_authenticated', '_parse', '_perform_form_overloading', '_request', '_stream', '_user', 'accepted_media_type', 'accepted_renderer', 'auth', 'authenticators', 'content_type', 'method', 'negotiator', 'parser_context', 'parsers', 'stream', 'successful_authenticator', 'user']
then I inspected the successful_authenticator I got:
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'authenticate', 'authenticate_header', 'www_authenticate_realm']
I also inspected other obvious hints but no luck.
It's easier than I thought, when I was printing out request.auth I got a string, and I thought it's a string type, but then I figured out it's a AccessToken, so from there I can get the application directly.
print request.auth.application

Resources