From 71ab2f3828492c52fcb4f7a0cd066650276f197d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Sch=C3=A4fer?= Date: Tue, 26 Feb 2019 15:43:03 +0100 Subject: [PATCH] Get rid of JWT_SECRET once and for all * refactor store/auth.js not to use `delete Object` * refactor store/auth.js to have less redundancy * implement fetchCurrentUser without knowing the id beforehand * test fetchCurrentUser and init --- .env.template | 1 - docker-compose.yml | 1 - store/auth.js | 74 +++++++++++---------------- store/auth.test.js | 123 ++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 141 insertions(+), 58 deletions(-) diff --git a/.env.template b/.env.template index 4313645bb..1fa2a542a 100644 --- a/.env.template +++ b/.env.template @@ -1,2 +1 @@ -JWT_SECRET="b/&&7b78BF&fv/Vd" MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ" diff --git a/docker-compose.yml b/docker-compose.yml index 64b743698..3b09fa57d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,6 @@ services: environment: - HOST=0.0.0.0 - BACKEND_URL=http://backend:4000 - - JWT_SECRET=b/&&7b78BF&fv/Vd - MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.bZ8KK9l70omjXbEkkbHGsQ" networks: diff --git a/store/auth.js b/store/auth.js index a6ab3b3f8..b1b029468 100644 --- a/store/auth.js +++ b/store/auth.js @@ -57,56 +57,44 @@ export const actions = { if (!token) { return } - - const payload = await jwt.verify(token, process.env.JWT_SECRET) - if (!payload.id) { - return - } commit('SET_TOKEN', token) - commit('SET_USER', { - id: payload.id - }) await dispatch('fetchCurrentUser') }, + async check({ commit, dispatch, getters }) { if (!this.app.$apolloHelpers.getToken()) { await dispatch('logout') } return getters.isLoggedIn }, - async fetchCurrentUser({ commit, getters }) { - await this.app.apolloProvider.defaultClient - .query({ - query: gql(` - query User($id: ID!) { - User(id: $id) { - id - name - slug - email - avatar - role - locationName - about - } + async fetchCurrentUser({ commit }) { + const client = this.app.apolloProvider.defaultClient + const { + data: { currentUser } + } = await client.query({ + query: gql(`{ + currentUser { + id + name + slug + email + avatar + role } - `), - variables: { id: getters.user.id } - }) - .then(({ data }) => { - const user = data.User.pop() - if (user.id && user.email) { - commit('SET_USER', user) - } - }) - return getters.user + }`) + }) + const { token, ...user } = currentUser + commit('SET_USER', user) + return user }, async login({ commit }, { email, password }) { commit('SET_PENDING', true) try { - const res = await this.app.apolloProvider.defaultClient - .mutate({ - mutation: gql(` + const client = this.app.apolloProvider.defaultClient + const { + data: { login } + } = await client.mutate({ + mutation: gql(` mutation($email: String!, $password: String!) { login(email: $email, password: $password) { id @@ -121,15 +109,13 @@ export const actions = { } } `), - variables: { email, password } - }) - .then(({ data }) => data && data.login) + variables: { email, password } + }) + const { token, ...user } = login - await this.app.$apolloHelpers.onLogin(res.token) - commit('SET_TOKEN', res.token) - const userData = Object.assign({}, res) - delete userData.token - commit('SET_USER', userData) + await this.app.$apolloHelpers.onLogin(token) + commit('SET_TOKEN', token) + commit('SET_USER', user) } catch (err) { throw new Error(err) } finally { diff --git a/store/auth.test.js b/store/auth.test.js index 98bd1ce13..290938b7e 100644 --- a/store/auth.test.js +++ b/store/auth.test.js @@ -2,23 +2,35 @@ import { getters, mutations, actions } from './auth.js' let state let commit +let dispatch const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InUzIiwic2x1ZyI6Implbm55LXJvc3RvY2siLCJuYW1lIjoiSmVubnkgUm9zdG9jayIsImF2YXRhciI6Imh0dHBzOi8vczMuYW1hem9uYXdzLmNvbS91aWZhY2VzL2ZhY2VzL3R3aXR0ZXIvbXV0dV9rcmlzaC8xMjguanBnIiwiZW1haWwiOiJ1c2VyQGV4YW1wbGUub3JnIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1NDUxNDQ2ODgsImV4cCI6MTYzMTU0NDY4OCwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDozMDAwIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo0MDAwIiwic3ViIjoidTMifQ.s5_JeQN9TaUPfymAXPOpbMAwhmTIg9cnOvNEcj4z75k' +const currentUser = { + id: 'u3', + name: 'Jenny Rostock', + slug: 'jenny-rostock', + email: 'user@example.org', + avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/mutu_krish/128.jpg', + role: 'user' +} const successfulLoginResponse = { data: { login: { - id: 'u3', - name: 'Jenny Rostock', - slug: 'jenny-rostock', - email: 'user@example.org', - avatar: - 'https://s3.amazonaws.com/uifaces/faces/twitter/mutu_krish/128.jpg', - role: 'user', + ...currentUser, token } } } +const successfulCurrentUserResponse = { + data: { + currentUser: { + ...currentUser, + token + } + } +} + const incorrectPasswordResponse = { data: { login: null @@ -39,6 +51,7 @@ const incorrectPasswordResponse = { beforeEach(() => { commit = jest.fn() + dispatch = jest.fn(() => Promise.resolve()) }) describe('getters', () => { @@ -55,16 +68,102 @@ describe('getters', () => { describe('actions', () => { let action + describe('init', () => { + const theAction = () => { + const module = { + app: { + $apolloHelpers: { + getToken: () => token + } + } + } + action = actions.init.bind(module) + return action({ commit, dispatch }) + } + + describe('client-side', () => { + beforeEach(() => { + process.server = false + }) + + it('returns', async () => { + await theAction() + expect(dispatch.mock.calls).toEqual([]) + expect(commit.mock.calls).toEqual([]) + }) + }) + + describe('server-side', () => { + beforeEach(() => { + process.server = true + }) + + it('fetches the current user', async () => { + await theAction() + expect(dispatch.mock.calls).toEqual([['fetchCurrentUser']]) + }) + + it('saves the JWT Bearer token', async () => { + await theAction() + expect(commit.mock.calls).toEqual( + expect.arrayContaining([['SET_TOKEN', token]]) + ) + }) + }) + }) + + describe('fetchCurrentUser', () => { + describe('given a successful response', () => { + beforeEach(async () => { + const module = { + app: { + apolloProvider: { + defaultClient: { + query: jest.fn(() => + Promise.resolve(successfulCurrentUserResponse) + ) + } + } + } + } + action = actions.fetchCurrentUser.bind(module) + await action({ commit }) + }) + + it('saves user data without token', () => { + expect(commit.mock.calls).toEqual( + expect.arrayContaining([ + [ + 'SET_USER', + { + id: 'u3', + name: 'Jenny Rostock', + slug: 'jenny-rostock', + email: 'user@example.org', + avatar: + 'https://s3.amazonaws.com/uifaces/faces/twitter/mutu_krish/128.jpg', + role: 'user' + } + ] + ]) + ) + }) + }) + }) + describe('login', () => { describe('given valid credentials and a successful response', () => { beforeEach(async () => { - const response = Object.assign({}, successfulLoginResponse) - const mutate = jest.fn(() => Promise.resolve(response)) - const onLogin = jest.fn(() => Promise.resolve()) const module = { app: { - apolloProvider: { defaultClient: { mutate } }, - $apolloHelpers: { onLogin } + apolloProvider: { + defaultClient: { + mutate: jest.fn(() => Promise.resolve(successfulLoginResponse)) + } + }, + $apolloHelpers: { + onLogin: jest.fn(() => Promise.resolve()) + } } } action = actions.login.bind(module)