Open jwfehr opened 8 years ago
I had a similar problem, but I basically implemented a workaround such that a user could access the graphqlview only on the testing/development environment (localhost). Depends on what your goal is, I only had the graphqlview to help me model and test graphql queries, and didn't actually want it available in the production environment. Here's the code if you're interested:
isLocal = 'FLASK_DEBUG' in os.environ and os.environ['FLASK_DEBUG'] == '1'
app.add_url_rule('/graphql', view_func=GraphQLView.as_view('graphql', schema=schema, context={ 'client': MONGO_CLIENT }, graphiql= True if isLocal else False))
@kmakihara Did you end up writing individual graphql routes that handle authentication? How are you limiting the amount of data an authenticated user can potentially receive without limiting the usefulness of graphql?
It's been a while that issue is open, but for general purpose, I ended up using route decorators in my view function like this :
from flask_jwt_extended import jwt_required
...
def graphql_view():
view = GraphQLView.as_view('graphql', schema=schema, context={'session': db.session},
graphiql=True)
return jwt_required(view)
app.add_url_rule(
'/graphql',
view_func=graphql_view()
)
Here I use a JWT authentication with but I could use flask-login or any authentication method:
from flask_login import login_required
...
def graphql_view():
view = GraphQLView.as_view('graphql', schema=schema, context={'session': db.session},
graphiql=True)
return login_required(view)
...
Here is maybe a more intuitive way for those who don't use jwt:
def auth_required(fn):
def wrapper(*args, **kwargs):
session = request.headers.get(AUTH_HEADER, '')
# Do some authentication here maybe...
return fn(*args, **kwargs)
return wrapper
def graphql_view():
view = GraphQLView.as_view(
'graphql',
schema=schema,
graphiql=True,
context={
'session': DBSession,
}
)
return auth_required(view)
app = Flask(__name__)
app.debug = True
app.add_url_rule(
'/graphql',
view_func=graphql_view()
)
Taking the suggestions above, I have been able to use existing Python JWT libraries to authenticate the /graphql
endpoint. However, I am unsure what the best practice is to handle user identity management, which involves decoding the token and reading off the identifying information. For instance, if I have query{portfolio}
, I want to be able to return the portfolio of the authenticated user. I am not sure how to get that information and pass it along to the schema so that it can be used in resolver and mutator functions. Right now, I feel I am doing something rather silly: I have an additional endpoint in my Flask app called /jwt_id
that accepts a JWT parameter in the payload, which I then decode and return the id. Then in all of the resolvers, I use requests
to hit this endpoint, which is completely redundant since now the token is in the payload and in the header but I couldn't think of a workaround. Is there a clean way to pass the decoded token information to the schema? I'm assuming I can somehow leverage the context
parameter...?
UPDATE:
Figured it out - context
is indeed the way to do it!
I am using flask + jwt. For queries I just use Viewer, as a top level object:
class Query(graphene.ObjectType):
....
viewer = graphene.Field(Viewer)
@staticmethod
def resolve_viewer(_root, info):
return Viewer.get_user_by_token(info.context.headers.get('Authorization'))
For mutations I have to create abstract mutation class and extend it with all my mutation except login mutation:
class AuthorizedMutation(relay.ClientIDMutation):
class Meta:
abstract = True
@classmethod
@abstractmethod
def mutate_authorized(cls, root, info, **kwargs):
pass
@classmethod
def mutate_and_get_payload(cls, root, info, **kwargs):
_ = auth_service.authorize_token(info.context.headers.get('Authorization'))
return cls.mutate_authorized(root, info, **kwargs)
All credentials and tokes related work is handeled by auth_service
. Viewer.get_user_by_token
also calls it.
Hope it will be helpful. P.S.: Do not forget, that subscriptions also need to be secured.
Hi, I'm the maintainer of Flask-GraphQL-Auth. Inspired by Flask-JWT-Extended, There is a problem with error-handling but it works pretty well. How about try this?
You can use Flask-GraphQL-Auth like you used Flask-JWT-Extended.
here are some examples.
class Query(graphene.ObjectType):
protected = graphene.String(message=graphene.String(),
token=graphene.String())
@query_jwt_required
def resolve_protected(self, info, message):
return str(get_raw_jwt())
you can find more on github and docs
GitHub: https://github.com/callsign-viper/Flask-GraphQL-Auth Docs: https://flask-graphql-auth.readthedocs.io/en/latest/
I am trying to figure out the best way to authenticate a request before my schema executes the query/mutation. I was thinking I could use the @app.resolver('url') tag and create a function that would authenticate the request and then pass it to the graphqlview. Something like this:
But this doesn't work, can someone either show me what I am doing wrong here or give me a different way to authenticate the request? Thanks!