Giter Club home page Giter Club logo

Comments (11)

Schweigi avatar Schweigi commented on May 17, 2024 3

The following example demonstrates how to do an authentication without involving the UserModel. We use it to authenticate service related requests.

myapp/authentication.py

from rest_framework_jwt.authentication import JSONWebTokenAuthentication

class AuthenticatedServiceClient:
    def is_authenticated(self):
        return True

class JwtServiceOnlyAuthentication(JSONWebTokenAuthentication):
    def authenticate_credentials(self, payload):
        # Assign properties from payload to the AuthenticatedServiceClient object if necessary
        return AuthenticatedServiceClient()

myapp/settings.py

REST_FRAMEWORK = {
    'UNAUTHENTICATED_USER': None,
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'myapp.authentication.JwtServiceOnlyAuthentication',
    ),
}

request.user will then contain the AuthenticatedServiceClient object.

from django-rest-framework-jwt.

jpadilla avatar jpadilla commented on May 17, 2024

@mgonto totally, you'd need to build your own authentication backend.

from django-rest-framework-jwt.

mgonto avatar mgonto commented on May 17, 2024

@jpadilla do you have a link to something I can read to find out about this? I'm a DJango n00b sorry :D.

Thanks!

from django-rest-framework-jwt.

jpadilla avatar jpadilla commented on May 17, 2024

Check out https://github.com/GetBlimp/django-rest-framework-jwt/blob/master/rest_framework_jwt/authentication.py#L60-76

from django-rest-framework-jwt.

mgonto avatar mgonto commented on May 17, 2024

So I should fork your project, change that code and then use my fork, right?

If that's the case, thanks and close the issue :)

from django-rest-framework-jwt.

jpadilla avatar jpadilla commented on May 17, 2024

You could define your own or subclass this one from code base. Be sure to update Django REST Framework's DEFAULT_AUTHENTICATION_CLASSES to contain your module.

from django-rest-framework-jwt.

mgonto avatar mgonto commented on May 17, 2024

Ohh understood.

Thanks for the info :).


Martin Gontovnikas
Software Engineer
Buenos Aires, Argentina

