Parcourir la source

:construction: Reversed session/access token roles

tags/0.0.3^2
tomit4 il y a 2 ans
Parent
révision
33fc9497c6

+ 25
- 16
backend/lib/auth/strategies/jwt.js Voir le fichier

@@ -1,5 +1,15 @@
1 1
 'use strict'
2 2
 const JWT = require('jsonwebtoken')
3
+const crypto = require('crypto')
4
+
5
+const hashToken = async token => {
6
+    const salt = process.env.APP_SESSION_SALT
7
+    try {
8
+        return crypto.createHmac('sha256', salt).update(token).digest('hex')
9
+    } catch (err) {
10
+        throw new Error(err.message)
11
+    }
12
+}
3 13
 
4 14
 module.exports = options => {
5 15
     return {
@@ -7,29 +17,28 @@ module.exports = options => {
7 17
         verifyOptions: {
8 18
             algorithms: ['HS256'],
9 19
         },
10
-        // NOTE: TASK 3 Not yet done, but this passes a hashedSessionToken
11
-        // through headers in failed attempt to never have raw JWT's in front end
12
-
13
-        // Always check rawAccessToken, if it fails, we check the session, if session
14
-        // is valid, then we reissue it
15
-        // if session is NOT valid, DELETE the session (and kick user back to login)
20
+        //  TODO: Always check rawAccessToken, if it fails, we check the session, if
21
+        // session is valid, then we reissue it if session is NOT valid, DELETE
22
+        // the session (and kick user back to login)
16 23
         // TODO: set up cron job to occassionaly clean up activeSessions
17
-        validate: (decoded, request, h) => {
18
-            // NOTE: this won't work as it immediately invalidates anything that isn't a raw jwt
19
-            const hashedSessionToken = request.headers.authorization
24
+        validate: async (decoded, request, h) => {
25
+            const accessTokenFromHeaders = request.headers.authorization
26
+            const hashedAccessTokenFromHeaders = await hashToken(
27
+                accessTokenFromHeaders,
28
+            )
29
+            const accessToken =
30
+                request.server.app.activeSessions[hashedAccessTokenFromHeaders]
31
+                    .accessToken
20 32
             const sessionToken =
21
-                request.server.app.activeSessions[hashedSessionToken]
33
+                request.server.app.activeSessions[hashedAccessTokenFromHeaders]
22 34
                     .sessionToken
23
-            console.log('sessionToken :=>', sessionToken)
35
+            console.log('sessionToken from jwt strategy :=>', sessionToken)
24 36
             try {
25 37
                 const validatedJwt = JWT.verify(
26
-                    sessionToken,
38
+                    accessToken,
27 39
                     process.env.APP_SECRET,
28 40
                 )
29
-                return {
30
-                    isValid: true,
31
-                    credentials: validatedJwt.email,
32
-                }
41
+                return { isValid: true, credentials: validatedJwt.email }
33 42
             } catch (err) {
34 43
                 console.error('ERROR :=>', err)
35 44
                 return { isValid: false, error: err.message }

+ 2
- 2
backend/lib/plugins/user.js Voir le fichier

@@ -14,7 +14,7 @@ const UserLoginRoute = require('../routes/user/login')
14 14
 const UserSignupRoute = require('../routes/user/signup')
15 15
 const UserEmailRoute = require('../routes/user/email.js')
16 16
 const UserVerifyActiveRoute = require('../routes/user/verifyactivesession.js')
17
-const UserGetSessionRoute = require('../routes/user/getsession.js')
17
+const UserGetAccessRoute = require('../routes/user/getaccess.js')
18 18
 const UserValidateSessionRoute = require('../routes/user/validatesession.js')
19 19
 const UserByEmail = require('../routes/user/user-by-email.js')
20 20
 const UserPassword = require('../routes/user/authentication')
@@ -56,7 +56,7 @@ module.exports = {
56 56
         await server.route(UserProfilesListRoute)
57 57
         await server.route(UserEmailRoute)
58 58
         await server.route(UserVerifyActiveRoute)
59
-        await server.route(UserGetSessionRoute)
59
+        await server.route(UserGetAccessRoute)
60 60
         await server.route(UserValidateSessionRoute)
61 61
         await server.route(UserByEmail)
62 62
         await server.route(UserPassword)

+ 4
- 4
backend/lib/routes/user/email.js Voir le fichier

@@ -25,7 +25,7 @@ module.exports = {
25 25
             const userCredentials = request.payload
26 26
             try {
27 27
                 const emailSent = await userService.emailSent(userCredentials)
28
-                const hashedSessionToken = Object.keys(
28
+                const hashedAccessToken = Object.keys(
29 29
                     userService.activeSessions,
30 30
                 ).find(hashedToken => {
31 31
                     return (
@@ -35,15 +35,15 @@ module.exports = {
35 35
                 })
36 36
                 // Registers the activeSessions object for use by jwt auth strategy
37 37
                 request.server.app.activeSessions = userService.activeSessions
38
-                if (!hashedSessionToken.length) {
39
-                    throw Error('hashedSessionToken not Found!!')
38
+                if (!hashedAccessToken.length) {
39
+                    throw Error('hashedAccessToken not Found!!')
40 40
                 }
41 41
                 return {
42 42
                     ok: true,
43 43
                     handler: pluginConfig.handlerType,
44 44
                     data: {
45 45
                         emailSentSuccessfully: emailSent.wasSuccessfull,
46
-                        hashedSessionToken: hashedSessionToken,
46
+                        hashedAccessToken: hashedAccessToken,
47 47
                     },
48 48
                 }
49 49
             } catch (err) {

backend/lib/routes/user/getsession.js → backend/lib/routes/user/getaccess.js Voir le fichier

@@ -14,7 +14,7 @@ const pluginConfig = {
14 14
 
15 15
 module.exports = {
16 16
     method: 'POST',
17
-    path: '/getsession',
17
+    path: '/getaccess',
18 18
     options: {
19 19
         ...pluginConfig.docs.get,
20 20
         tags: ['api'],
@@ -26,14 +26,14 @@ module.exports = {
26 26
         handler: async function (request, h) {
27 27
             const { userService } = request.server.services()
28 28
             const res = request.payload
29
-            const token = await userService.createToken(res)
29
+            const accessToken = await userService.createToken(res)
30 30
             try {
31 31
                 const response = h.response({
32 32
                     ok: true,
33 33
                     handler: pluginConfig.handlerType,
34
-                    data: token,
34
+                    data: accessToken,
35 35
                 })
36
-                response.header('Authorization', token)
36
+                response.header('Authorization', accessToken)
37 37
                 return response
38 38
             } catch (err) {
39 39
                 return {

+ 2
- 2
backend/lib/routes/user/validatesession.js Voir le fichier

@@ -25,11 +25,11 @@ module.exports = {
25 25
             exposedHeaders: ['Authorization', 'Access-Control-Expose-Headers'],
26 26
         },
27 27
         handler: async function (request, h) {
28
-            const hashedSessionToken = request.payload
28
+            const hashedAccessToken = request.payload
29 29
             const { userService } = request.server.services()
30 30
             try {
31 31
                 const validatedSessionToken =
32
-                    userService.validateSession(hashedSessionToken)
32
+                    userService.validateSession(hashedAccessToken)
33 33
                 return {
34 34
                     ok: true,
35 35
                     handler: pluginConfig.handlerType,

+ 16
- 19
backend/lib/services/user.js Voir le fichier

@@ -20,7 +20,6 @@ const hashToken = async token => {
20 20
     try {
21 21
         return crypto.createHmac('sha256', salt).update(token).digest('hex')
22 22
     } catch (err) {
23
-        // console.error('ERROR :=>', err)
24 23
         throw new Error(err.message)
25 24
     }
26 25
 }
@@ -278,12 +277,12 @@ module.exports = class UserService extends Schmervice.Service {
278 277
      * @ params {UserSession} {ValidatedTokens}
279 278
      * @returns Void
280 279
      */
281
-    _createAccessTokenIfExpired(userSession, validatedTokens) {
282
-        if (!validatedTokens.accessTokenIsValid.payload) {
283
-            const accessToken = this.createToken({
284
-                payload: validatedTokens.sessionTokenIsValid.payload,
280
+    _createSessionTokenIfExpired(userSession, validatedTokens) {
281
+        if (!validatedTokens.sessionTokenIsValid.payload) {
282
+            const sessionToken = this.createToken({
283
+                payload: validatedTokens.accessTokenIsValid.payload,
285 284
             })
286
-            userSession.accessToken = accessToken
285
+            userSession.sessionToken = sessionToken
287 286
         }
288 287
     }
289 288
     /**
@@ -292,8 +291,8 @@ module.exports = class UserService extends Schmervice.Service {
292 291
      * @param {HashedSessionToken} hashedSessionToken
293 292
      * @returns {PayloadFromActiveSessions}
294 293
      */
295
-    validateSession(hashedSessionToken) {
296
-        const userSession = this.activeSessions[hashedSessionToken]
294
+    validateSession(hashedAccessToken) {
295
+        const userSession = this.activeSessions[hashedAccessToken]
297 296
         if (!userSession) {
298 297
             throw new Error(
299 298
                 'hashedSessionToken not in activeSessions registry!',
@@ -301,12 +300,10 @@ module.exports = class UserService extends Schmervice.Service {
301 300
         }
302 301
         const tokens = this._grabTokensFromActiveSessions(userSession)
303 302
         const validatedTokens = this._validateTokens(tokens)
304
-        this._createAccessTokenIfExpired(userSession, validatedTokens)
303
+        this._createSessionTokenIfExpired(userSession, validatedTokens)
305 304
         return {
306
-            ...validatedTokens.sessionTokenIsValid.payload,
307
-            // sessionToken: this.activeSessions[hashedSessionToken].sessionToken,
308
-            // NOTE: this won't work as the jwt auth strategy needs a raw JWT string
309
-            sessionToken: hashedSessionToken,
305
+            ...validatedTokens.accessTokenIsValid.payload,
306
+            accessToken: this.activeSessions[hashedAccessToken].accessToken,
310 307
         }
311 308
     }
312 309
     /**
@@ -348,20 +345,20 @@ module.exports = class UserService extends Schmervice.Service {
348 345
      * @ returns {Object}
349 346
      */
350 347
     async emailSent(userCredentials) {
351
-        const hashedSessionToken = await hashToken(userCredentials.sessionToken)
352
-        if (Object.keys(this.activeSessions).includes(hashedSessionToken)) {
348
+        const hashedAccessToken = await hashToken(userCredentials.accessToken)
349
+        if (Object.keys(this.activeSessions).includes(hashedAccessToken)) {
353 350
             return new Error('session already in cache!!')
354 351
         }
355 352
         // Set expiration time for ten minutes from now
356 353
         const duration = 600000
357 354
 
358
-        this.activeSessions[hashedSessionToken] = {
355
+        this.activeSessions[hashedAccessToken] = {
359 356
             email: userCredentials.email,
360 357
             name: userCredentials.name,
361 358
             seeking: userCredentials.seeking,
362
-            sessionToken: userCredentials.sessionToken,
359
+            accessToken: userCredentials.accessToken,
363 360
             expiration: Date.now() + duration,
364
-            accessToken: null,
361
+            sessionToken: null,
365 362
         }
366 363
 
367 364
         const sendSmtpEmail = {
@@ -373,7 +370,7 @@ module.exports = class UserService extends Schmervice.Service {
373 370
             templateId: 1,
374 371
             params: {
375 372
                 // TODO: Change this in production...
376
-                link: `localhost:3000/verify/${hashedSessionToken}`,
373
+                link: `localhost:3000/verify/${hashedAccessToken}`,
377 374
             },
378 375
         }
379 376
 

+ 6
- 5
frontend/src/components/onboarding/Auth.vue Voir le fichier

@@ -48,14 +48,15 @@ export default {
48 48
                 password: userPass.val,
49 49
             })
50 50
             await this.createProfileForNewUser(newUserId, this.responses)
51
-            const sessionToken = await this.getSessionToken({
51
+            const accessToken = await this.getAccessToken({
52 52
                 ...this.answered,
53 53
             })
54
+            console.log('accessToken :=>', accessToken)
54 55
             const sessionInfo = await this.authenticator.sendAuthEmail({
55 56
                 ...this.answered,
56
-                sessionToken: sessionToken,
57
+                accessToken: accessToken,
57 58
             })
58
-            document.cookie = `siimee_session=${sessionInfo.hashedSessionToken}; max-age=600; path=/; secure`
59
+            document.cookie = `siimee_access=${sessionInfo.hashedAccessToken}; max-age=600; path=/; secure`
59 60
         } catch (err) {
60 61
             // TODO: render an error page in this component displaying which
61 62
             // error occurred and how to reach out to staff
@@ -69,8 +70,8 @@ export default {
69 70
                     'User has not answered minimum amount of questions to create profile',
70 71
                 )
71 72
         },
72
-        async getSessionToken(payload) {
73
-            return await this.authenticator.getSessionToken({
73
+        async getAccessToken(payload) {
74
+            return await this.authenticator.getAccessToken({
74 75
                 payload,
75 76
             })
76 77
         },

+ 6
- 6
frontend/src/services/auth.service.js Voir le fichier

@@ -7,14 +7,14 @@ class Authenticator {
7 7
     async sendAuthEmail(answered) {
8 8
         return await db.post('/user/sendemail/', answered)
9 9
     }
10
-    async verifyAuthSession(hashedSessionToken) {
11
-        return await db.get(`/user/verify/${hashedSessionToken}`)
10
+    async verifyAuthSession(hashedToken) {
11
+        return await db.get(`/user/verify/${hashedToken}`)
12 12
     }
13
-    async getSessionToken(req) {
14
-        return await db.post('/user/getsession', req, true)
13
+    async getAccessToken(req) {
14
+        return await db.post('/user/getaccess', req, true)
15 15
     }
16
-    async validateSession(hashedSessionToken) {
17
-        return await db.post('/user/validatesession', hashedSessionToken, true)
16
+    async validateSession(hashedAccessToken) {
17
+        return await db.post('/user/validatesession', hashedAccessToken, true)
18 18
     }
19 19
 }
20 20
 

+ 19
- 18
frontend/src/views/OnboardingView.vue Voir le fichier

@@ -43,7 +43,7 @@ import {
43 43
 import { surveyFactory } from '@/utils'
44 44
 import stepViews from '@/components/onboarding'
45 45
 import SurveyCompleteView from './SurveyCompleteView.vue'
46
-let hashedSessionToken = null
46
+let hashedAccessToken = null
47 47
 let currentProfileId = null
48 48
 
49 49
 export default {
@@ -64,22 +64,23 @@ export default {
64 64
     async created() {
65 65
         this.survey = await surveyFactory.createSurvey()
66 66
         this.authenticator = new Authenticator()
67
-        hashedSessionToken = this.grabStoredCookie('siimee_session')
67
+        hashedAccessToken = this.grabStoredCookie('siimee_access')
68 68
         try {
69
-            const sessionData = await this.verifySession(hashedSessionToken)
69
+            const sessionData = await this.verifySession(hashedAccessToken)
70
+            console.log('sessionData :=>', sessionData)
70 71
             // TODO: Move this logic onto the backend and have it
71 72
             // returned in verifySession(hashedSessionToken)
72 73
             const userId = await this.grabUserIdByEmail(
73 74
                 sessionData.email,
74
-                sessionData.sessionToken,
75
+                sessionData.accessToken,
75 76
             )
76 77
             currentProfileId = await this.grabProfileIdByUserId(
77 78
                 userId,
78
-                sessionData.sessionToken,
79
+                sessionData.accessToken,
79 80
             )
80 81
             this.responses = await this.grabResponsesByProfileId(
81 82
                 currentProfileId,
82
-                sessionData.sessionToken,
83
+                sessionData.accessToken,
83 84
             )
84 85
             this.currentStep = this.responses.length + 3
85 86
             this.goToStep(this.currentStep)
@@ -109,11 +110,11 @@ export default {
109 110
         },
110 111
         // TODO: sessionToken is flying around far too often,
111 112
         // see above TODO regarding moving most of this logic to the backend
112
-        async verifySession(hashedSessionToken) {
113
-            if (!hashedSessionToken)
113
+        async verifySession(hashedAccessToken) {
114
+            if (!hashedAccessToken)
114 115
                 return console.warn('WARNING :=> sessionToken is not defined')
115 116
             const validatedToken = await this.authenticator.validateSession(
116
-                hashedSessionToken,
117
+                hashedAccessToken,
117 118
             )
118 119
             if (validatedToken.error) {
119 120
                 throw new Error(validatedToken.error)
@@ -121,16 +122,16 @@ export default {
121 122
                 return validatedToken
122 123
             }
123 124
         },
124
-        async grabUserIdByEmail(email, sessionToken) {
125
-            const user = await fetchUserByEmail(email, sessionToken)
125
+        async grabUserIdByEmail(email, accessToken) {
126
+            const user = await fetchUserByEmail(email, accessToken)
126 127
             if (!user) {
127 128
                 throw new Error('User NOT found by email')
128 129
             } else return user.user_id
129 130
         },
130
-        async grabProfileIdByUserId(userId, sessionToken) {
131
+        async grabProfileIdByUserId(userId, accessToken) {
131 132
             const profilesFromUserId = await fetchProfilesByUserId(
132 133
                 userId,
133
-                sessionToken,
134
+                accessToken,
134 135
             )
135 136
             if (
136 137
                 profilesFromUserId.length === 1 &&
@@ -144,10 +145,10 @@ export default {
144 145
                 throw new Error('No Profile for User ID found')
145 146
             }
146 147
         },
147
-        async grabProfileByProfileId(profileId, sessionToken) {
148
+        async grabProfileByProfileId(profileId, accessToken) {
148 149
             const profile = await fetchProfileByProfileId(
149 150
                 profileId,
150
-                sessionToken,
151
+                accessToken,
151 152
             )
152 153
             if (!profile || profile.status === 401) {
153 154
                 throw new Error(`No Profile Found for profileId ${profileId}`)
@@ -155,11 +156,11 @@ export default {
155 156
                 return profile
156 157
             }
157 158
         },
158
-        async grabResponsesByProfileId(profileId, sessionToken) {
159
+        async grabResponsesByProfileId(profileId, accessToken) {
159 160
             const responses = []
160 161
             const profile = await this.grabProfileByProfileId(
161 162
                 profileId,
162
-                sessionToken,
163
+                accessToken,
163 164
             )
164 165
             if (!profile.responses.length || profile.responses.status === 401) {
165 166
                 throw new Error(`No Responses Found for profileId ${profileId}`)
@@ -199,7 +200,7 @@ export default {
199 200
                     currentProfileId,
200 201
                 )
201 202
                 try {
202
-                    await this.verifySession(hashedSessionToken)
203
+                    await this.verifySession(hashedAccessToken)
203 204
                 } catch (err) {
204 205
                     this.currentStep = 0
205 206
                     this.goToStep(this.currentStep)

+ 6
- 6
frontend/src/views/VerifyView.vue Voir le fichier

@@ -7,7 +7,7 @@
7 7
 <script>
8 8
 import { Authenticator } from '../services/auth.service.js'
9 9
 let hash = null
10
-let hashedSessionToken = null
10
+let hashedAccessToken = null
11 11
 export default {
12 12
     name: 'VerifyView',
13 13
     data: () => ({
@@ -16,10 +16,10 @@ export default {
16 16
     async created() {
17 17
         this.authenticator = new Authenticator()
18 18
         hash = this.$route.params.hashedToken
19
-        hashedSessionToken = this.grabCookie('siimee_session')
19
+        hashedAccessToken = this.grabCookie('siimee_access')
20 20
         try {
21 21
             this.isHashInUrl(hash)
22
-            await this.doesSessionTokenExist(hashedSessionToken)
22
+            await this.doesAccessTokenExist(hashedAccessToken)
23 23
             await this.verifyActiveSession(hash)
24 24
             await this.isSessionTokenValid(hash)
25 25
         } catch (err) {
@@ -43,9 +43,9 @@ export default {
43 43
         isHashInUrl(hash) {
44 44
             if (!hash) throw new Error('URL contains no hash!')
45 45
         },
46
-        async doesSessionTokenExist(hashedSessionToken) {
47
-            if (!hashedSessionToken)
48
-                throw new Error('sessionToken not in cookie store!')
46
+        async doesAccessTokenExist(hashedAccessToken) {
47
+            if (!hashedAccessToken)
48
+                throw new Error('accessToken not in cookie store!')
49 49
         },
50 50
         async verifyActiveSession(hashedToken) {
51 51
             const sessionData = await this.authenticator.verifyAuthSession(

Chargement…
Annuler
Enregistrer