Skip to content

Instantly share code, notes, and snippets.

@lindenmelvin
Last active March 10, 2020 19:59
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 lindenmelvin/a0ac0d571eecc65838fc90a191c81839 to your computer and use it in GitHub Desktop.
Save lindenmelvin/a0ac0d571eecc65838fc90a191c81839 to your computer and use it in GitHub Desktop.

Setup

  • Clone all files into a directory
  • Run yarn
  • Run export JWT_SECRET="your_jwt_secret_key"
  • Run node server.js
  • Run node client-auth.js
  • See a console log lists of posts fetched from GraphQL
const jwt = require('jsonwebtoken');
module.exports = {
authenticate: (email, password) => {
let user;
// Implement the credential validation however you'd like.
if (email.length && password.length) {
user = {
id: 1,
roles: ["admin"]
}
}
if (!user) throw new Error("Invalid credentials.");
return jwt.sign(user, process.env.JWT_SECRET);
},
validateToken: token => {
try {
const { id, roles } = jwt.verify(token, process.env.JWT_SECRET);
return { id, roles };
} catch (e) {
throw new Error('Authentication token is invalid.');
}
}
}
const axios = require("axios");
axios.defaults.baseURL = "http://localhost:4000/";
const login = async (email, password) => {
const axiosResponse = await axios.post('/', {
query: `mutation Login {
login(email: "${email}", password: "${password}") {
token
}
}`
});
const graphqlQueryData = axiosResponse.data;
const authenticationToken = graphqlQueryData.data.login.token;
axios.defaults.headers.common['Authorization'] = `Bearer ${authenticationToken}`;
}
const posts = async () => {
const axiosResponse = await axios.post('/', {
query: `query FetchPosts {
posts {
title
}
}`
});
const graphqlQueryData = axiosResponse.data;
const posts = graphqlQueryData.data.posts;
return posts;
}
const main = async () => {
await login("foo@example.com", "bar");
const postsForUser = await posts();
console.log("posts", postsForUser)
}
main();
{
"name": "graphql-authorization-and-authentication",
"version": "1.0.0",
"main": "server.js",
"license": "MIT",
"dependencies": {
"apollo-server": "^2.9.16",
"axios": "^0.19.2",
"jsonwebtoken": "^8.5.1"
}
}
module.exports = [
{
authorId: 1,
title: "Post 1"
},
{
authorId: 1,
title: "Post 2"
},
{
authorId: 2,
title: "Post 3"
},
{
authorId: 2,
title: "Post 4"
}
];
const posts = require("./posts");
const { authenticate } = require("./auth-service");
module.exports = {
Query: {
posts: (_parent, _args, context, _info) => {
return posts.filter(post => {
const isAuthor = post.authorId === context.user.id;
const isAdmin = context.user.roles.includes("admin");
return isAuthor || isAdmin;
});
}
},
Mutation: {
login: (_parent, args, _context, _info) => {
const { email, password } = args;
const token = authenticate(email, password);
return { token };
}
}
};
const { ApolloServer, gql } = require('apollo-server');
const { validateToken } = require("./auth-service");
const resolvers = require("./resolvers");
const typeDefs = gql`
type User {
id: ID!
email: String!
password: String!
}
type Post {
title: String!
authorId: ID!
}
type AuthResponse {
token: String!
}
type Query {
posts: [Post!]!
}
type Mutation {
login(email: String!, password: String!): AuthResponse!
}
`;
const context = ({ req }) => {
if (req.body.query.match("Login")) return {};
const authorizationHeader = req.headers.authorization || '';
const token = authorizationHeader.split(' ')[1];
if (!token) throw new Error("Authentication token is required.");
const user = validateToken(token);
return { user };
}
const server = new ApolloServer({
typeDefs,
resolvers,
context,
playground: false
});
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment