-
Notifications
You must be signed in to change notification settings - Fork 106
Description
Is your feature request related to a problem? Please describe.
Currently, it's not obvious how we can access the raw API key from within a view that's protected behind a permission classes. (This may be useful in order to e.g. fetch resources associated to that key).
Users can access the request headers directly:
scheme, _, key = request.META["HTTP_AUTHORIZATION"].partition(" ")but this essentially duplicates logic that's already defined on the HasAPIKey permission class applied to the view.
The only way to reuse that logic is to instantiate the permission class and call into .get_key()… But it doesn't feel very natural, does it?
class ProjectListView(APIView):
permission_classes = [HasProjectAPIKey]
def get(self, request: HttpRequest):
key: str = HasProjectAPIKey().get_key(request)
# ...Describe the solution you'd like
Make .get_key() a class method, so that we can reuse it within a view a bit more naturally:
class ProjectListView(APIView):
permission_classes = [HasProjectAPIKey]
def get(self, request: HttpRequest):
key: str = HasProjectAPIKey.get_key(request)
# ...Is this a breaking change? Luckily, it doesn't seem to be:
- A class method is okay to use on an instance, so if people were already using the first way (
HasProjectAPIKey().get_key()), they'll still be able to do it if we switch to a class method. - If people customized
.get_key()(see Customization: Key parsing) and they defined it as an instance method, then surely they know how they're going to use it, and changing it on the base won't break anything for them anyway (even if they callsuper().get_key(), since that would also work within an instance method).
Describe alternatives you've considered
There are a couple of other ways we could have gone here, none of which are really okay:
- Have
BaseAPIKeyset a magic.current_api_key(or similar) attribute on the view instance/class. We could make it work with type-checking, but it would lead to all sorts of weird edge cases and possible bugs related to shared view state. Also it probably wouldn't work with function-based views. - Introduce an
APIKeyMiddlewarethat basically calls.get_key()and sets it on therequestinstance. This isn't great, because a) it would break type-checking (we're adding a new dynamic attribute on the request that isn't known todjango-stubs), and b) it would expose the plaintext API key anywhere therequestis accessible, which is a massive potential security hole if the developer isn't careful. - Introduce an
APIKeyViewMixinthat injects a.get_api_key(request)method on the view. That method would inspect the view'spermission_classes. The problem is: there might be multiple API key permission classes set on the view, so which one would we use? "The one for which the API key is valid" won't work, because all permissions need to pass for a request to be authorized… So, nope.
The only sensible thing is to let the developer explicitly retrieve the raw API key from the request, using exact the permission class they want to use.
Additional context
Prompted by #93 (comment)