Twitter: @mgonto (https://twitter.com/mgonto)
Linkedin: http://www.linkedin.com/in/mgonto
Github: https://github.com/mgonto

On Friday, 13 de June de 2014 at 17:48, José Padilla wrote:

You could define your own or subclass this one from code base. Be sure to update Django REST Framework's DEFAULT_AUTHENTICATION_CLASSES to contain your module.


Reply to this email directly or view it on GitHub (#22 (comment)).

from django-rest-framework-jwt.

jpadilla avatar jpadilla commented on May 17, 2024

@mgonto No problem

from django-rest-framework-jwt.

Aameer avatar Aameer commented on May 17, 2024

Hey Guys,
We have been using drf-jwt for a while now. I added some customization by to avoid concurrent logins. Now our use case requires us to use JWT with payload as secretKey and nothing to do with user model in addition to the previous running authentication mechanism. I tried to follow @Schweigi 's approach but what was happening was when I tried to generate a token it was asking me to fill out the username and password. Btw these secretKeys are saved in database. Disassociating UserModel from payload could open up drf-jwt for lot of other cases.

from rest_framework_jwt.serializers import *
from rest_framework_jwt.authentication import JSONWebTokenAuthentication 

def jwt_payload_handler_secret_based(token):
    """
    Updated jwt payload handler to add last login unix timestamp for token/user-less in payload.
    """
    warnings.warn(
        'The following fields will be removed in the future: '
        '`email` and `user_id`. ',
        DeprecationWarning
    )

    payload = {
        'token': token,
        'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA,
        'jwt_indee_verification_id': str(token.last_login.timestamp())
    }

    # Include original issued at time for a brand new token,
    # to allow token refresh
    if api_settings.JWT_ALLOW_REFRESH:
        payload['orig_iat'] = timegm(
            datetime.utcnow().utctimetuple()
        )

    if api_settings.JWT_AUDIENCE is not None:
        payload['aud'] = api_settings.JWT_AUDIENCE

    if api_settings.JWT_ISSUER is not None:
        payload['iss'] = api_settings.JWT_ISSUER

    return payload


class ServiceOnlyJSONWebTokenSerializer(JSONWebTokenSerializer):
    """
    Serializer class used to validate a token.
    Returns a JSON Web Token that can be used to authenticate later calls.
    """

    def __init__(self, *args, **kwargs):
        """
        Dynamically add the USERNAME_FIELD to self.fields.
        """
        super(JSONWebTokenSerializer, self).__init__(*args, **kwargs)
        self.fields['tdvalue'] = serializers.IntegerField(required=False)
        self.fields['jwt_indee_service_id'] = serializers.CharField(required=True)

    def validate(self, attrs):

        credentials = {
            self.username_field: attrs.get(self.username_field),
            'password': attrs.get('password')
        }
        try:
            fycTokenObj = FycToken.objects.get(
                token_key=attrs.get('jwt_indee_service_id')
            )
        except Exception as fyctoken_fetch_error:
            msg = _(
                'Some error occured while logging in.')
            raise serializers.ValidationError(msg)

        if all(credentials.values()):

            if fycTokenObj:
                if not fycTokenObj.active:
                    msg = _('Token is disabled.')
                    raise serializers.ValidationError(msg)

                tmp_exp = api_settings.JWT_EXPIRATION_DELTA

                if attrs.get('tdvalue') is not None:
                    api_settings.JWT_EXPIRATION_DELTA = timedelta(
                        seconds=attrs.get('tdvalue'))

                # save the login time stamp
                fycTokenObj.last_login = datetime.now()
                fycTokenObj.save()

                jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
                payload = jwt_payload_handler_secret_based(fycTokenObj.token_key)

                return {
                    'token': jwt_encode_handler(payload),
                    'user': fycTokenObj
                }

            else:
                msg = _('Unable to log in with provided credentials. Pappa')
                raise serializers.ValidationError(msg)
        else:
            msg = _('Must include "jwt_indee_service_id".')
            raise serializers.ValidationError(msg)

class ServiceOnlyJSONWebTokenAuthentication(JSONWebTokenAuthentication):
    """
    Overriding the default JSONWebTokenAuthentication to accomodate tokens for apps.
    https://github.com/GetBlimp/django-rest-framework-jwt/issues/22#issuecomment-166451133
    https://www.django-rest-framework.org/api-guide/authentication/
    """

    def authenticate_credentials(self, payload):
        """
        Returns an active user that matches the payload's user id and email.
        """
        jwt_indee_service_id = jwt_get_indee_service_id_from_payload(payload)
        jwt_indee_verification_id = jwt_get_indee_ll_id_from_payload(payload)

        if not jwt_indee_service_id:
            msg = _('Invalid Token.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            fycTokenObj = FycToken.objects.get(token_id = jwt_indee_service_id)
        except User.DoesNotExist:
            msg = _("Couldn't find the Token. Please reach out to [email protected]")
            raise exceptions.AuthenticationFailed(msg)

        if not fycTokenObj.active:
            msg = _('Token has been disabled by the sender.')
            raise exceptions.AuthenticationFailed(msg)

        if fycTokenObj.expiry_date < timezone.now() :
            msg = _('Token has expired.')
            raise exceptions.AuthenticationFailed(msg)

        if jwt_indee_verification_id != str(fycTokenObj.last_login.timestamp()):
            msg = _('This login was overriden, concurrent logins are not allowed.')
            raise exceptions.AuthenticationFailed(msg)

        return AuthenticatedServiceClient()

In the urls.py I have

from rest_framework_jwt.views import ObtainJSONWebToken
urlpatterns += patterns('',
                        url(r'^api-token-auth/test/', ObtainJSONWebToken.as_view(
                            serializer_class=ServiceOnlyJSONWebTokenSerializer)),
)

When I try and generate the token I see this error:
userdetailsstillasked

Could anyone provide some pointers on this? Thanks. I also checked #349 and #145 but wasn't able to figure this out. Thanks

from django-rest-framework-jwt.

Alex3917 avatar Alex3917 commented on May 17, 2024

@Aameer You can just use PyJWT library for signing and decoding custom tokens. Alternatively, Django has built-in functionality for signing strings and tokens:

https://docs.djangoproject.com/en/2.1/topics/signing/

from django-rest-framework-jwt.

Aameer avatar Aameer commented on May 17, 2024

@Alex3917 Thanks for you comment. What I ended up doing is mentioned as under. This made sure my current system with drf-jwt is working as expected and I have this parallel system which uses secrets alone as authentication mechanism and re-uses many of the things used by drf-jwt. Would have loved to see something like this in built with drf-jwt, where we could even decouple users.

URLS

from apps.myapps.api_views import FycTokenGenrator
urlpatterns += patterns('',
                        url(r'^loginWithSecret/',                                                                                                                                                                          
                            FycTokenGenrator.as_view(),
                            name='fyc-token'),

                        )

VIEWS

from rest_framework import permissions, status, generics

from apps.mytokenapp.models import FycToken
from apps.mytokenapp.serializers import FycTokenSerializerOpen

from apps.myapps.utils import CustomJSONWebTokenAuthentication,\
ServiceOnlyJSONWebTokenAuthentication, jwt_payload_handler_token_based,
get_encoded_jwt


class FycTokenGenrator(generics.RetrieveUpdateDestroyAPIView):
    """
    API Endpoint to get the secret from user and reply with jwt token.
    For now added same auth over it to test.
    """
    authentication_classes = (ServiceOnlyJSONWebTokenAuthentication,)
    permission_classes = (permissions.AllowAny,)
    serializer_class = FycTokenSerializerOpen

    def get_permissions(self):
        """
        Override the permissions for POST
        """
        if self.request.method == 'POST':
            self.permission_classes = (permissions.AllowAny,)
        return super(FycTokenGenrator, self).get_permissions()

    def get(self, request):
        return Response({'message': "GET method not allowed"},
                        status=status.HTTP_405_METHOD_NOT_ALLOWED)

    def post(self, request):
        try:
            token_key = request.data.get('jwt_service_id', None)
            if token_key:
                try:
                    fycTokenObj = FycToken.objects.get(
                        fyctoken_key=token_key
                    )
                except Exception as fyctoken_fetch_error:
                    msg = 'Some error occured while logging in.'
                    return Response({'message': msg},
                                    status=status.HTTP_403_FORBIDDEN)
                fycTokenObj.last_login = timezone.now()
                fycTokenObj.save()
                payload = jwt_payload_handler_token_based(fycTokenObj)
                encoded_jwt = get_encoded_jwt(payload)
                return Response(
                    {'token': encoded_jwt}, status=status.HTTP_200_OK)
            else:
                msg = 'Some error occured while logging in.'
                return Response({'message': msg},status=status.HTTP_403_FORBIDDEN)
        except Exception as post_fyc_token_generator_error:
            message = "Some error occured while logging in"
            return Response({
                "message": str(message)},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    def put(self, request):
        return Response({'message': "Put method not allowed"},
                        status=status.HTTP_405_METHOD_NOT_ALLOWED)

    def patch(self, request):
        return Response({'message': "Patch method not allowed"},
                        status=status.HTTP_405_METHOD_NOT_ALLOWED)

    def delete(self, request):
        return Response({'message': "Delete method not allowed"},
                        status=status.HTTP_405_METHOD_NOT_ALLOWED)

AuthMiddleWare

import jwt
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

def jwt_payload_handler_token_based(tokenObj):
    """
    Updated jwt payload handler to add last login unix timestamp 
    for token/user-less in payload.
    """
    warnings.warn(
        'The following fields will be removed in the future: '
        '`email` and `user_id`. ',
        DeprecationWarning
    )

    payload = {
        'jwt_service_id': tokenObj.fyctoken_key,
        'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA,
        'jwt_verification_id': str(tokenObj.last_login.timestamp())
    }

    # Include original issued at time for a brand new token,
    # to allow token refresh
    if api_settings.JWT_ALLOW_REFRESH:
        payload['orig_iat'] = timegm(
            datetime.utcnow().utctimetuple()
        )

    if api_settings.JWT_AUDIENCE is not None:
        payload['aud'] = api_settings.JWT_AUDIENCE

    if api_settings.JWT_ISSUER is not None:
        payload['iss'] = api_settings.JWT_ISSUER

    print(payload)
    return payload

def get_encoded_jwt(payload):
    encoded_jwt = jwt.encode(
        payload,
        settings.SECRET_KEY,
        algorithm='HS256'
    )
    return encoded_jwt

class AuthenticatedServiceClient(object):

    def is_authenticated(self, fycTokenObj=None):
        """
        Always return True. This is a way to tell if the token has been
        authenticated
        """
        return True

    def __init__(self,fycTokenObj=None):
        """
        This will assign a large enough id for the with secret only user/service so that it doesn't
        clash with actual Users' model. (any suggestions to improve this portion are welcome!)
        """
        total_users_supported_without_clashing_throttle = 999999999
        self.pk = total_users_supported_without_clashing_throttle + fycTokenObj.id #model id which has secrets


def jwt_get_ll_id_from_payload(payload):
    """
    Gets the last login unix time stamp from the payload
    """
    return payload.get('jwt_verification_id', None)

def jwt_get_service_id_from_payload(payload):
    """
    Gets the service id payload
    """
    return payload.get('jwt_service_id', None)

# TOKEN MIDDLE WARE
class ServiceOnlyJSONWebTokenAuthentication(JSONWebTokenAuthentication):
    """
    Overriding the default JSONWebTokenAuthentication to accomodate tokens for apps.
    https://github.com/GetBlimp/django-rest-framework-jwt/issues/22#issuecomment-166451133
    https://www.django-rest-framework.org/api-guide/authentication/
    """

    def authenticate_credentials(self, payload):
        """
        Returns an active user that matches the payload's user id and email.
        """
        jwt_service_id = jwt_get_service_id_from_payload(payload)
        jwt_verification_id = jwt_get_ll_id_from_payload(payload)

        if not jwt_service_id:
            msg = _('Invalid Token.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            # from model which has the secret
            fycTokenObj = FycToken.objects.get(fyctoken_key = jwt_service_id)
        except Exception as fycTokenObjDoesNotExistError:
            msg = _("Couldn't find the Token.")
            raise exceptions.AuthenticationFailed(msg)

        if not fycTokenObj.active:
            msg = _('Token has been disabled by the sender.')
            raise exceptions.AuthenticationFailed(msg)

        if fycTokenObj.expiry_date < timezone.now() :
            msg = _('Token has expired.')
            raise exceptions.AuthenticationFailed(msg)

        if jwt_verification_id != str(fycTokenObj.last_login.timestamp()):
            msg = _('This login was overriden, concurrent logins are not allowed.')
            raise exceptions.AuthenticationFailed(msg)

        return AuthenticatedServiceClient(fycTokenObj)

from django-rest-framework-jwt.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.