djangorestframework-api-key
                                
                                
                                
                                    djangorestframework-api-key copied to clipboard
                            
                            
                            
                        Retrieving the raw API key from within a view
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 https://github.com/florimondmanca/djangorestframework-api-key/pull/93#discussion_r372016983
Just a quick followup on this. In order to be able to use the API key to filter objects on a particular model, I think I'm right in saying that, currently, the only way to do this is:-
def get_queryset(self):
    key = HasProjectAPIKey().get_key(self.request)
    project = ProjectAPIKey.objects.get_from_key(key).project
    return super().get_queryset().filter(project=project)
Is there a simpler way? Or are there plans to implement one?
Hi @bodgerbarnett,
Right, that looks like the way to do this right now.
This issue is quite old so it can definitely benefit from more informed challenging.
What API would you expect to do this kind of filtering based on the request API key? Is there a way we could achieve this by doing something different (perhaps more effective, with less duplicate computation / SQL queries?) than what you’ve suggested under the hood?
I'm not sure what the ideal way to go is, but for the time being, I've created my own ProjectAPIKeyManager with a single method which I can pass a request into and get a ProjectAPIKey out of:-
from rest_framework_api_key.models import APIKeyManager
class ProjectAPIKeyManager(APIKeyManager):
    def get_from_request(self, request):
        from .permissions import HasProjectAPIKey
        key = HasProjectAPIKey().get_key(request)
        if key:
            return self.get_from_key(key)
It works for me at this stage.
The awkward thing seems to be that I've obviously enforced the HasProjectAPIKey permission on the view but then I need to use that class to get the actual key later on but hey, it works.