Skip to content

Instantly share code, notes, and snippets.

@eosemeyko
Created February 9, 2019 18:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eosemeyko/eb51851a57c18d1c24bdf92bda86c26c to your computer and use it in GitHub Desktop.
Save eosemeyko/eb51851a57c18d1c24bdf92bda86c26c to your computer and use it in GitHub Desktop.
graphql Auth Directive
import { AuthenticationError } from 'apollo-server-errors';
import { defaultFieldResolver } from 'graphql';
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { verifyAndDecodeToken } from '../common/verifyAndDecodeToken';
export class AuthDirective extends SchemaDirectiveVisitor {
visitObject(type) {
this.ensureFieldsWrapped(type);
type._requiredAuthRole = this.args.role;
}
visitFieldDefinition(field, details) {
this.ensureFieldsWrapped(details.objectType);
field._requiredAuthRole = this.args.role;
}
ensureFieldsWrapped(objectType) {
// Mark the GraphQLObjectType object to avoid re-wrapping:
if (objectType._authFieldsWrapped) return;
objectType._authFieldsWrapped = true;
const fields = objectType.getFields();
Object.keys(fields).forEach(fieldName => {
const field = fields[fieldName];
const { resolve = defaultFieldResolver } = field;
field.resolve = async (...args) => {
const requiredRole = field._requiredAuthRole || objectType._requiredAuthRole;
// Если не требуется авторизация пропускаем запрос
if (!requiredRole) return resolve.apply(this, args);
// Decode Bearer token
const decoded = verifyAndDecodeToken({ context: args[2] });
const role = (decoded as any).role;
// TODO: Если отсутствует роль в токене отказываем
if (!role) throw new AuthenticationError('Access denied to this resource');
// Проверка роли доступа. Если админ все разрешено
if (requiredRole === role || role === 'ADMIN') return resolve.apply(this, args);
// Доступ этой роли запрещен
throw new AuthenticationError('Access denied to this resource');
};
});
}
}
import { makeExecutableSchema } from 'graphql-tools';
import { fileLoader, mergeTypes } from 'merge-graphql-schemas';
import * as path from 'path';
import { AuthDirective } from './directives/AuthDirective';
import { resolvers } from './resolvers';
const typesArray = fileLoader(path.join(__dirname, './schema'));
const typeDefs = mergeTypes(typesArray, { all: true });
export const schema = makeExecutableSchema({
resolvers,
typeDefs,
schemaDirectives: {
auth: AuthDirective,
},
});
directive @auth(role: Role = ADMIN) on OBJECT | FIELD_DEFINITION
enum Role {
USER
ADMIN
}
type User @auth(role: USER) {
id: ID
name: String
password: String @auth
createdAt: DateTime
updatedAt: DateTime
}
type Query {
users: [User] @auth
user(id: ID): User @auth(role: USER)
}
type Mutation {
createUser (name: String): Boolean @auth
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment