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 to only include ApiKeyAuth authentication/authorization strategy? #1094

Closed
alexburner opened this issue Oct 31, 2023 · 6 comments
Closed

Comments

@alexburner
Copy link

alexburner commented Oct 31, 2023

Describe the bug

We are only exposing endpoints that use djangorestframework-api-key in our generated schemas. We're following the blueprint instructions here to include ApiKeyAuth.

However, we're also seeing basicAuth and cookieAuth in the generated docs, how do we suppress those?

Including an empty list for AUTHENTICATION_WHITELIST does not seem to affect it.

To Reproduce

In settings.py:

SPECTACULAR_SETTINGS = {
    # ...
    "AUTHENTICATION_WHITELIST": [],
    "APPEND_COMPONENTS": {
        "securitySchemes": {
            "ApiKeyAuth": {
                "type": "apiKey",
                "in": "header",
                "name": "Authorization",
            }
        }
    },
    "SECURITY": [{"ApiKeyAuth": []}],
}

Expected behavior

We would like to only see the ApiKeyAuth strategy in generated docs

Observed behavior

We also see basicAuth and cookieAuth

In Swagger "Authorize":
Screenshot 2023-10-31 at 8 49 45 AM

In Redoc "Authorizations":
Screenshot 2023-10-31 at 8 57 33 AM

In .yaml file "securitySchemes":

components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: Authorization
    basicAuth:
      type: http
      scheme: basic
    cookieAuth:
      type: apiKey
      in: cookie
      name: sessionid
@alexburner alexburner changed the title How to only include ApiKeyAuth authentication strategy? How to only include ApiKeyAuth authentication/authorization strategy? Oct 31, 2023
@tfranzel
Copy link
Owner

tfranzel commented Nov 1, 2023

I suppose that makes sense because you only add one auth method and are not removing the others. This means you have some endpoints still supporting those auth methods.

I usually recommend properly setting DRF's DEFAULT_AUTHENTICATION_CLASSES

If you just want to hide them without removing them use the whitelist:

# Controls which authentication methods are exposed in the schema. If not None, will hide
# authentication classes that are not contained in the whitelist. Use full import paths
# like ['rest_framework.authentication.TokenAuthentication', ...].
# Empty list ([]) will hide all authentication methods. The default None will show all.
'AUTHENTICATION_WHITELIST': None,

@alexburner
Copy link
Author

alexburner commented Nov 1, 2023

Yeah that is a tricky bit — we have additional auth methods for some internal API endpoints, but we don't want to include those in our shared documentation.

We're currently filtering to "public" endpoints with a preprocessing hook:

SPECTACULAR_SETTINGS = {
  "PREPROCESSING_HOOKS": ["path.to.custom_preprocessing_hook"],
}
def custom_preprocessing_hook(endpoints):
    filtered = []
    for path, path_regex, method, callback in endpoints:
        # Only include public API paths
        if path.startswith("/api/v1/"):
            filtered.append((path, path_regex, method, callback))
    return filtered

You mention:

If you just want to hide them without removing them use the whitelist

We have tried using the whitelist, but it doesn't seem to affect anything. Buried in my lengthy original description:

Including an empty list for AUTHENTICATION_WHITELIST does not seem to affect it.

We've tried it with both [] and None, but all methods continue to be included 🤔

Is there another way to be doing it, or is this a bug?

@tfranzel
Copy link
Owner

tfranzel commented Nov 1, 2023

We've tried it with both [] and None, but all methods continue to be included 🤔

[] should remove all auto-detected auths and I'm very confident that it works. In 98% of cases, people have some other config down the line that overwrites previous settings. Please make sure your settings are actually used. Also make sure you have the most recent version.

we have additional auth methods for some internal API endpoints, but we don't want to include those in our shared documentation.

Actually we can generate a filtered schema based on the permission the user has on the API. "SERVE_PUBLIC": False will change that behavior, but your permissions have to be properly set. just a fyi

@alexburner
Copy link
Author

