npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

graphql-autharoo

v1.0.7

Published

Authorization helpers for graphql resolvers.

Downloads

15

Readme

graphql-autharoo

npm Build Status codecov npm GitHub issues by-label GitHub issues by-label

Authorization helpers for graphql resolvers.

This library will help you handle contextual authorization needs in your graphql queries, mutations, type and batch resolvers. At its core, it provides multiplexing and filtering capabilities for resolving data based on the request, context, and permissions.

Features:

  • authSwitch helper for selecting the data handler given the context.
  • authBatching helper for batch resolvers and efficient filtering and resolution of data.
  • Flexible requiredScopes and providedScopes for dynamic permissioning of objects.
  • Multiple validators for supporting a wide range of permissions schemes and formats.
  • Authentication check prior to authorization and permissions validation.
  • All pieces are configurable and pluggable for your specific use cases.
  • Passing all tests with 100% code coverage and zero dependencies.

See the full documentation for the details.

Install

npm install graphql-autharoo

or 

yarn add graphql-autharoo


and then

import { authSwitch, authBatching } from 'graphql-autharoo';

Examples

authSwitch

authSwitch process a request by sequentially checking the required and provided scopes. When passing conditions are found, the callback is executed.

signature: authSwitch(scopeChain, options)

returns: async function(sources, args, context, info)

// setup the resolver chain
const resolverChain = [

  // Some admin only area
  {
    // square bracket interpolation to ease permission writing
    requiredScopes: ['admin:[list,view]'],
    providedScopes: (sources, args, context, info) => {
      // need to return an array of strings
      return context.auth.userScopes.find(args.id)
    },

    callback: () => {
      return "success admin"
    }
  },

  // A second scoping for user viewing
  {
    // feel free to get creative with your permissioning format.
    // It's just string equality under the hood.
    requiredScopes: ['admin:[list,view]', 'user/self/[profile,setting]:[view,update,delete]'],

    // providedScopes: by default looks at 'context.auth.scopes'
    // for an array of string permissions.
    callback: () => {
      return "success user"
    }
  },

  // Default
  {
    // The bottomless pit...
    // empty brackets will validate against (almost) anything.
    requiredScopes: [],
    callback: () => {
      return "success default"
    }
  }

  // If a request falls through all of the stages... 
  // it is a form of denial and will get assigned 'AuthorizationError'
  // An empty bracket clause means that should never happen.
  // (You can do some things with the auth batch helper as well as nested auth helpers)
])

const options = {
  expandRequired: true
}

// use authSwitch to handle a GraphQL request
// you need to pass the context because it is used internally (see wishlist)

const results = await authSwitch(resolverChain, options)(sources, args, context, info);

authBatching

authBatching is designed for handling authorization with batch request processors like dataloader and graphql-resolve-batch. It has nearly identical signature and operation, sequentially process a request through the scopings like authSwitch. The difference is that the source elements are progressively authorized, filtered, fulfilled, and reassembled on your behalf.

signature: authBatching(scopeChain, options)

returns: async function(sources, args, context, info)

const resolverChain = [
  {
    // Admins only
    requiredScopes: () => ['admin:view'],

    // a reusable scope extractor, needs to return an array of arrays of strings (permission list per source element)
    providedScopes: adminProvided,   
    
    // a reusable callback that returns all matches for sources
    callback: allCallback            
  },
  {
    // Group members only
    requiredScopes: () => ['group:member'],
    
    // a reusable scope extractor for membership
    providedScopes: memberProvided,  
    
    // we can almost always return everything because the filtering already happened
    callback: allCallback            
  },
  {
    // User only view
    requiredScopes: () => ['user:view'],

    // a reusable scope extractor for ids
    providedScopes: idProvided,
    
    // again, return something for every source, what that is is up to you
    callback: allCallback
  },
  {
    // empty brackets mean pass everything, unless unauthenticated
    requiredScopes: () => [],        
    
    // returns and array of empty arrays the same length as sources
    providedScopes: nothingProvided, 
    
    // A reusable callback which returns only things with public settings
    callback: publicCallback         
  }

  // Any element of the sources which makes it this far will
  // receive the 'AuthorizationError'
]

Usage

import { GraphQLObjectType, GraphQLString } from 'graphql';
import { createBatchResolver } from 'graphql-resolve-batch';
import { authSwitch, authBatching } from 'graphql-autharoo';

const UserType = new GraphQLObjectType({
  // ...
});

const MutationType = new GraphQLObjectType({
  name: 'Mutation',
  fields: {
    update: {
      type: UserType,
      resolve: authSwitch([
        {
          requiredScopes: ['admin:update', 'user:self:update']
          callback: (source, args, context, info) => {
            ...
            // put your normal batch resolving code in the callbacks
            ...
          }
        }

        // There is only one scoping block in this example.
        // Any requests making it this far will be denied access.
      ],{
        // ... Optionally, specify any options you need to send to graphql-autharoo or the validators
      })
    },
  },
});


const QueryType = new GraphQLObjectType({
  name: 'Query',
  fields: {
    user: {
      type: UserType,
      resolve: createBatchResolver(async (sources, args, context, info) => {

        // The authBatching helper goes inside of the batch resolver
        // We first set it up with the helper function call
        // and then execute it with the values coming in from the batch resolver.

        // Setup
        const scopeBlocks = [
          ...
          // put your normal batch resolving code in the callbacks
          ...
        ]
        const options = {...}

        // Initialization
        const authResolver = authBatching(scopeBlocks, options)

        // any any-procssing here

        // Actual processing
        const results = await authResolver(sources, args, context, info)

        // any post-procssing here

        // like it says
        return results
      }),
    },
  },
});

Nesting

Yup, it's definitely turtles from here on down.

const switchResolver = [

  // Admin can see everything, so shortcut
  {
    requiredScopes: ['admin:view'],
    callback: (sources) => sources
  },

  // User view, now get more granular
  { 
    requiredScopes: ['user:view'],
    callback: authBatching([
      {
        // only group members
        requiredScopes: ['group:member:view'],
        // return the user scopes per source
        providedScopes: (sources, _, context) => sources.map(s => s.isUserAMember(context.user.id) ? context.auth.scopes : []),
        callback: (sources) => sources
      },
      
      // default - public groups only
      {
        // pass everyting through
        requiredScopes: [],
        // pass everyting through
        providedScopes: (sources) => sources.map(s => []),
        // only return public results
        callback: (sources) => sources.map(s => s.isPublic ? s : null)
      }
    ])
  },

  // No auth, no results
  {
    requiredScopes: [],
    callback: (sources) => sources.map(s => new AuthorizationError())
  }
]

Wishlist

  • examples, you can find some here: https://github.com/sysgears/apollo-universal-starter-kit/tree/auth-upgrades
  • dataloader integration (maybe just needs to be verified?)
  • make the authentication stage configurable
  • make expansion callback configurable
  • better permission string processiny
    • remove no whitespace requirement (easy)
    • nested bracket expansion (easyish)
    • enable variable interpolation (???)
  • enable premissions to be objects

Happy Auth'n!