title: Strawberry docs
General
Types
Codegen
Extensions
>Errors
>Guides
Editor integration
Concepts
Integrations
Federation
Operations
Permissions
Permissions can be managed using Permission
classes. A Permission
class
extends BasePermission
and has a has_permission
method. It can be added to a
field using the permission_classes
keyword argument. A basic example looks
like this:
import typingimport strawberryfrom strawberry.permission import BasePermission
class IsAuthenticated(BasePermission): message = "User is not authenticated"
# This method can also be async! def has_permission( self, source: typing.Any, info: strawberry.Info, **kwargs ) -> bool: return False
@strawberry.typeclass Query: user: str = strawberry.field(permission_classes=[IsAuthenticated])
Your has_permission
method should check if this request has permission to
access the field. Note that the has_permission
method can also be
asynchronous.
If the has_permission
method returns a truthy value then the field access will
go ahead. Otherwise, an error will be raised using the message
class
attribute.
Take a look at our Dealing with Errors Guide for more information on how errors are handled.
{ "data": null, "errors": [ { "message": "User is not authenticated" } ]}
Accessing user information
Accessing the current user information to implement your permission checks depends on the web framework you are using.
Most frameworks will have a Request
object where you can either access the
current user directly or access headers/cookies/query parameters to authenticate
the user.
All the Strawberry integrations provide this Request object in the
info.context
object that is accessible in every resolver and in the
has_permission
function.
You can find more details about a specific framework integration under the "Integrations" heading in the navigation.
In this example we are using starlette
which uses the
ASGI integration:
import typingfrom myauth import authenticate_header, authenticate_query_param
from starlette.requests import Requestfrom starlette.websockets import WebSocketfrom strawberry.permission import BasePermission
class IsAuthenticated(BasePermission): message = "User is not authenticated"
def has_permission( self, source: typing.Any, info: strawberry.Info, **kwargs ) -> bool: request: typing.Union[Request, WebSocket] = info.context["request"]
if "Authorization" in request.headers: return authenticate_header(request)
if "auth" in request.query_params: return authenticate_query_params(request)
return False
Here we retrieve the request
object from the context
provided by info
.
This object will be either a Request
or Websocket
instance from starlette
(see: Request docs and
Websocket docs).
In the next step we take either the Authorization
header or the auth
query
parameter out of the request
object, depending on which is available. We then
pass those on to some authenticate methods we've implemented ourselves.
Beyond providing hooks, Authentication is not currently Strawberry's responsibility. You should provide your own helpers to figure out if a request has the permissions you expect.
For more discussion on Authentication see Issue #830.
Custom Error Extensions & classes
In addition to the message, permissions automatically add pre-defined error
extensions to the error, and can use a custom GraphQLError
class. This can be
configured by modifying the error_class
and error_extensions
attributes on
the BasePermission
class. Error extensions will be propagated to the response
as specified in the
GraphQL spec.
import typing
from strawberry.permission import BasePermission
from your_business_logic import GQLNotImplementedError
class IsAuthenticated(BasePermission): message = "User is not authenticated" error_class = GQLNotImplementedError error_extensions = {"code": "UNAUTHORIZED"}
def has_permission( self, source: typing.Any, info: strawberry.Info, **kwargs ) -> bool: return False
Advanced Permissions
Internally, permissions in strawberry use the PermissionsExtension
field
extension.
The following snippet
import strawberry
@strawberry.typeclass Query: user: str = strawberry.field(permission_classes=[IsAuthenticated])
is internally equivalent to
import strawberryfrom strawberry.permission import PermissionExtension
@strawberry.typeclass Query: @strawberry.field(extensions=[PermissionExtension(permissions=[IsAuthenticated()])]) def name(self) -> str: return "ABC"
Using the new PermissionExtension
API, permissions support even more features:
Silent errors
In some cases, it is practical to avoid throwing an error when the user has no
permission to access the field and instead return None
or an empty list to the
client. To return None
or []
instead of raising an error, the
fail_silently
keyword argument on PermissionExtension
can be set to True
:
Note that this will only work if the field returns a type that is nullable or
a list, e.g. Optional[str]
or List[str]
.
import strawberryfrom strawberry.permission import PermissionExtension, BasePermission
@strawberry.typeclass Query: @strawberry.field( extensions=[ PermissionExtension(permissions=[IsAuthenticated()], fail_silently=True) ] ) def name(self) -> str: return "ABC"
Please note than in many cases, defensive programming is a better approach than
using fail_silently
. Clients will no longer be able to distinguish between a
permission error and an empty result. Before implementing fail_silently
,
consider if it is possible to use alternative solutions like the @skip
or
@include
directives to dynamically exclude fields from the query for users
without permission. Check the GraphQL documentation for more information on
directives.
Customizable Error Handling
To customize the error handling, the on_unauthorized
method on the
BasePermission
class can be used. Further changes can be implemented by
subclassing the PermissionExtension
class.
Schema Directives
Permissions will automatically be added as schema directives to the schema. This
behavior can be altered by setting the add_directives
to False
on
PermissionExtension
, or by setting the _schema_directive
class attribute of
the permission class to a custom directive.