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-directive/auth

v1.0.2

Published

GraphQL authorization directive

Downloads

7

Readme

GraphQL Authorization Directive

Node.js CI Coverage Status

A TypeScript/JavaScript library that provides an easy way to add authorization logic to your Node.js GraphQL API using directives.

The authorization directive allows you to define authorization policies that determine whether a user is authorized to access certain fields or arguments in your GraphQL schema. These policies can be applied at the field or argument level, giving you granular control over who can access what data. By using the authorization directive, you can ensure that only authenticated users with the appropriate permissions are able to access sensitive data in your GraphQL API. This helps to improve the security of your application and protect user data.

The authorization directive also simplifies your code and makes it easier to review. By defining authorization policies once, you can apply them consistently across your entire GraphQL schema. This reduces the amount of code you need to write and makes it easier to maintain and update your API. Additionally, the authorization policies are easy to understand and review, making it easier for other developers to understand how your application works and how data is protected. Overall, the authorization directive is a powerful tool for securing your GraphQL API and ensuring that your users' data is protected.

Example Usage

Authorization method wrapped into single directive @authorize(policy: "list, of, policy") like example below.

const typeDefs = `
    input UserInput {
        name: String!   
        email: String! 
        # role only can be set by Admin 
        role: String   @authorize(policy: "Admin")
    }

    type User {
        id: String!
        name: String!   
        # email only visible by authenticated user
        email: String! @authorize(policy: "Authenticated")
        # role only visible by Admin
        role: String   @authorize(policy: "Admin")
    }

    type Query {
        users: [User]!
    }

    type Mutation { 

        # anyone can register user
        # but can not specify role (except Admin)
        addUser(
            user:UserInput!
        ): Boolean!

        # Authenticated user can modify user 
        # but can not modify role
        editUser(
            id:String!, 
            user:UserInput!
        ): Boolean!    @authorize(policy: "Authenticated")
    }
`

On above code, we applied several @authorize directive with Authenticated and Admin policy. We can define the policy like below.

  • Authenticated any user with a valid JWT token.
  • Admin any user which the token claims contains role: Admin

Register above policy while creating the schema transformer like below.

import auth from "@graphql-directive/auth"

const transform = auth.createTransformer({
    policies: {
        Admin: ({ contextValue }) => contextValue.user.role === "Admin",
        Authenticated: ({ contextValue }) => !!contextValue.user
    }
})

contextValue is a context that is passed from the server. Its the same value that is passed by the third parameter of GraphQL resolver.

Next step is to use the transform function created above to transform your GraphQL schema.

import auth from "@graphql-directive/auth"
import { makeExecutableSchema } from "@graphql-tools/schema"

const schema = transform(makeExecutableSchema({
    typeDefs: [auth.typeDefs, typeDefs],
    resolvers: {
        /* list of resolvers */
    }
}))

You can use executable schema above on any GraphQL server. In this example we use Apollo GraphQL

import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone"

const server = new ApolloServer({ schema })
const { url } = await startStandaloneServer(server, {
    // the main logic to create contextValue that is used when creating auth policy
    context: async ({ req, res }) => {
        // read JWT token (Bearer <token>)
        const token = req.headers["authorization"]
        if (!token || !token.toLowerCase().startsWith("Bearer")) return {}
        const user = verify(token.split(" ")[1], "lorem")
        if (typeof user === "string") return {}
        return user 
    }
})
console.log(`Server running at ${url}`)

API Documentation

createTransformer

The createTransformer function is a higher-order function that returns a function to transform a GraphQLSchema. The returned function takes a GraphQLSchema and returns a new transformed schema.

Arguments

  • options (optional): An object that can contain the following properties:
    • policies: A record of policy functions. Each policy function takes an AuthorizeContext object as an argument and returns a boolean or a promise that resolves to a boolean. Default is an empty object.
    • queryResolution: A string that specifies how the result will be resolved when unauthorized user access a field. Possible values are "ThrowError" and "Filter". Default is "Filter". ThrowError

Return value

The createTransformer function returns a transformer function that takes a GraphQLSchema and returns a new transformed schema.

Policies

policies property is a key-value object consist of authorization policy logic. It takes an AuthorizeContext object as input and returns a boolean or a Promise<boolean> that resolves to a boolean indicating whether the user is authorized to perform the requested operation.

type PolicyFunction = (ctx: AuthorizationContext) => boolean | Promise<boolean>

The AuthorizeContext object has the following properties:

  • path: The location of where validator applied from the root path through the GraphQL fields

  • contextValue: An object shared across all resolvers that are executing for a particular operation. Use this to share per-operation state, including authentication information, dataloader instances, and anything else to track across resolvers.

  • parent: The return value of the resolver for this field's parent (i.e., the previous resolver in the resolver chain).

  • args: An object that contains all GraphQL arguments provided for this field.

  • info: Contains information about the operation's execution state, including the field name, the path to the field from the root, and more.

  • directiveArgs: Object that is passed into the directive, for example if the directive is @authorize(policy: "Admin, User"), the value is { policy: "Admin, User" }

Query Resolution

Query resolution is how the query resolved based on user authorization. There are two resolution logic provided: ThrowError and Filter.

By default the query resolution used is Filter, its mean that if a user doesn't have access to protected field, server will filter the value by returning null. Based on example above, below query will behave differently based on user role.

query {
    users { name, email, role }
}

For Admin, above query will return a complete result including the role. But for other user the role field will be null.

Important to note that when query resolution set to Filter, make sure the data type of the filed where the directive applied must be nullable. An informative error will be thrown if its not satisfied.

ThrowError provide stricter authorization resolution by throwing an error when user doesn't have access to specific field. This is the best option when you strictly not allowed null in your schema. Based on previous example query, non admin user will get GraphQLError when requesting the role field. The error returned contains information of forbidden field path.

{
  "data": {},
  "errors": [
    {
      "message": "AUTHORIZATION_ERROR",
      "extensions": {
        "paths": [
          "users.role"
        ],
        "code": "INTERNAL_SERVER_ERROR",
        "stacktrace": [ ]
      }
    }
  ]
}