-
-
Save pedroraft/395c442fd980a9514e03384c60cfb773 to your computer and use it in GitHub Desktop.
full graphql jwt auth example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
JWT_SECRET=secret |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {GraphQLServerLambda} from 'graphql-yoga' | |
import {SchemaDirectiveVisitor} from 'graphql-tools' | |
import {defaultFieldResolver} from 'graphql' | |
import jwt from 'jsonwebtoken' | |
require('dotenv').config(); | |
const typeDefs = ` | |
directive @auth(roles: [String]) on QUERY | FIELD | |
type Query { | |
hello(name: String): String! | |
secret: String! @auth | |
admin: String @auth(roles: "admin") | |
reallySecret: String @auth(roles: ["kgb", "007"]) | |
} | |
` | |
const resolvers = { | |
Query: { | |
hello: (_, {name}) => `Hello ${name || 'World'}`, | |
secret: (_, {}, context) => 'shhhh', | |
admin: (_, {}, context) => 'admin', | |
reallySecret: (_, {}, context) => 'super secret agent man' | |
} | |
} | |
class AuthDirective extends SchemaDirectiveVisitor { | |
visitFieldDefinition(field) { | |
const {resolve = defaultFieldResolver} = field | |
const {roles: expectedRoles = []} = this.args | |
field.resolve = (...args) => { | |
const [, , context] = args | |
if (context.jwt) { | |
if (expectedRoles.length <= 0 || | |
expectedRoles.some(r => context.jwt.roles.includes(r)) | |
) { | |
// Call original resolver if role check has passed | |
return resolve.apply(this, args) | |
} | |
} | |
// We has two options here. throw an error or return null (if field is nullable). | |
throw new Error( | |
`You are not authorized. Expected roles: ${expectedRoles.join(', ')} for field ${field.name}`, | |
) | |
} | |
} | |
} | |
const makeContext = (req) => { | |
const token = req.event.headers.Authorization; | |
if (!token) { | |
return {} | |
} | |
const decoded = jwt.verify( | |
token.replace('Bearer ', ''), | |
process.env.JWT_SECRET | |
); | |
return {jwt: {...decoded}} | |
} | |
const lambda = new GraphQLServerLambda({ | |
typeDefs, | |
resolvers, | |
schemaDirectives: {auth: AuthDirective,}, | |
context: req => ({...makeContext(req)}) | |
}) | |
exports.server = lambda.graphqlHandler | |
exports.playground = lambda.playgroundHandler |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "lambda", | |
"version": "1.0.0", | |
"description": "", | |
"main": "handler.js", | |
"scripts": { | |
"start": "serverless offline", | |
"deploy": "serverless deploy", | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"keywords": [], | |
"author": "Pedro raft <pedroraft@gmail.com>", | |
"license": "MIT", | |
"dependencies": { | |
"dotenv": "^5.0.1", | |
"graphql-tools": "^2.24.0", | |
"graphql-yoga": "*", | |
"jsonwebtoken": "^8.2.1", | |
"jwt-decode": "^2.2.0" | |
}, | |
"devDependencies": { | |
"babel-core": "^6.26.0", | |
"babel-loader": "^7.1.3", | |
"babel-plugin-source-map-support": "^2.0.1", | |
"babel-plugin-transform-runtime": "^6.23.0", | |
"babel-preset-env": "^1.6.0", | |
"babel-preset-stage-3": "^6.24.1", | |
"eslint": "^4.19.1", | |
"eslint-config-airbnb-base": "^12.1.0", | |
"eslint-plugin-import": "^2.11.0", | |
"imports-loader": "^0.8.0", | |
"serverless": "1.25.0", | |
"serverless-offline": "3.16.0", | |
"serverless-webpack": "^5.1.1", | |
"webpack": "^4.5.0", | |
"webpack-node-externals": "^1.7.2" | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
service: yoga-example | |
plugins: | |
- serverless-webpack | |
- serverless-offline | |
custom: | |
webpack: | |
webpackConfig: 'webpack.config.js' | |
dynamodb: | |
start: | |
port: 8000 | |
inMemory: true | |
migrate: true | |
seed: true | |
provider: | |
name: aws | |
runtime: nodejs6.10 | |
functions: | |
graphql: | |
handler: src/handler.server | |
events: | |
- http: | |
path: / | |
method: post | |
cors: true | |
playground: | |
handler: src/handler.playground | |
events: | |
- http: | |
path: / | |
method: get | |
cors: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const path = require('path'); | |
const slsw = require('serverless-webpack'); | |
const nodeExternals = require('webpack-node-externals'); | |
module.exports = { | |
entry: slsw.lib.entries, | |
target: 'node', | |
mode: slsw.lib.webpack.isLocal ? 'development' : 'production', | |
optimization: { | |
// We no not want to minimize our code. | |
minimize: false | |
}, | |
performance: { | |
// Turn off size warnings for entry points | |
hints: false | |
}, | |
devtool: 'nosources-source-map', | |
externals: [nodeExternals()], | |
module: { | |
rules: [{ | |
test: /\.js$/, | |
use: [ | |
'imports-loader?graphql', | |
{ | |
loader: 'babel-loader', | |
options: { | |
presets: [ | |
["env", {"node": "6.10"}], | |
"stage-3" | |
], | |
plugins: [ | |
"source-map-support", | |
"transform-runtime" | |
] | |
} | |
}, | |
], | |
}], | |
}, | |
output: { | |
libraryTarget: 'commonjs2', | |
path: path.join(__dirname, '.webpack'), | |
filename: '[name].js', | |
sourceMapFilename: '[file].map' | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment