Created
October 11, 2018 19:06
-
-
Save lfades/633e503f26f3f8ade2f4fa557db3a931 to your computer and use it in GitHub Desktop.
Next.js Auth implementation: Apollo Server + Passport + Passport-jwt
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
/** | |
* client/pages/_app.js | |
* Implementation of the withApollo HOC in _app.js, this makes Apollo available in every page | |
*/ | |
import App, { Container } from 'next/app'; | |
import { ApolloProvider } from 'react-apollo'; | |
import withApollo from '../lib/withApollo'; | |
class MyApp extends App { | |
render() { | |
const { Component, pageProps, apollo } = this.props; | |
return ( | |
<Container> | |
<ApolloProvider client={apollo}> | |
<Component {...pageProps} /> | |
</ApolloProvider> | |
</Container> | |
); | |
} | |
} | |
export default withApollo(MyApp); |
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
/** | |
* server/app.js | |
* Apollo Server implementation | |
*/ | |
const path = require('path'); | |
const { ApolloServer } = require('apollo-server'); | |
const { importSchema } = require('graphql-import'); | |
const cookieParser = require('cookie-parser'); | |
const { APP_URL } = require('./configs'); | |
const resolvers = require('./resolvers'); | |
const schemaDirectives = require('./schemaDirectives'); | |
const dataSources = require('./dataSources'); | |
const Auth = require('./dataSources/Auth'); | |
const typeDefs = importSchema( | |
path.join(__dirname, './typeDefs/Schema.graphql') | |
); | |
/** | |
* Create our instance of cookieParser and a function (addCookies) to use it as | |
* a Promise instead of a middleware in express | |
*/ | |
const cp = cookieParser(); | |
const addCookies = (req, res) => | |
new Promise(resolve => { | |
cp(req, res, resolve); | |
}); | |
const server = new ApolloServer({ | |
typeDefs, | |
resolvers, | |
schemaDirectives, | |
dataSources, | |
cors: { | |
origin: APP_URL, | |
credentials: true, | |
optionsSuccessStatus: 200, | |
methods: ['POST'] | |
}, | |
playground: { | |
settings: { | |
// possible values: 'line', 'block', 'underline' | |
'editor.cursorShape': 'block', | |
'request.credentials': 'include' | |
} | |
}, | |
context: async ({ req, res }) => { | |
const auth = new Auth({ req, res }); | |
await addCookies(req, res); | |
await auth.authenticate(); | |
return { auth }; | |
} | |
}); | |
server.listen().then(({ url }) => { | |
console.log(`🚀 Server ready at ${url}`); | |
}); |
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
/** | |
* server/dataSources/Auth.js | |
* Auth implementation, it's not really a dataSource so it doesn't need to be here | |
*/ | |
const { authenticate, createJwt } = require('../lib/passport'); | |
const { ON_HTTPS } = require('../configs'); | |
const ONE_MINUTE = 1000 * 60; | |
const ONE_DAY = ONE_MINUTE * 60 * 24; | |
const ONE_MONTH = ONE_DAY * 30; | |
class Auth { | |
constructor({ req, res }) { | |
this.req = req; | |
this.res = res; | |
this.isReady = false; | |
this.hasSignedIn = false; | |
this.accessTokenName = 'ip_at'; | |
} | |
async authenticate() { | |
const { req, res } = this; | |
// The token is very likely to be in cookies | |
if (!req.headers.authorization) { | |
const cookie = req.cookies[this.accessTokenName]; | |
if (cookie) req.headers.authorization = `bearer ${cookie}`; | |
} | |
const payload = await authenticate(req, res); | |
if (payload) { | |
this.payload = payload; | |
this.hasSignedIn = true; | |
} | |
} | |
signInWithJWT(user) { | |
const token = createJwt({ uid: user.id }); | |
this.res.cookie(this.accessTokenName, token, { | |
secure: ON_HTTPS, | |
httpOnly: true, | |
expires: new Date(Date.now() + ONE_MONTH) | |
}); | |
} | |
logout() { | |
this.res.clearCookie(this.accessTokenName); | |
} | |
getUserId() { | |
return this.payload.uid; | |
} | |
} | |
module.exports = Auth; |
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
/** | |
* server/lib/passport.js | |
* Passport.js + passport-jwt implementation, it's really simple and based in a httpOnly cookie that lasts for 1 month | |
*/ | |
const jwt = require('jsonwebtoken'); | |
const passport = require('passport'); | |
const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt'); | |
const { JWT_SECRET } = require('../configs'); | |
const verifyOptions = { | |
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), | |
secretOrKey: JWT_SECRET, | |
audience: 'inspector.com', | |
algorithms: ['HS256'] | |
}; | |
passport.use( | |
new JwtStrategy(verifyOptions, (payload, done) => done(null, payload)) | |
); | |
exports.createJwt = payload => | |
jwt.sign(payload, JWT_SECRET, { | |
algorithm: 'HS256', | |
audience: verifyOptions.audience, | |
expiresIn: '30 days' | |
}); | |
exports.authenticate = (req, res) => | |
new Promise((resolve, reject) => { | |
passport.authenticate('jwt', (err, payload) => { | |
if (err) reject(err); | |
resolve(payload); | |
})(req, res); | |
}); |
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
/** | |
* client/lib/withApollo.js | |
* Apollo Client implementation | |
*/ | |
import withApollo from 'next-with-apollo'; | |
import ApolloClient from 'apollo-boost'; | |
export default withApollo( | |
({ headers }) => | |
new ApolloClient({ | |
uri: process.API_URL, | |
credentials: 'include', | |
headers | |
}) | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@cantoute that file does something like this: