How do I implement auth directive for mutations with Apollo?












1















I am trying to set up an Apollo backend for a project I'm working on, where I'm attempting to implement schema directives. However, I am not able to add my schema directive to mutations. So to my main question: How do I implement an auth directive for mutations?



I have added @auth(requires: ADMIN) to the end of my users query, which is working fine. Apollo will then require a bearer token with admin access for performing the users query.



extend type Query {
user(id: ID!): User
users: [User!]! @auth(requires: ADMIN)
}


When I tried to do it in the same way for the editMyUser mutation, the auth directive seems to be enforced for all the mutations, instead of just the one I wanted. Even the signUp mutation will give "not authorized" error, when I add the @auth part to the editMyUser mutation. Even though there is no relation between them.



The role field which is supposed to be passed to the auth directive when invoked is logging out empty.



extend type Mutation {
signUp(
username: String!
firstName: String
lastName: String
password: String!
isAdmin: Boolean
isActive: Boolean): User!
login(
username: String!
password: String!): User!
editMyUser(
id: ID!
firstName: String
lastName: String
password: String): User! @auth(requires: USER)
adminEditUser(
id: ID!
firstName: String
lastName: String
password: String
isActive: Boolean
isAdmin: Boolean
isBanned: Boolean): User!
}


This is how I implemented the schema directive



