Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@graphql-directive/auth

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@graphql-directive/auth - npm Package Compare versions

Comparing version 1.0.0 to 1.0.1

7

CHANGELOG.md

@@ -6,2 +6,9 @@ # Change Log

## [1.0.1](https://github.com/ktutnik/graphql-directive/compare/@graphql-directive/auth@1.0.0...@graphql-directive/auth@1.0.1) (2023-04-08)
### Bug Fixes
- **auth:** Export some types required by TypeScript typings ([#35](https://github.com/ktutnik/graphql-directive/issues/35)) ([71b450e](https://github.com/ktutnik/graphql-directive/commit/71b450eeff18bc55875815a5ab706c9b70135ff9))
- Upgrade package dependencies ([#36](https://github.com/ktutnik/graphql-directive/issues/36)) ([edbdb8e](https://github.com/ktutnik/graphql-directive/commit/edbdb8e2f1fa3ab5dd8d73c0ef6fd34e5057cf67))
# 1.0.0 (2023-04-07)

@@ -8,0 +15,0 @@

6

lib/index.d.ts
import { InvocationContext } from "@graphql-directive/core";
import { GraphQLSchema } from "graphql";
type AuthorizeContext = Omit<InvocationContext, "directives"> & {
export type AuthorizeContext = Omit<InvocationContext, "directives"> & {
directiveArgs: any;
};
type PolicyFunction = (ctx: AuthorizeContext) => boolean | Promise<boolean>;
type AuthorizeOptions = {
export type PolicyFunction = (ctx: AuthorizeContext) => boolean | Promise<boolean>;
export type AuthorizeOptions = {
policies: Record<string, PolicyFunction>;

@@ -9,0 +9,0 @@ queryResolution: "ThrowError" | "Filter";

@@ -65,4 +65,4 @@ "use strict";

exports.default = {
typeDefs, createTransformer
typeDefs, createTransformer,
};
//# sourceMappingURL=index.js.map
{
"name": "@graphql-directive/auth",
"version": "1.0.0",
"version": "1.0.1",
"description": "GraphQL authorization directive",

@@ -26,3 +26,3 @@ "main": "lib/index.js",

"dependencies": {
"@graphql-directive/core": "^1.0.0"
"@graphql-directive/core": "^1.0.1"
},

@@ -33,8 +33,8 @@ "funding": [

"devDependencies": {
"@graphql-tools/schema": "^9.0.16",
"@types/jest": "^29.4.0",
"@graphql-tools/schema": "^9.0.17",
"@types/jest": "^29.5.0",
"graphql": "^16.6.0",
"jest": "^29.4.3",
"jest": "^29.5.0",
"trash-cli": "^5.0.0",
"ts-jest": "^29.0.5",
"ts-jest": "^29.1.0",
"ts-node": "^10.9.1"

@@ -51,3 +51,3 @@ },

},
"gitHead": "280f220ce6940002f6181872fe3ef2ea45403543"
"gitHead": "2ab9b1bc3d03fcead54d1ae6ef4b56e71c684a35"
}

@@ -8,54 +8,173 @@ # GraphQL Authorization Directive

## Motivation
GraphQL Authorization Directive aims to simplify the process of adding authorization to your GraphQL API. By using directives, you can easily apply authorization logic to your schema without having to manually implement complex middleware functions.
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
```javascript
import auth from "@graphql-directive/auth"
import { ApolloServer } from "@apollo/server"
import { startStandaloneServer } from "@apollo/server/standalone"
import { makeExecutableSchema } from "@graphql-tools/schema"
Authorization method wrapped into single directive `@authorize(policy: "list, of, policy")` like example below.
```graphql
const typeDefs = `
type User {
name: String!
email: String!
role: String @authorize(policy: "admin")
}
input UserInput {
name: String!
name: String!
email: String!
role: String @authorize(policy: "admin")
# role only can be set by Admin
role: String @authorize(policy: "Admin")
}
input 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 {
getUsers: [User]!
users: [User]!
}
type Mutation {
addUser(user:UserInput!): Boolean!
modifyUser(user: UserInput!): Boolean!
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.
```typescript
import auth from "@graphql-directive/auth"
const transform = auth.createTransformer({
policies: {
admin: ({ contextValue }) => contextValue.user.role === "admin",
isLogin: ({ contextValue }) => !!contextValue.user
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.
```typescript
import auth from "@graphql-directive/auth"
import { makeExecutableSchema } from "@graphql-tools/schema"
const schema = transform(makeExecutableSchema({
typeDefs: [auth.typeDefs, typeDefs],
resolvers: {
Query: { getUsers: () => ([]) },
Mutation: {
addUser: () => true,
modifyUser: ()=> true
}
/* list of resolvers */
}
}))
```
You can use executable schema above on any GraphQL server. In this example we use Apollo GraphQL
```typescript
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone"
const server = new ApolloServer({ schema })
startStandaloneServer(server, { context: async ({ req, res }) => ({}) }).then(x => console.log(x.url))
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}`)
```
## 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.
```graphql
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.
```json
{
"data": {},
"errors": [
{
"message": "AUTHORIZATION_ERROR",
"extensions": {
"paths": [
"users.role"
],
"code": "INTERNAL_SERVER_ERROR",
"stacktrace": [ ]
}
}
]
}
```
## 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.
```typescript
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" }`

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc