mercurius-auth supports the following options:
- applyPolicy
(policy: any, parent: object, args: Record<string, any>, context: MercuriusContext, info: GraphQLResolveInfo) => Promise<boolean | Error>
- the policy promise to run when an auth protected field is selected by the query. This must returntrue
in order to pass the check and allow access to the protected field. - authContext
(context: MercuriusContext) => object | Promise<object>
(optional) - assigns the returned data toMercuriusContext.auth
for use in theapplyPolicy
function. This runs within apreExecution
Mercurius GraphQL request hook. - mode
'directive' | 'external'
(optional, default:'directive'
) - the mode of operation for the plugin. Depending on the mode of operation selected, this has the following options:
-
authDirective
string
- the name of the directive that the Mercurius auth plugin will look for within the GraphQL schema in order to identify protected fields. For example, for directive definitiondirective @auth on OBJECT | FIELD_DEFINITION
, the corresponding name would beauth
. -
filterSchema
boolean
- whentrue
, introspection queries will only return the parts of the schema which are accessible based on the applied policies.
- policy
MercuriusAuthPolicy
(optional) - the auth policy definition. The field definition is passed as the first argument whenapplyPolicy
is called for the associated field.
Extends: Record<string, MercuriusAuthTypePolicy>
Each key within the MercuriusAuthPolicy
type corresponds with the GraphQL type name. For example, if we wanted to protect an object type:
type Message {
...
}
We would use the key: Message
:
{
Message: { ... }
}
Extends: Record<string, any>
- __typePolicy
any
(optional) - The policy definition for the type.
Each key within the MercuriusAuthTypePolicy
type corresponds with the GraphQL field name on a type. For example, if we wanted to protect type field message
:
type Message {
title: String
message: String
}
We would use the key: message
:
{
Message: {
message: { requires: 'user' }
}
}
If we want to protect the entire type, we would use __typePolicy
:
{
Message: {
__typePolicy: { requires: 'user' }
}
}
This also works alongside specific field policies on the type:
{
Message: {
__typePolicy: { requires: 'user', }
message: { requires: 'admin' }
}
}
The plugin must be registered after Mercurius is registered.
'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 {
add(x: Int, y: Int): Int @auth(requires: USER)
}
`
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({ port: 3000 })
'use strict'
const Fastify = require('fastify')
const mercurius = require('mercurius')
const mercuriusAuth = require('..')
const app = Fastify()
const schema = `
type Message {
title: String
message: String
adminMessage: String
}
type Query {
messages: [Message]
message(title: String): Message
}
`
const messages = [
{
title: 'one',
message: 'one',
adminMessage: 'admin message one'
},
{
title: 'two',
message: 'two',
adminMessage: 'admin message two'
}
]
const resolvers = {
Query: {
messages: async (parent, args, context, info) => {
return messages
},
message: async (parent, args, context, info) => {
return messages.find(message => message.title === args.title)
}
}
}
app.register(mercurius, {
schema,
resolvers
})
app.register(mercuriusAuth, {
authContext (context) {
const permissions = context.reply.request.headers['x-user'] || ''
return { permissions }
},
async applyPolicy (policy, parent, args, context, info) {
return context.auth.permissions.includes(policy.requires)
},
mode: 'external',
policy: {
Message: {
__typePolicy: { requires: 'user' },
adminMessage: { requires: 'admin' }
},
Query: {
messages: { requires: 'user' }
}
}
})
app.listen({ port: 3000 })