@ryanchenkie
2. Custom Directives
const authMiddleware = (req, res, next) => {
if (userIsAuthenticated(req)) {
next();
} else {
res.status(401).send("Sorry, you're not allowed here!");
}
app.get('/private-data', authMiddleware, (req, res) => {
res.send(somePrivateData);
});
const authMiddleware = ...
app.use('/graphql', authMiddleware, graphqlHTTP({
schema
});
const jwt = require('express-jwt');
const jwtDecode = require('jwt-decode');
const jwtMiddleware = jwt({ secret: 'some-strong-secret-key' });
const getUserFromJwt = (req, res, next) => {
const authHeader = req.headers.authorization;
req.user = jwtDecode(authHeader);
next();
}
app.use(jwtMiddleware);
app.use(getUserFromJwt);
const resolvers = {
Query: {
articlesByAuthor: (_, args, context) => {
return model.getArticles(context.user.sub);
}
}
}
const resolvers = {
Query: {
articlesByAuthor: (_, args, context) => {
const scope = context.user.scope;
if (scope.includes('read:articles')) {
return model.getArticles(context.user.id);
}
}
}
}
const checkScopeAndResolve = (scope, expectedScope, controller) => {
const hasScope = scope.includes(expectedScope);
if (!expectedScopes.length || hasScope) {
return controller.apply(this);
}
}
const controller = model.getArticles(context.user.id);
const resolvers = {
Query: {
articlesByAuthor: (_, args, context)
=> checkScopeAndResolve(
context.user.scope,
['read:articles'],
controller
);
}
}
import { createError } from 'apollo-errors';
import jwt from 'jsonwebtoken';
const AuthorizationError = createError('AuthorizationError', {
message: 'You are not authorized!'
});
const checkScopeAndResolve = (context, expectedScope, controller) => {
const token = context.headers.authorization;
try {
const jwtPayload = jwt.verify(token.replace('Bearer ', ''), secretKey);
const hasScope = jwtPayload.scope.includes(expectedScope);
if (!expectedScopes.length || hasScope) {
return controller.apply(this);
}
} catch (err) {
throw new AuthorizationError();
}
}
query Hero($episode: Episode, $withFriends: Boolean!) {
hero(episode: $episode) {
name
friends @include(if: $withFriends) {
name
}
}
}
const typeDefs = `
directive @isAuthenticated on QUERY | FIELD
directive @hasScope(scope: [String]) on QUERY | FIELD
type Article {
id: ID!
author: String!
reviewerComments: [ReviewerComment] @hasScope(scope: ["read:comments"])
}
type Query {
allArticles: [Article] @isAuthenticated
}
`;
const directiveResolvers = {
isAuthenticated(result, source, args, context) {
const token = context.headers.authorization;
// ...
},
hasScope(result, source, args, context) {
const token = context.headers.authorization;
// ...
}
};
const attachDirectives = schema => {
forEachField(schema, field => {
const directives = field.astNode.directives;
directives.forEach(directive => {
...
});
});
};
github.com/chenkie/graphql-auth
@ryanchenkie
bit.ly/graphql-auth
graphqlworkshops.com