{"action":"created","issue":{"url":"https://api.github.com/repos/apollographql/graphql-tools/issues/313","repository_url":"https://api.github.com/repos/apollographql/graphql-tools","labels_url":"https://api.github.com/repos/apollographql/graphql-tools/issues/313/labels{/name}","comments_url":"https://api.github.com/repos/apollographql/graphql-tools/issues/313/comments","events_url":"https://api.github.com/repos/apollographql/graphql-tools/issues/313/events","html_url":"https://github.com/apollographql/graphql-tools/issues/313","id":221524666,"number":313,"title":"Authorization","user":{"login":"mxstbr","id":7525670,"avatar_url":"https://avatars3.githubusercontent.com/u/7525670?v=3","gravatar_id":"","url":"https://api.github.com/users/mxstbr","html_url":"https://github.com/mxstbr","followers_url":"https://api.github.com/users/mxstbr/followers","following_url":"https://api.github.com/users/mxstbr/following{/other_user}","gists_url":"https://api.github.com/users/mxstbr/gists{/gist_id}","starred_url":"https://api.github.com/users/mxstbr/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mxstbr/subscriptions","organizations_url":"https://api.github.com/users/mxstbr/orgs","repos_url":"https://api.github.com/users/mxstbr/repos","events_url":"https://api.github.com/users/mxstbr/events{/privacy}","received_events_url":"https://api.github.com/users/mxstbr/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":45,"created_at":"2017-04-13T10:39:47Z","updated_at":"2017-04-24T21:13:40Z","closed_at":null,"body":"Authorization in GraphQL is (as noted by @helfer in [this post](https://dev-blog.apollodata.com/auth-in-graphql-part-2-c6441bcc4302)) currently not a generally solved problem. Everybody has their own, custom solution that works for them but there's no general-purpose solution/library for it. (as far as I can tell)\r\n\r\nI think `graphql-tools` is uniquely positioned to fill that hole in the community. Permissions are naturally bound to the schema of the requested data, so to me it feels like a natural fit for `graphql-tools`. (note that this is just an idea, wanted to kick off a discussion)\r\n\r\nImagine a simple type like this:\r\n\r\n```GraphQL\r\n# I want posts to only be readable by logged-in users\r\ntype Post {\r\n  id: ID\r\n  content: String\r\n  author: User\r\n  notes: [String]  # I want these to only be accessible by the author\r\n} \r\n\r\nextends type Query {\r\n  post(id: ID!): Post\r\n}\r\n```\r\n\r\nThere's two types of permissions I want to enforce here. One of them I'm able to tell without running the resolvers first (only readable by the logged-in user), for the other I need the data from the database so they have to run after the resolvers. (only accessible by the author)\r\n\r\nI could imagine an API like this working out well:\r\n\r\n```JS\r\nmakeExecutableSchema({\r\n  schema: schema,\r\n  resolvers: resolverMap,\r\n  // This is the new part:\r\n  permissions: permissionMap,\r\n})\r\n```\r\n\r\nSimilar to `resolvers` this `permissionMap` would map to the schema:\r\n\r\n```JS\r\nconst permissionMap = {\r\n  Query: {\r\n    post: () => { /* ... */ }\r\n  },\r\n  Post: {\r\n    notes: () => { /* ... */ }\r\n  }\r\n}\r\n```\r\n\r\nDepending on if you return a truthy or a falsey value from these functions folks get access or a \"Permission denied\" error. The functions get the same information as resolvers. (i.e. `obj`, `args` and `context`)\r\n\r\nThis works for the pre-database case (only logged-in users should be able to access), since I could do something like this:\r\n\r\n```JS\r\n// The user comes from the context, and is passed in from the server\r\n// Note: This is simplified, there's more complex checks to figure out if somebody is logged in of course\r\nconst isLoggedIn = (_, _, { user }) => user;\r\n\r\nconst permissionMap = {\r\n  Query: {\r\n    post: isLoggedIn\r\n  },\r\n  Post: isLoggedIn\r\n}\r\n```\r\n\r\nNow the issue is _post_ resolver permission functions. I don't have a good idea how to handle these right now, but maybe something like this could work where each \"path\" has two magic properties (`.pre`, `.post`) which run those functions before and after the resolver:\r\n\r\n```JS\r\nconst isLoggedIn = (_, _, { user }) => user;\r\n// Check if the user requesting the resource is the author of the post\r\nconst isAuthor = (resolverResult, _, { user }) => user.id === resolverResult.author;\r\n\r\nconst permissionMap = {\r\n  Query: {\r\n    post: isLoggedIn\r\n  },\r\n  Post: {\r\n    '.pre': isLoggedIn,\r\n    notes: {\r\n      '.post': isAuthor,\r\n    }\r\n  }\r\n}\r\n```\r\n\r\nWhat do you think? Any previous discussions I'm missing?"},"comment":{"url":"https://api.github.com/repos/apollographql/graphql-tools/issues/comments/296824067","html_url":"https://github.com/apollographql/graphql-tools/issues/313#issuecomment-296824067","issue_url":"https://api.github.com/repos/apollographql/graphql-tools/issues/313","id":296824067,"user":{"login":"JeffRMoore","id":479533,"avatar_url":"https://avatars1.githubusercontent.com/u/479533?v=3","gravatar_id":"","url":"https://api.github.com/users/JeffRMoore","html_url":"https://github.com/JeffRMoore","followers_url":"https://api.github.com/users/JeffRMoore/followers","following_url":"https://api.github.com/users/JeffRMoore/following{/other_user}","gists_url":"https://api.github.com/users/JeffRMoore/gists{/gist_id}","starred_url":"https://api.github.com/users/JeffRMoore/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/JeffRMoore/subscriptions","organizations_url":"https://api.github.com/users/JeffRMoore/orgs","repos_url":"https://api.github.com/users/JeffRMoore/repos","events_url":"https://api.github.com/users/JeffRMoore/events{/privacy}","received_events_url":"https://api.github.com/users/JeffRMoore/received_events","type":"User","site_admin":false},"created_at":"2017-04-24T21:13:40Z","updated_at":"2017-04-24T21:13:40Z","body":"@thebigredgeek I was thinking mostly of the overhead of constructing the GraphQLRequest object and the closures for applying the middleware chains.  Its possibly not a big deal.  Something to double check, nothing more.\r\n\r\n@AndrewIngram, In the schema, its just meta-data.  Its not until you supply the functions with GraphQL Tools that it becomes an implementation.  That's the purpose of GraphQL Tools, no?  To supply a resolving implementation, separating the description from the implementation?\r\n\r\nAs for whether that auth information should be co-located with the schema?  Should GraphQL queries be co-located with their components or be separate?  Should html be co-located or separate?  Its just another application of principle of separation of concerns.\r\n\r\nIf for a particular case it doesn't make sense to co-locate auth metadata with the schema, don't.  If it does, do.\r\n\r\nI'm pretty sure there's no one right way.\r\n\r\nThe change cases @AndrewIngram mentions brings up an important point.  Maybe there are some BC implications with evolving the API?  How does one preserve the \"no versioning\" convention and evolve the authorization design?\r\n\r\nI've used this pattern with validation and it works very well.  Think `name @maxLength(length=31)`.  The challenge with validation is that you can only make the rules looser, not tighter.  If I change to `length=63`, no problem.  If I change my declaration to `length=15` any data that has been previously accepted is not valid by the current rule declaration. \r\n\r\nChanging validation rules often caused unexpected consequences.  For example phone number going from optional to required.  Now, what should we do with the user records that have no phone number?  Especially when they edit their account info?  Should we make them enter a phone number to change their email (also on the same record)?  Frustrating.  Should we allow partially valid records?  Dangerous?  Should we only validate changed fields and not consider the validity of the record as a whole?  Now we're pushing conditionals into downstream code.  We used a variety of approaches to handle this including data migration and versioning the record, and rules like \"required only on create or if already specified.\"\r\n\r\nThere was a lot of value for us in making constraints a part of the meta-data description of the API.  We could do things like user defined fields and apply validation rules in multiple contexts with different implementations.  I would want to use this pattern again.\r\n\r\nIs there value in the auth realm?  Possibly.  I might be able to use that information to hide or allow operations rather than allowing them to fail.  Or rather than duplicating a hard coded check into the client code, a check that would have to be maintained in parallel with one on the server side, use the metadata to apply the same check both on the client and the server."}}
{"actor":{"display_login":"JeffRMoore"}}