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

mercurius-auth

Package Overview
Dependencies
Maintainers
2
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mercurius-auth - npm Package Compare versions

Comparing version 1.1.0 to 1.2.0

examples/gateway-with-auth-on-type.js

60

docs/auth-directive.md

@@ -56,1 +56,61 @@ # Auth directive

```
The auth directive can also be use at the type level, to wrap all fields of a type (useful when working with federated types). You can nest auth directives this way as well to protect certain types/fields of a parent type.
```js
'use strict'
const Fastify = require('fastify')
const mercurius = require('mercurius')
const mercuriusAuth = require('mercurius-auth')
const app = Fastify()
const schema = `
directive @auth(
requires: Role = ADMIN,
) on OBJECT | FIELD_DEFINITION
enum Role {
ADMIN
REVIEWER
USER
UNKNOWN
}
type Query {
user: User
}
type User @auth(requires: USER) {
id: Int
name: String
location: String @auth(requires: ADMIN)
}
`
const resolvers = {
Query: {
add: async (_, { x, y }) => x + y
}
}
app.register(mercurius, {
schema,
resolvers
})
app.register(mercuriusAuth, {
authContext (context) {
return {
identity: context.reply.request.headers['x-user']
}
},
async applyPolicy (authDirectiveAST, parent, args, context, info) {
return context.auth.identity === 'admin'
},
authDirective: 'auth'
})
app.listen(3000)
```

41

lib/auth.js

@@ -37,2 +37,22 @@ 'use strict'

wrapFields (schemaType, authDirective) {
// Handle fields on schema type
if (typeof schemaType.getFields === 'function') {
for (const [fieldName, field] of Object.entries(schemaType.getFields())) {
if (typeof field.astNode !== 'undefined') {
// Override resolvers on protected fields
const authDirectiveASTForField = authDirective || this[kGetAuthDirectiveAST](field.astNode)
if (authDirectiveASTForField !== null) {
if (typeof field.resolve === 'function') {
const originalFieldResolver = field.resolve
field.resolve = this[kMakeProtectedResolver](authDirectiveASTForField, originalFieldResolver)
} else {
field.resolve = this[kMakeProtectedResolver](authDirectiveASTForField, (parent) => parent[fieldName])
}
}
}
}
}
}
registerAuthHandlers (schema) {

@@ -42,19 +62,10 @@ // Traverse schema types and override resolvers with auth protection where necessary

for (const schemaType of Object.values(schemaTypeMap)) {
// Handle fields on schema type
if (typeof schemaType.getFields === 'function') {
for (const [fieldName, field] of Object.entries(schemaType.getFields())) {
if (typeof field.astNode !== 'undefined') {
// Override resolvers on protected fields
const authDirectiveASTForField = this[kGetAuthDirectiveAST](field.astNode)
if (authDirectiveASTForField !== null) {
if (typeof field.resolve === 'function') {
const originalFieldResolver = field.resolve
field.resolve = this[kMakeProtectedResolver](authDirectiveASTForField, originalFieldResolver)
} else {
field.resolve = this[kMakeProtectedResolver](authDirectiveASTForField, (parent) => parent[fieldName])
}
}
}
// Handle directive on type
if (typeof schemaType.astNode !== 'undefined') {
const authDirectiveASTForType = this[kGetAuthDirectiveAST](schemaType.astNode)
if (authDirectiveASTForType !== null) {
this.wrapFields(schemaType, authDirectiveASTForType)
}
}
this.wrapFields(schemaType)
}

@@ -61,0 +72,0 @@ }

{
"name": "mercurius-auth",
"version": "1.1.0",
"version": "1.2.0",
"description": "Mercurius Auth Plugin adds configurable Authentication and Authorization support to Mercurius.",

@@ -34,3 +34,3 @@ "main": "index.js",

"@sinonjs/fake-timers": "^7.0.5",
"@types/node": "^15.0.2",
"@types/node": "^16.0.0",
"@types/ws": "^7.4.2",

@@ -42,3 +42,3 @@ "@typescript-eslint/eslint-plugin": "^4.1.0",

"fastify": "^3.0.2",
"mercurius": "^7.5.0",
"mercurius": "^8.0.0",
"pre-commit": "^1.2.2",

@@ -48,5 +48,5 @@ "snazzy": "^9.0.0",

"tap": "^15.0.2",
"tsd": "^0.14.0",
"tsd": "^0.17.0",
"typescript": "^4.0.3",
"wait-on": "^5.3.0"
"wait-on": "^6.0.0"
},

@@ -53,0 +53,0 @@ "dependencies": {

@@ -779,1 +779,261 @@ 'use strict'

})
test('basic - should work at type level with field resolvers', async (t) => {
t.plan(1)
const schema = `
directive @auth(
requires: Role = ADMIN,
) on OBJECT | FIELD_DEFINITION
enum Role {
ADMIN
REVIEWER
USER
UNKNOWN
}
type Query {
getUser: User
}
type User @auth(requires: USER) {
id: Int
name: String
}`
const resolvers = {
Query: {
getUser: async (_, obj) => ({
id: 1,
name: 'testuser',
test: 'TEST'
})
},
User: {
id: async (src) => src.id
}
}
const query = `query {
getUser {
id
name
}
}`
const app = Fastify()
t.teardown(app.close.bind(app))
app.register(mercurius, {
schema,
resolvers
})
app.register(mercuriusAuth, {
authContext (context) {
return {
identity: context.reply.request.headers['x-user']
}
},
async applyPolicy (authDirectiveAST, parent, args, context, info) {
return context.auth.identity === 'user'
},
authDirective: 'auth'
})
const response = await app.inject({
method: 'POST',
headers: { 'content-type': 'application/json', 'X-User': 'user' },
url: '/graphql',
body: JSON.stringify({ query })
})
t.same(JSON.parse(response.body), {
data: {
getUser: {
id: 1,
name: 'testuser'
}
}
})
})
test('basic - should work at type level with nested directive', async (t) => {
t.plan(1)
const schema = `
directive @auth(
requires: Role = ADMIN,
) on OBJECT | FIELD_DEFINITION
enum Role {
ADMIN
REVIEWER
USER
UNKNOWN
}
type Query {
getUser: User
}
type User @auth(requires: USER) {
id: Int
name: String
protected: String @auth(requires: ADMIN)
}`
const resolvers = {
Query: {
getUser: async (_, obj) => ({
id: 1,
name: 'testuser',
protected: 'protected data'
})
},
User: {
id: async (src) => src.id
}
}
const query = `query {
getUser {
id
name
protected
}
}`
const app = Fastify()
t.teardown(app.close.bind(app))
app.register(mercurius, {
schema,
resolvers
})
app.register(mercuriusAuth, {
authContext (context) {
return {
identity: context.reply.request.headers['x-user'].toUpperCase().split(',')
}
},
async applyPolicy (authDirectiveAST, parent, args, context, info) {
const findArg = (arg, ast) => {
let result
ast.arguments.forEach((a) => {
if (a.kind === 'Argument' &&
a.name.value === arg) {
result = a.value.value
}
})
return result
}
const requires = findArg('requires', authDirectiveAST)
return context.auth.identity.includes(requires)
},
authDirective: 'auth'
})
const response = await app.inject({
method: 'POST',
headers: { 'content-type': 'application/json', 'X-User': 'user' },
url: '/graphql',
body: JSON.stringify({ query })
})
t.same(JSON.parse(response.body), {
data: {
getUser: {
id: 1,
name: 'testuser',
protected: null
}
},
errors: [
{ message: 'Failed auth policy check on protected', locations: [{ line: 5, column: 7 }], path: ['getUser', 'protected'] }
]
})
})
test('basic - should error for all fields in type', async (t) => {
t.plan(1)
const schema = `
directive @auth(
requires: Role = ADMIN,
) on OBJECT | FIELD_DEFINITION
enum Role {
ADMIN
REVIEWER
USER
UNKNOWN
}
type Query {
getUser: User
}
type User @auth(requires: ADMIN) {
id: Int
name: String
}`
const resolvers = {
Query: {
getUser: async (_, obj) => ({
id: 1,
name: 'testuser'
})
},
User: {
id: async (src) => src.id
}
}
const query = `query {
getUser {
id
name
}
}`
const app = Fastify()
t.teardown(app.close.bind(app))
app.register(mercurius, {
schema,
resolvers
})
app.register(mercuriusAuth, {
authContext (context) {
return {
identity: context.reply.request.headers['x-user']
}
},
async applyPolicy (authDirectiveAST, parent, args, context, info) {
return context.auth.identity === 'admin'
},
authDirective: 'auth'
})
const response = await app.inject({
method: 'POST',
headers: { 'content-type': 'application/json', 'X-User': 'user' },
url: '/graphql',
body: JSON.stringify({ query })
})
t.same(JSON.parse(response.body), {
data: {
getUser: {
id: null,
name: null
}
},
errors: [
{ message: 'Failed auth policy check on id', locations: [{ line: 3, column: 7 }], path: ['getUser', 'id'] },
{ message: 'Failed auth policy check on name', locations: [{ line: 4, column: 7 }], path: ['getUser', 'name'] }
]
})
})

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