export default gql`
directive @auth(requires: Role = ADMIN) on OBJECT | FIELD_DEFINITION

enum Role {
ADMIN
USER
}


https://github.com/jwhenshaw/graphql-directives-auth
This is the Auth Directive I've implemented in my code for reference.



So to summarise, when I implement auth directives for mutations they are implemented for all mutations, instead of just the one, and it is not even working correctly, as roles are not passed on to the directive.



I would love to get some help with this. Thanks!










share|improve this question





























    1















    I am trying to set up an Apollo backend for a project I'm working on, where I'm attempting to implement schema directives. However, I am not able to add my schema directive to mutations. So to my main question: How do I implement an auth directive for mutations?



    I have added @auth(requires: ADMIN) to the end of my users query, which is working fine. Apollo will then require a bearer token with admin access for performing the users query.



    extend type Query {
    user(id: ID!): User
    users: [User!]! @auth(requires: ADMIN)
    }


    When I tried to do it in the same way for the editMyUser mutation, the auth directive seems to be enforced for all the mutations, instead of just the one I wanted. Even the signUp mutation will give "not authorized" error, when I add the @auth part to the editMyUser mutation. Even though there is no relation between them.



    The role field which is supposed to be passed to the auth directive when invoked is logging out empty.



    extend type Mutation {
    signUp(
    username: String!
    firstName: String
    lastName: String
    password: String!
    isAdmin: Boolean
    isActive: Boolean): User!
    login(
    username: String!
    password: String!): User!
    editMyUser(
    id: ID!
    firstName: String
    lastName: String
    password: String): User! @auth(requires: USER)
    adminEditUser(
    id: ID!
    firstName: String
    lastName: String
    password: String
    isActive: Boolean
    isAdmin: Boolean
    isBanned: Boolean): User!
    }


    This is how I implemented the schema directive



    export default gql`
    directive @auth(requires: Role = ADMIN) on OBJECT | FIELD_DEFINITION

    enum Role {
    ADMIN
    USER
    }


    https://github.com/jwhenshaw/graphql-directives-auth
    This is the Auth Directive I've implemented in my code for reference.



    So to summarise, when I implement auth directives for mutations they are implemented for all mutations, instead of just the one, and it is not even working correctly, as roles are not passed on to the directive.



    I would love to get some help with this. Thanks!










    share|improve this question



























      1












      1








      1








      I am trying to set up an Apollo backend for a project I'm working on, where I'm attempting to implement schema directives. However, I am not able to add my schema directive to mutations. So to my main question: How do I implement an auth directive for mutations?



      I have added @auth(requires: ADMIN) to the end of my users query, which is working fine. Apollo will then require a bearer token with admin access for performing the users query.



      extend type Query {
      user(id: ID!): User
      users: [User!]! @auth(requires: ADMIN)
      }


      When I tried to do it in the same way for the editMyUser mutation, the auth directive seems to be enforced for all the mutations, instead of just the one I wanted. Even the signUp mutation will give "not authorized" error, when I add the @auth part to the editMyUser mutation. Even though there is no relation between them.



      The role field which is supposed to be passed to the auth directive when invoked is logging out empty.



      extend type Mutation {
      signUp(
      username: String!
      firstName: String
      lastName: String
      password: String!
      isAdmin: Boolean
      isActive: Boolean): User!
      login(
      username: String!
      password: String!): User!
      editMyUser(
      id: ID!
      firstName: String
      lastName: String
      password: String): User! @auth(requires: USER)
      adminEditUser(
      id: ID!
      firstName: String
      lastName: String
      password: String
      isActive: Boolean
      isAdmin: Boolean
      isBanned: Boolean): User!
      }


      This is how I implemented the schema directive



      export default gql`
      directive @auth(requires: Role = ADMIN) on OBJECT | FIELD_DEFINITION

      enum Role {
      ADMIN
      USER
      }


      https://github.com/jwhenshaw/graphql-directives-auth
      This is the Auth Directive I've implemented in my code for reference.



      So to summarise, when I implement auth directives for mutations they are implemented for all mutations, instead of just the one, and it is not even working correctly, as roles are not passed on to the directive.



      I would love to get some help with this. Thanks!










      share|improve this question
















      I am trying to set up an Apollo backend for a project I'm working on, where I'm attempting to implement schema directives. However, I am not able to add my schema directive to mutations. So to my main question: How do I implement an auth directive for mutations?



      I have added @auth(requires: ADMIN) to the end of my users query, which is working fine. Apollo will then require a bearer token with admin access for performing the users query.



      extend type Query {
      user(id: ID!): User
      users: [User!]! @auth(requires: ADMIN)
      }


      When I tried to do it in the same way for the editMyUser mutation, the auth directive seems to be enforced for all the mutations, instead of just the one I wanted. Even the signUp mutation will give "not authorized" error, when I add the @auth part to the editMyUser mutation. Even though there is no relation between them.



      The role field which is supposed to be passed to the auth directive when invoked is logging out empty.



      extend type Mutation {
      signUp(
      username: String!
      firstName: String
      lastName: String
      password: String!
      isAdmin: Boolean
      isActive: Boolean): User!
      login(
      username: String!
      password: String!): User!
      editMyUser(
      id: ID!
      firstName: String
      lastName: String
      password: String): User! @auth(requires: USER)
      adminEditUser(
      id: ID!
      firstName: String
      lastName: String
      password: String
      isActive: Boolean
      isAdmin: Boolean
      isBanned: Boolean): User!
      }


      This is how I implemented the schema directive



      export default gql`
      directive @auth(requires: Role = ADMIN) on OBJECT | FIELD_DEFINITION

      enum Role {
      ADMIN
      USER
      }


      https://github.com/jwhenshaw/graphql-directives-auth
      This is the Auth Directive I've implemented in my code for reference.



      So to summarise, when I implement auth directives for mutations they are implemented for all mutations, instead of just the one, and it is not even working correctly, as roles are not passed on to the directive.



      I would love to get some help with this. Thanks!







      javascript authentication graphql apollo apollo-server






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 2 at 11:18









      Ahmed Ashour

      3,604102643




      3,604102643










      asked Jan 2 at 10:19









      fantastiskfantastisk

      207




      207
























          2 Answers
          2






          active

          oldest

          votes


















          2














          Within that repo the AuthDirective class passes the objectType of the field being wrapped into the ensureFieldWrapped method. What this means is that for your example where you are assigning the directive directly onto a field, editMyUser, on the object of Mutation the method is wrapping all the children of Mutation (I believe the same should be true for your Queries as well actually).



          So, within the example repo, this is fine as we have an Object type User and we wrap it and its fields. However, if you do not wish to do this we can alter the AuthDirective class to wrap just the field it is on.



          I have done this and pushed to the repo, https://github.com/jwhenshaw/graphql-directives-auth, where you can see there is now a FieldAuthDirective and a ObjectAuthDirective. I still need to clean the code a bit but pushed a working example and left some logs to help highlight the differences. You can view it here https://qzj70qn2mj.sse.codesandbox.io/ if you don't fancy running it locally.



          Hope this helps, let me know if I need to elaborate some more.






          share|improve this answer































            1














            The issue here is that the referenced implementation throws an error when the wrapping resolver does not find any required roles (neither for the object type nor the field in question) in these lines of code.



            The logic is that as you use the directive for some field of an object type, you also need to provide a requirement for the type itself. In my opinion this logic isn't too bad and is as the code comment suggests to be on the safe side. The author of that implementation probably focused on usage of the directive for actual data types, not for queries or mutations.



            Let me be even a little more specific: What you and I do (as I am trying to accomplish the same thing as you today), when using the directive for one or several queries/mutations is actually applying the directive on the fields of the schema types Query and Mutation. So if we don't want a minimum requirement for all queries and/or mutations of our schema, the code should not throw an error in that condition I linked above, but it should call the wrapped resolver just as if the requirements were met (because there are none).



            Example:



            if (!requiredRole) {
            // No auth required, just call the resolver
            return resolve.apply(this, args);
            }


            I hope this helps! 🙂






            share|improve this answer























              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "1"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54004551%2fhow-do-i-implement-auth-directive-for-mutations-with-apollo%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              2














              Within that repo the AuthDirective class passes the objectType of the field being wrapped into the ensureFieldWrapped method. What this means is that for your example where you are assigning the directive directly onto a field, editMyUser, on the object of Mutation the method is wrapping all the children of Mutation (I believe the same should be true for your Queries as well actually).



              So, within the example repo, this is fine as we have an Object type User and we wrap it and its fields. However, if you do not wish to do this we can alter the AuthDirective class to wrap just the field it is on.



              I have done this and pushed to the repo, https://github.com/jwhenshaw/graphql-directives-auth, where you can see there is now a FieldAuthDirective and a ObjectAuthDirective. I still need to clean the code a bit but pushed a working example and left some logs to help highlight the differences. You can view it here https://qzj70qn2mj.sse.codesandbox.io/ if you don't fancy running it locally.



              Hope this helps, let me know if I need to elaborate some more.






              share|improve this answer




























                2














                Within that repo the AuthDirective class passes the objectType of the field being wrapped into the ensureFieldWrapped method. What this means is that for your example where you are assigning the directive directly onto a field, editMyUser, on the object of Mutation the method is wrapping all the children of Mutation (I believe the same should be true for your Queries as well actually).



                So, within the example repo, this is fine as we have an Object type User and we wrap it and its fields. However, if you do not wish to do this we can alter the AuthDirective class to wrap just the field it is on.



                I have done this and pushed to the repo, https://github.com/jwhenshaw/graphql-directives-auth, where you can see there is now a FieldAuthDirective and a ObjectAuthDirective. I still need to clean the code a bit but pushed a working example and left some logs to help highlight the differences. You can view it here https://qzj70qn2mj.sse.codesandbox.io/ if you don't fancy running it locally.



                Hope this helps, let me know if I need to elaborate some more.






                share|improve this answer


























                  2












                  2








                  2







                  Within that repo the AuthDirective class passes the objectType of the field being wrapped into the ensureFieldWrapped method. What this means is that for your example where you are assigning the directive directly onto a field, editMyUser, on the object of Mutation the method is wrapping all the children of Mutation (I believe the same should be true for your Queries as well actually).



                  So, within the example repo, this is fine as we have an Object type User and we wrap it and its fields. However, if you do not wish to do this we can alter the AuthDirective class to wrap just the field it is on.



                  I have done this and pushed to the repo, https://github.com/jwhenshaw/graphql-directives-auth, where you can see there is now a FieldAuthDirective and a ObjectAuthDirective. I still need to clean the code a bit but pushed a working example and left some logs to help highlight the differences. You can view it here https://qzj70qn2mj.sse.codesandbox.io/ if you don't fancy running it locally.



                  Hope this helps, let me know if I need to elaborate some more.






                  share|improve this answer













                  Within that repo the AuthDirective class passes the objectType of the field being wrapped into the ensureFieldWrapped method. What this means is that for your example where you are assigning the directive directly onto a field, editMyUser, on the object of Mutation the method is wrapping all the children of Mutation (I believe the same should be true for your Queries as well actually).



                  So, within the example repo, this is fine as we have an Object type User and we wrap it and its fields. However, if you do not wish to do this we can alter the AuthDirective class to wrap just the field it is on.



                  I have done this and pushed to the repo, https://github.com/jwhenshaw/graphql-directives-auth, where you can see there is now a FieldAuthDirective and a ObjectAuthDirective. I still need to clean the code a bit but pushed a working example and left some logs to help highlight the differences. You can view it here https://qzj70qn2mj.sse.codesandbox.io/ if you don't fancy running it locally.



                  Hope this helps, let me know if I need to elaborate some more.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Jan 2 at 16:22









                  James HenshawJames Henshaw

                  361




                  361

























                      1














                      The issue here is that the referenced implementation throws an error when the wrapping resolver does not find any required roles (neither for the object type nor the field in question) in these lines of code.



                      The logic is that as you use the directive for some field of an object type, you also need to provide a requirement for the type itself. In my opinion this logic isn't too bad and is as the code comment suggests to be on the safe side. The author of that implementation probably focused on usage of the directive for actual data types, not for queries or mutations.



                      Let me be even a little more specific: What you and I do (as I am trying to accomplish the same thing as you today), when using the directive for one or several queries/mutations is actually applying the directive on the fields of the schema types Query and Mutation. So if we don't want a minimum requirement for all queries and/or mutations of our schema, the code should not throw an error in that condition I linked above, but it should call the wrapped resolver just as if the requirements were met (because there are none).



                      Example:



                      if (!requiredRole) {
                      // No auth required, just call the resolver
                      return resolve.apply(this, args);
                      }


                      I hope this helps! 🙂






                      share|improve this answer




























                        1














                        The issue here is that the referenced implementation throws an error when the wrapping resolver does not find any required roles (neither for the object type nor the field in question) in these lines of code.



                        The logic is that as you use the directive for some field of an object type, you also need to provide a requirement for the type itself. In my opinion this logic isn't too bad and is as the code comment suggests to be on the safe side. The author of that implementation probably focused on usage of the directive for actual data types, not for queries or mutations.



                        Let me be even a little more specific: What you and I do (as I am trying to accomplish the same thing as you today), when using the directive for one or several queries/mutations is actually applying the directive on the fields of the schema types Query and Mutation. So if we don't want a minimum requirement for all queries and/or mutations of our schema, the code should not throw an error in that condition I linked above, but it should call the wrapped resolver just as if the requirements were met (because there are none).



                        Example:



                        if (!requiredRole) {
                        // No auth required, just call the resolver
                        return resolve.apply(this, args);
                        }


                        I hope this helps! 🙂






                        share|improve this answer


























                          1












                          1








                          1







                          The issue here is that the referenced implementation throws an error when the wrapping resolver does not find any required roles (neither for the object type nor the field in question) in these lines of code.



                          The logic is that as you use the directive for some field of an object type, you also need to provide a requirement for the type itself. In my opinion this logic isn't too bad and is as the code comment suggests to be on the safe side. The author of that implementation probably focused on usage of the directive for actual data types, not for queries or mutations.



                          Let me be even a little more specific: What you and I do (as I am trying to accomplish the same thing as you today), when using the directive for one or several queries/mutations is actually applying the directive on the fields of the schema types Query and Mutation. So if we don't want a minimum requirement for all queries and/or mutations of our schema, the code should not throw an error in that condition I linked above, but it should call the wrapped resolver just as if the requirements were met (because there are none).



                          Example:



                          if (!requiredRole) {
                          // No auth required, just call the resolver
                          return resolve.apply(this, args);
                          }


                          I hope this helps! 🙂






                          share|improve this answer













                          The issue here is that the referenced implementation throws an error when the wrapping resolver does not find any required roles (neither for the object type nor the field in question) in these lines of code.



                          The logic is that as you use the directive for some field of an object type, you also need to provide a requirement for the type itself. In my opinion this logic isn't too bad and is as the code comment suggests to be on the safe side. The author of that implementation probably focused on usage of the directive for actual data types, not for queries or mutations.



                          Let me be even a little more specific: What you and I do (as I am trying to accomplish the same thing as you today), when using the directive for one or several queries/mutations is actually applying the directive on the fields of the schema types Query and Mutation. So if we don't want a minimum requirement for all queries and/or mutations of our schema, the code should not throw an error in that condition I linked above, but it should call the wrapped resolver just as if the requirements were met (because there are none).



                          Example:



                          if (!requiredRole) {
                          // No auth required, just call the resolver
                          return resolve.apply(this, args);
                          }


                          I hope this helps! 🙂







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Jan 2 at 16:21









                          nether_catnether_cat

                          11616




                          11616






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Stack Overflow!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54004551%2fhow-do-i-implement-auth-directive-for-mutations-with-apollo%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

                              ts Property 'filter' does not exist on type '{}'

                              mat-slide-toggle shouldn't change it's state when I click cancel in confirmation window