[] should remove all auto-detected auths and I'm very confident that it works. In 98% of cases, people have some other config down the line that overwrites previous settings. Please make sure your settings are actually used. Also make sure you have the most recent version.

Hmm I'm double checking, and [] isn't removing auths, even if I comment out APPEND_COMPONENTS / SECURITY:
Screenshot 2023-11-01 at 7 06 54 PM

(this confirms our settings are being used to some degree, since it does remove ApiKeyAuth strategy)

I did also upgrade from 0.25.1 to 0.26.5, but still seeing the same thing.

Here's our whole config, could something else be conflicting?

REST_FRAMEWORK = {
    "DEFAULT_PERMISSION_CLASSES": ("rest_framework.permissions.IsAuthenticated",),
    "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
    "PAGE_SIZE": 100,
}

SPECTACULAR_SETTINGS = {
    "TITLE": "My Great Title",
    "DESCRIPTION": "My Great Description",
    "VERSION": "0.1.0",
    "SERVE_INCLUDE_SCHEMA": False,
    "SWAGGER_UI_SETTINGS": {
        "displayOperationId": False,
        "defaultModelsExpandDepth": 0,
        "persistAuthorization": True,
        "tryItOutEnabled": True,
    },
    # Help with "tag" (group) extraction
    "SCHEMA_PATH_PREFIX": "/app/api/v[0-9]",
    # Require user login to view API schema
    "SERVE_PERMISSIONS": ["rest_framework.permissions.IsAuthenticated"],
    # Filter down to customer-facing API endpoints
    "PREPROCESSING_HOOKS": ["path.to.custom_preprocessing_hook"],
    # Add djangorestframework-api-key annotation
    # https://github.com/tfranzel/drf-spectacular/blob/0.25.1/docs/blueprints.rst#djangorestframework-api-key
    "APPEND_COMPONENTS": {
        "securitySchemes": {
            "ApiKeyAuth": {
                "type": "apiKey",
                "in": "header",
                "name": "Authorization",
            }
        }
    },
    "SECURITY": [{"ApiKeyAuth": []}],
    # Currently, basicAuth & cookieAuth are also included
    # issue: https://github.com/tfranzel/drf-spectacular/issues/1094
    "AUTHENTICATION_WHITELIST": [],
}

Actually we can generate a filtered schema based on the permission the user has on the API. "SERVE_PUBLIC": False will change that behavior, but your permissions have to be properly set. just a fyi

Oo this sounds nice, how would one do this?

One potential issue, all our endpoints are simple api view functions using @permission_classes, like:

from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework_api_key.permissions import HasAPIKey

@api_view(["GET"])
@permission_classes([HasAPIKey])
def my_great_endpoint_name(request):
    return Response({"message": "success"})

Not sure if that affects things

If we could get AUTHENTICATION_WHITELIST to only include @permission_classes([HasAPIKey]) that would be 💯

@tfranzel
Copy link
Owner

tfranzel commented Nov 2, 2023

still betting on a different AUTHENTICATION_WHITELIST somewhere else in your code afterwards. 😆


Oo this sounds nice, how would one do this?

well If the authenticated schema request would also pass through the permission_classes with the extracted user, then it will show up in the schema otherwise it won't.

rest_framework_api_key is just awkward because it does not depend on authentication_classes, which is the DRF mechanic for this. HasAPIKey is cumbersome for that reasons. Usually you would have HasScope or something as permission and the user extraction happens in the authentication_class.

@alexburner
Copy link
Author

Ohhhh my goodness gravy, I deeply appreciate all the time you put into helping me here

I went on a long quest to fork drf-spectacular and add some print statements to confirm everything was getting hit correctly with the correct values in our system...

And... I found that due to a build/docker issue, not only was my fork change not getting picked up, my original 0.25.1 -> 0.26.5 upgrade was not getting picked up

Once I fixed that, and was actually running 0.26.5, everything worked 🤦‍♂️

My apologies for the wild goose chase, thank you again for all your time 🙏

@tfranzel tfranzel closed this as completed Nov 2, 2023
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

2 participants