26
votes

I am using both great tools DRF and Django-REST-Swagger, however a few of my API views are under token authentication.

So now I'd like to add to my swagger doc page of my API the possibility to test those token auth api urls, including the Token header. How could I do this?.

A snapshot of my class API view is like this:

class BookList(APIView):
    """
    List all books, or create a new book.
    """
    authentication_classes = (TokenAuthentication, )
    permission_classes = (IsAuthenticated,)
    ...

Since Swagger auto detects a lot of stuff, I was expecting to notice about token auth, and ask me about token or user id in its web interface, but it doesn't. Hence I am testing it manually through CURL commands...

8

8 Answers

31
votes

If you're using token authentication, you might want to look at this question

Basically, you just need to add this to your settings.py:

SWAGGER_SETTINGS = {
    'SECURITY_DEFINITIONS': {
        'api_key': {
            'type': 'apiKey',
            'in': 'header',
            'name': 'Authorization'
        }
    },
}

In your Swagger UI page you should see an Authorize button. Click that and enter your Authorization value in the input text field.

11
votes

I answer myself since I made it work.

Actually Swagger settings has an option for this, api_key ->

SWAGGER_SETTINGS = {
    "exclude_namespaces": [], # List URL namespaces to ignore
    "api_version": '0.1',  # Specify your API's version
    "api_path": "/",  # Specify the path to your API not a root level
    "enabled_methods": [  # Specify which methods to enable in Swagger UI
        'get',
        'post',
        'put',
        'patch',
        'delete'
    ],
    "api_key": '', # An API key
    "is_authenticated": False,  # Set to True to enforce user authentication,
    "is_superuser": False,  # Set to True to enforce admin only access
}

To me it wasn't that clear, but I've just input a valid token for testing user and it worked for the auth needed views :-)

8
votes

My Problem was that after activating TokenAuthentification my api urls were not shown any more in the swagger UI due to an AuthentificationError.
For me the solution was to activate both authentaction classes in the Django Rest Framework Settings:
SessionAuthentification -> for the Swagger UI
TokenAuthentification -> for the Rest Clients

REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAdminUser',),
'DEFAULT_AUTHENTICATION_CLASSES': (
    'rest_framework.authentication.TokenAuthentication',
    'rest_framework.authentication.SessionAuthentication'
)

}

8
votes

The schema view needs to have the permission of AllowAny. This allows the plugin to see which endpoints are available before the user has authenticated. The end points should still be protected if they are setup correctly. Example:

@api_view()
@renderer_classes([SwaggerUIRenderer, OpenAPIRenderer, renderers.CoreJSONRenderer])
@authentication_classes((TokenAuthentication, SessionAuthentication))
@permission_classes((AllowAny,))
def schema_view(request):
    generator = schemas.SchemaGenerator(
        title='My API end points',
        patterns=my_urls,
        url="/api/v1/")
    return response.Response(generator.get_schema(request=request))

It is best to remove the SessionAuthentication and only use the TokenAuthentication but that is a matter of choice, here I have removed it

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated'
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication'
)

Be sure it add 'rest_framework.authtoken' into your installed apps and remove the CsrfViewMiddleware from the middleware classes as it will no longer be needed. And the swagger settings

SWAGGER_SETTINGS = {
    'SECURITY_DEFINITIONS': {
        'api_key': {
            'type': 'apiKey',
            'in': 'header',
            'name': 'Authorization'
        }
    },
    'USE_SESSION_AUTH': False,
    'JSON_EDITOR': True,
}

This will make swagger populate the token into all of the example curl commands as well, which is really nice to have. Leaving the session auth in place seems to disable this.

The swagger authorization dialog asks for the api_key which needs to be provided. Can not seem improve this, will update this post if I do.

2
votes

just add to you rest framework setting SessionAuthentication should be at the top

'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'mymodule.authentication.CustomeAuthentication',
)

Note: SessionAuthentication will use your Django login session

CustomeAuthentication will be used for rest api for real use case.

1
votes

if you are implementing the answer of @Melvic Ybanez with django-rest-swagger==2.2.0 and still doesn't work. Downgrade to django-rest-swagger==2.1.2. Button authorize should work now.

0
votes

I manage to change Swagger's default basic authentication to token authentication with this configuration but when try me button is pressed rest swagger accepts any authentication regardless of valid token.

Also note, when I added SessionAuthentication to my REST_FRAMEWORK in my settings.py, my api failed to be displayed on swagger docs.

django-rest-swagger==2.2.0 djangorestframework==3.7.7

settings.py

INSTALLED_APPS = [
    'rest_framework',
    'rest_framework_swagger',
    'rest_framework.authtoken',
]

REST_FRAMEWORK = {
    # Parser classes priority-wise for Swagger
    'DEFAULT_PARSER_CLASSES': [
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser',
        'rest_framework.parsers.JSONParser',
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ) 
}

# SWAGGER SETTINGS
SWAGGER_SETTINGS = {
    'SECURITY_DEFINITIONS': {
        'api_Key': {
            'type': 'apiKey',
            'in': 'header',
            'name': 'Token Authorization'
        }
    },
}

some helpful documentation https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-definitions-object

0
votes

Please use rest_framework_jwt.authentication.JSONWebTokenAuthentication instead of rest_framework.authentication.TokenAuthentication.

My code is

REST_FRAMEWORK = {
     'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
         # 'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser',
        'rest_framework.parsers.JSONParser',
    ),
}

AUTH_USER_MODEL = 'auth.User'

JWT_AUTH = {
    'JWT_ENCODE_HANDLER':
        'rest_framework_jwt.utils.jwt_encode_handler',

    'JWT_DECODE_HANDLER':
        'rest_framework_jwt.utils.jwt_decode_handler',

    'JWT_PAYLOAD_HANDLER':
        'rest_framework_jwt.utils.jwt_payload_handler',

    'JWT_PAYLOAD_GET_USER_ID_HANDLER':
        'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',

    'JWT_RESPONSE_PAYLOAD_HANDLER':
        'rest_framework_jwt.utils.jwt_response_payload_handler',

    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_ALGORITHM': 'HS256',
    'JWT_VERIFY': True,
    'JWT_VERIFY_EXPIRATION': True,
    'JWT_LEEWAY': 0,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=86400),
    'JWT_AUDIENCE': None,
    'JWT_ISSUER': None,

    'JWT_ALLOW_REFRESH': True,
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=1),

    'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}

SWAGGER_SETTINGS = {
    'SHOW_REQUEST_HEADERS': True,
    'SECURITY_DEFINITIONS': {
        'Bearer': {
            'type': 'apiKey',
            'name': 'Authorization',
            'in': 'header'
        }
    },
    'USE_SESSION_AUTH': False,
    'JSON_EDITOR': True,
    'SUPPORTED_SUBMIT_METHODS': [
        'get',
        'post',
        'put',
        'delete',
        'patch'
    ],
}

I solved the problem.