Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How can I use this to verify Auth0 token? #32

Open
magedhelmy1 opened this issue Sep 11, 2023 · 3 comments
Open

How can I use this to verify Auth0 token? #32

magedhelmy1 opened this issue Sep 11, 2023 · 3 comments

Comments

@magedhelmy1
Copy link

Below is a working example from Django Rest Framework:


REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
    ],
    'EXCEPTION_HANDLER': 'messages_api.views.api_exception_handler',
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_simplejwt.authentication.JWTTokenUserAuthentication',
    ],
}

# JWT

AUTH0_DOMAIN = get_env_var('AUTH0_DOMAIN')
AUTH0_AUDIENCE = get_env_var('AUTH0_AUDIENCE')

SIMPLE_JWT = {
    'ALGORITHM': 'RS256',
    'JWK_URL': f'https://{AUTH0_DOMAIN}/.well-known/jwks.json',
    'AUDIENCE': AUTH0_AUDIENCE,
    'ISSUER': f'https://{AUTH0_DOMAIN}/',
    'USER_ID_CLAIM': 'sub',
    'AUTH_TOKEN_CLASSES': ('authz.tokens.Auth0Token',),
}


class ProtectedMessageApiView(MessageApiView):
    text = "This is a protected message."
    permission_classes = [IsAuthenticated]

Now, how to make it check that the Auth0 is correct and protect the below view until the Auth0 is verified:

@router.get("/protected", response={200: MessageSchema, 403: ErrorResponse})
def protected_message(request):
    if not request.auth:
        return ErrorResponse(message="User is not authenticated"), 403
    return get_message("This is a protected message.")
@eadwinCode
Copy link
Owner

eadwinCode commented Sep 13, 2023

@magedhelmy1 I dont quite get your question. Can you explain more please?

@eadwinCode
Copy link
Owner

from ninja_jwt.authentication import JWTAuth
...

@router.get("/protected", response={200: MessageSchema, 403: ErrorResponse}, auth=JWTAuth())
def protected_message(request):
    if not request.auth:
        return ErrorResponse(message="User is not authenticated"), 403
    return get_message("This is a protected message.")

@gmmarc
Copy link

gmmarc commented Jan 15, 2025

@magedhelmy1 this took me a while, but I figured it out:

On my settings.py:

AUTH0_AUDIENCE = env("AUTH0_AUDIENCE")
AUTH0_DOMAIN = env("AUTH0_DOMAIN")

NINJA_JWT = {
    "ALGORITHM": "RS256",
    "AUDIENCE": AUTH0_AUDIENCE,
    "ISSUER": f"https://{AUTH0_DOMAIN}/",
    "JWK_URL": f"https://{AUTH0_DOMAIN}/.well-known/jwks.json",
    "JTI_CLAIM": None,
    "TOKEN_TYPE_CLAIM": None,
    "USER_ID_CLAIM": "sub",
    "USER_ID_FIELD": "username", # this is the field of the user model that will store the auth0 ID. I'm leveraging `username` here, but it's probably better to have something like `auth0_id`
}

This part was key to make it work:

    "JTI_CLAIM": None,
    "TOKEN_TYPE_CLAIM": None,

@eadwinCode can I ask you if setting these to None is a security concern?

Final note:
The JWTAuth will look for a user in the user model with the ID from the token—in the case of Auth0, it's the field sub in the token. If it doesn't find it, it will raise an AuthenticationFailed error.

If you have an SPA, you may not have the user in the database until they signup and you get the token from the frontend.

If that's the case, you can do something like the code below.

IMPORTANT: it's obviously more secure to create users through a machine-to-machine communication between Django and Auth0 and don't rely on tokens to create users that don't exist.

from django.contrib.auth.models import AbstractUser
from django.utils.translation import gettext_lazy as _
from ninja_jwt.authentication import JWTAuth
from ninja_jwt.exceptions import AuthenticationFailed, InvalidToken
from ninja_jwt.settings import api_settings


class JWTAuthCreateUser(JWTAuth):
    def get_user(self, validated_token) -> AbstractUser:
        """
        Attempts to find and return a user using the given validated token.
        """
        try:
            user_id = validated_token[api_settings.USER_ID_CLAIM]
        except KeyError as e:
            raise InvalidToken(
                _("Token contained no recognizable user identification")
            ) from e

        try:
            # Create the user if it doesn't exist
            user, created = self.user_model.objects.get_or_create(
                **{api_settings.USER_ID_FIELD: user_id}
            )
        except self.user_model.DoesNotExist as e:
            raise AuthenticationFailed(_("User not found")) from e

        if not user.is_active:
            raise AuthenticationFailed(_("User is inactive"))

        return user


@router.get("/protected", auth=JWTAuthCreateUser())
def protected_message(request):
    if not request.auth:
        return ErrorResponse(message="User is not authenticated"), 403
    return get_message("This is a protected message.")
   

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants