summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWes Bos <wesbos@gmail.com>2018-05-15 22:15:36 -0400
committerWes Bos <wesbos@gmail.com>2018-05-15 22:15:36 -0400
commit3f9b14e3c1d7f47d9b3e48b94b3b5bf2c722205e (patch)
tree63373ce8c75e51305b7899af6c53903ef6c4f179
parentae1a94b08f5aba0c16c6536e388b4dd4a53120fd (diff)
migrate to cookies for jwt
-rw-r--r--backend/package-lock.json9
-rw-r--r--backend/package.json1
-rw-r--r--backend/src/index.js23
-rw-r--r--backend/src/resolvers/Mutation.js24
-rw-r--r--backend/src/resolvers/Query.js3
-rw-r--r--backend/src/schema.graphql1
-rw-r--r--frontend/components/Signin.js2
-rw-r--r--frontend/components/Signout.js17
-rw-r--r--frontend/components/Signup.js2
-rw-r--r--frontend/lib/withData.js19
-rw-r--r--frontend/queries/queries.graphql6
11 files changed, 71 insertions, 36 deletions
diff --git a/backend/package-lock.json b/backend/package-lock.json
index f59765c..fd2a1a7 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -2469,6 +2469,15 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
+ "cookie-parser": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.3.tgz",
+ "integrity": "sha1-D+MfoZ0AC5X0qt8fU/3CuKIDuqU=",
+ "requires": {
+ "cookie": "0.3.1",
+ "cookie-signature": "1.0.6"
+ }
+ },
"cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
diff --git a/backend/package.json b/backend/package.json
index 1ca4ff9..a2c9199 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -8,6 +8,7 @@
},
"dependencies": {
"bcryptjs": "2.4.3",
+ "cookie-parser": "^1.4.3",
"graphql": "^0.13.2",
"graphql-yoga": "1.13.1",
"jsonwebtoken": "8.2.1",
diff --git a/backend/src/index.js b/backend/src/index.js
index 04d13cc..6978225 100644
--- a/backend/src/index.js
+++ b/backend/src/index.js
@@ -3,14 +3,16 @@ require('dotenv').config({ path: 'variables.env' });
/* eslint-enable */
const jwt = require('jsonwebtoken');
const createServer = require('./createServer');
+const cookieParser = require('cookie-parser');
const server = createServer();
+server.express.use(cookieParser());
+
// 1. Check JWT
server.express.use((req, res, next) => {
- const Authorization = req.get('Authorization');
- if (Authorization) {
- const token = Authorization.replace('Bearer ', '');
+ const { token } = req.cookies;
+ if (token) {
const { userId } = jwt.verify(token, process.env.APP_SECRET);
req.userId = userId;
}
@@ -30,6 +32,15 @@ server.express.use(async (req, res, next) => {
next();
});
-server.start({ port: 4444 }, deets => {
- console.log(`Server is running on http://localhost:${deets.port}`);
-});
+server.start(
+ {
+ cors: {
+ credentials: true,
+ origin: process.env.FRONTEND_URL,
+ },
+ port: 4444,
+ },
+ deets => {
+ console.log(`Server is running on http://localhost:${deets.port}`);
+ }
+);
diff --git a/backend/src/resolvers/Mutation.js b/backend/src/resolvers/Mutation.js
index 268b348..153833d 100644
--- a/backend/src/resolvers/Mutation.js
+++ b/backend/src/resolvers/Mutation.js
@@ -22,10 +22,18 @@ const mutations = {
info
);
- return {
- token: jwt.sign({ userId: user.id }, process.env.APP_SECRET),
- user,
- };
+ const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
+ ctx.response.cookie('token', token, {
+ maxAge: 1000 * 60 * 60 * 24 * 365,
+ httpOnly: true,
+ });
+ return { user };
+ },
+
+ async signout(parent, args, ctx, info) {
+ ctx.response.clearCookie('token');
+ // TODO: What do we return here?
+ return { id: 'abc123' };
},
async signin(parent, { email, password }, ctx, info) {
@@ -38,8 +46,14 @@ const mutations = {
if (!valid) {
throw new Error('Invalid password');
}
+ // set the cookie
+ const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
+ ctx.response.cookie('token', token, {
+ maxAge: 1000 * 60 * 60 * 24 * 365,
+ httpOnly: true,
+ });
return {
- token: jwt.sign({ userId: user.id }, process.env.APP_SECRET),
+ token,
user,
};
},
diff --git a/backend/src/resolvers/Query.js b/backend/src/resolvers/Query.js
index 276f7fa..b1e0c5b 100644
--- a/backend/src/resolvers/Query.js
+++ b/backend/src/resolvers/Query.js
@@ -36,8 +36,7 @@ const Query = {
},
me(parent, args, ctx, info) {
- const Authorization = ctx.request.get('Authorization');
- if (!Authorization || Authorization === 'null') {
+ if (!ctx.request.userId) {
return null; // don't error out, just return nothing
}
diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql
index 5c77d56..14d4797 100644
--- a/backend/src/schema.graphql
+++ b/backend/src/schema.graphql
@@ -18,6 +18,7 @@ type Mutation {
removeFromCart(id: ID!): CartItem
createOrder(token: String!): Order!
updateUser(name: String): User
+ signout: User
updatePermissions(permissions: [Permission], userId: ID!): User
}
diff --git a/frontend/components/Signin.js b/frontend/components/Signin.js
index f719c60..7a4db8c 100644
--- a/frontend/components/Signin.js
+++ b/frontend/components/Signin.js
@@ -13,7 +13,7 @@ class Signin extends Component {
loginUser = async (e, signin, client) => {
e.preventDefault();
const res = await signin();
- localStorage.setItem('token', res.data.signin.token);
+ // TODO instead of refetching, can we just use the data returned?
client.query({ query: CURRENT_USER_QUERY, fetchPolicy: 'network-only' });
};
diff --git a/frontend/components/Signout.js b/frontend/components/Signout.js
index 4ef8e6c..fbd7fd9 100644
--- a/frontend/components/Signout.js
+++ b/frontend/components/Signout.js
@@ -1,20 +1,13 @@
import React, { Component } from 'react';
-import { Query } from 'react-apollo';
-import { CURRENT_USER_QUERY } from '../queries/queries.graphql';
+import { Query, Mutation } from 'react-apollo';
+import { CURRENT_USER_QUERY, SIGN_OUT_MUTATION } from '../queries/queries.graphql';
class Signout extends Component {
- signout = refetch => {
- console.log(refetch);
- console.log('Signing Out');
- localStorage.removeItem('token');
- refetch();
- };
-
render() {
return (
- <Query query={CURRENT_USER_QUERY}>
- {({ refetch }) => <button onClick={() => this.signout(refetch)}>Sign Out</button>}
- </Query>
+ <Mutation mutation={SIGN_OUT_MUTATION} refetchQueries={[{ query: CURRENT_USER_QUERY }]}>
+ {signout => <button onClick={signout}>Sign Out</button>}
+ </Mutation>
);
}
}
diff --git a/frontend/components/Signup.js b/frontend/components/Signup.js
index e8c639b..22a729f 100644
--- a/frontend/components/Signup.js
+++ b/frontend/components/Signup.js
@@ -26,7 +26,7 @@ class Signup extends Component {
onSubmit={async e => {
e.preventDefault();
const res = await signup();
- localStorage.setItem('token', res.data.signup.token);
+ // TODO can we return theuser from signup?
client.query({ query: CURRENT_USER_QUERY, fetchPolicy: 'network-only' });
}}
>
diff --git a/frontend/lib/withData.js b/frontend/lib/withData.js
index 9f7c2a8..0696333 100644
--- a/frontend/lib/withData.js
+++ b/frontend/lib/withData.js
@@ -6,15 +6,16 @@ import { LOCAL_STATE_QUERY } from '../queries/queries.graphql';
const client = new ApolloClient({
uri: process.env.NODE_ENV === 'development' ? 'http://localhost:4444' : 'http://localhost:4444',
ssrMode: !process.browser, // Disables forceFetch on the server (so queries are only run once)
- request: operation => {
- // if we're in the client and the user has a token, send it along with the request
- if (typeof localStorage !== 'undefined' && localStorage.getItem('token')) {
- operation.setContext({
- headers: {
- authorization: `Bearer ${localStorage.getItem('token')}`,
- },
- });
- }
+ // TODO this is a bug: https://github.com/apollographql/apollo-client/issues/3265
+ fetchOptions: {
+ credentials: 'include',
+ },
+ request: async operation => {
+ operation.setContext({
+ fetchOptions: {
+ credentials: 'include',
+ },
+ });
},
clientState: {
resolvers: {
diff --git a/frontend/queries/queries.graphql b/frontend/queries/queries.graphql
index 61d5587..22e5f29 100644
--- a/frontend/queries/queries.graphql
+++ b/frontend/queries/queries.graphql
@@ -36,6 +36,12 @@ mutation SIGNIN_MUTATION($email: String!, $password: String!) {
}
}
+mutation SIGN_OUT_MUTATION {
+ signout {
+ id
+ }
+}
+
mutation REQUEST_RESET_MUTATION($email: String!) {
requestReset(email: $email) {
id