From 2732ade1780e2c7a2cc6ba543948dc79a7935a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Babi=C4=8D?= Date: Mon, 14 Oct 2019 18:41:44 +0200 Subject: [PATCH] prepare for refresh tokens feature --- package-lock.json | 204 +++++++++++++++----- package.json | 3 + src/server.spec.ts | 97 ++++++++-- src/server.ts | 3 + src/server/UserResolver.spec.ts | 38 ++-- src/server/UserResolver.ts | 3 +- src/server/testing.ts | 14 +- src/server/userResolver/ContextInterface.ts | 8 + src/server/userResolver/auth.ts | 28 +-- 9 files changed, 305 insertions(+), 93 deletions(-) create mode 100644 src/server/userResolver/ContextInterface.ts diff --git a/package-lock.json b/package-lock.json index 198dfbb..4f17068 100644 --- a/package-lock.json +++ b/package-lock.json @@ -707,6 +707,15 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==" }, + "@types/node-fetch": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.2.tgz", + "integrity": "sha512-djYYKmdNRSBtL1x4CiE9UJb9yZhwtI1VC+UxZD0psNznrUj80ywsxKlEGAE+QL1qvLjPbfb24VosjkYM6W4RSQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/range-parser": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", @@ -772,6 +781,22 @@ "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", "dev": true }, + "@types/zen-observable": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz", + "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==", + "dev": true + }, + "@wry/context": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.4.4.tgz", + "integrity": "sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag==", + "dev": true, + "requires": { + "@types/node": ">=6", + "tslib": "^1.9.3" + } + }, "@wry/equality": { "version": "0.1.9", "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.1.9.tgz", @@ -871,6 +896,33 @@ "normalize-path": "^2.1.1" } }, + "apollo-boost": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/apollo-boost/-/apollo-boost-0.4.4.tgz", + "integrity": "sha512-ASngBvazmp9xNxXfJ2InAzfDwz65o4lswlEPrWoN35scXmCz8Nz4k3CboUXbrcN/G0IExkRf/W7o9Rg0cjEBqg==", + "dev": true, + "requires": { + "apollo-cache": "^1.3.2", + "apollo-cache-inmemory": "^1.6.3", + "apollo-client": "^2.6.4", + "apollo-link": "^1.0.6", + "apollo-link-error": "^1.0.3", + "apollo-link-http": "^1.3.1", + "graphql-tag": "^2.4.2", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, + "apollo-cache": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.2.tgz", + "integrity": "sha512-+KA685AV5ETEJfjZuviRTEImGA11uNBp/MJGnaCvkgr+BYRrGLruVKBv6WvyFod27WEB2sp7SsG8cNBKANhGLg==", + "dev": true, + "requires": { + "apollo-utilities": "^1.3.2", + "tslib": "^1.9.3" + } + }, "apollo-cache-control": { "version": "0.8.4", "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.8.4.tgz", @@ -880,6 +932,35 @@ "graphql-extensions": "^0.10.3" } }, + "apollo-cache-inmemory": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.3.tgz", + "integrity": "sha512-S4B/zQNSuYc0M/1Wq8dJDTIO9yRgU0ZwDGnmlqxGGmFombOZb9mLjylewSfQKmjNpciZ7iUIBbJ0mHlPJTzdXg==", + "dev": true, + "requires": { + "apollo-cache": "^1.3.2", + "apollo-utilities": "^1.3.2", + "optimism": "^0.10.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, + "apollo-client": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.4.tgz", + "integrity": "sha512-oWOwEOxQ9neHHVZrQhHDbI6bIibp9SHgxaLRVPoGvOFy7OH5XUykZE7hBQAVxq99tQjBzgytaZffQkeWo1B4VQ==", + "dev": true, + "requires": { + "@types/zen-observable": "^0.8.0", + "apollo-cache": "1.3.2", + "apollo-link": "^1.0.0", + "apollo-utilities": "1.3.2", + "symbol-observable": "^1.0.2", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + } + }, "apollo-datasource": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/apollo-datasource/-/apollo-datasource-0.6.3.tgz", @@ -941,6 +1022,39 @@ "zen-observable-ts": "^0.8.20" } }, + "apollo-link-error": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.1.12.tgz", + "integrity": "sha512-psNmHyuy3valGikt/XHJfe0pKJnRX19tLLs6P6EHRxg+6q6JMXNVLYPaQBkL0FkwdTCB0cbFJAGRYCBviG8TDA==", + "dev": true, + "requires": { + "apollo-link": "^1.2.13", + "apollo-link-http-common": "^0.2.15", + "tslib": "^1.9.3" + } + }, + "apollo-link-http": { + "version": "1.5.16", + "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.16.tgz", + "integrity": "sha512-IA3xA/OcrOzINRZEECI6IdhRp/Twom5X5L9jMehfzEo2AXdeRwAMlH5LuvTZHgKD8V1MBnXdM6YXawXkTDSmJw==", + "dev": true, + "requires": { + "apollo-link": "^1.2.13", + "apollo-link-http-common": "^0.2.15", + "tslib": "^1.9.3" + } + }, + "apollo-link-http-common": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.15.tgz", + "integrity": "sha512-+Heey4S2IPsPyTf8Ag3PugUupASJMW894iVps6hXbvwtg1aHSNMXUYO5VG7iRHkPzqpuzT4HMBanCTXPjtGzxg==", + "dev": true, + "requires": { + "apollo-link": "^1.2.13", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, "apollo-server": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/apollo-server/-/apollo-server-2.9.3.tgz", @@ -1362,6 +1476,24 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + } + } + }, "babel-jest": { "version": "24.9.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", @@ -1713,12 +1845,6 @@ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, - "class-transformer": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.2.3.tgz", - "integrity": "sha512-qsP+0xoavpOlJHuYsQJsN58HXSl8Jvveo+T37rEvCEeRfMWoytAyR0Ua/YsFgpM6AZYZ/og2PJwArwzJl1aXtQ==", - "dev": true - }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -2270,15 +2396,6 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "encoding": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", - "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", - "dev": true, - "requires": { - "iconv-lite": "~0.4.13" - } - }, "end-of-stream": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", @@ -2677,6 +2794,26 @@ "locate-path": "^3.0.0" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3836,28 +3973,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "dev": true, - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - }, - "dependencies": { - "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "dev": true, - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - } - } - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -5241,6 +5356,15 @@ "wrappy": "1" } }, + "optimism": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.10.3.tgz", + "integrity": "sha512-9A5pqGoQk49H6Vhjb9kPgAeeECfUDF6aIICbMDL23kDLStBn1MWk3YvcZ4xWF9CsSf6XEgvRLkXy4xof/56vVw==", + "dev": true, + "requires": { + "@wry/context": "^0.4.0" + } + }, "optimist": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", @@ -7058,12 +7182,6 @@ "iconv-lite": "0.4.24" } }, - "whatwg-fetch": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", - "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==", - "dev": true - }, "whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", diff --git a/package.json b/package.json index ba04e2a..d4d877b 100644 --- a/package.json +++ b/package.json @@ -31,8 +31,11 @@ "@types/jest": "^24.0.18", "@types/jsonwebtoken": "^8.3.3", "@types/node": "^12.7.5", + "@types/node-fetch": "^2.5.2", + "apollo-boost": "^0.4.4", "apollo-server-testing": "^2.9.6", "jest": "^24.9.0", + "node-fetch": "^2.6.0", "ts-jest": "^24.1.0", "ts-node-dev": "^1.0.0-pre.42", "typeorm-transactional-cls-hooked": "^0.1.8", diff --git a/src/server.spec.ts b/src/server.spec.ts index e4bcc15..15afc53 100644 --- a/src/server.spec.ts +++ b/src/server.spec.ts @@ -1,26 +1,85 @@ -import { ApolloServer } from "apollo-server-express" -import { createTestClient } from "apollo-server-testing" -import { createConnection, getConnection } from "typeorm" +import ApolloClient, { gql } from "apollo-boost" +import fetch from "node-fetch" +import { createConnection } from "typeorm" import { createServer } from "./server" -import { testingConnectionOptions } from "./server/testing" -import auth = require("./server/userResolver/auth") +import { + initializeRollbackTransactions, + runInRollbackTransaction, + testingConnectionOptions, +} from "./server/testing" -describe("app should", () => { - it("call the context function on apollo server", async () => { - const spy = jest.spyOn(auth, "contextFunction") - await createConnection(testingConnectionOptions()) +const port = 4001 - const port = 4001 - const server = (await createServer(port)) as any - const { query } = createTestClient(server) - await query({ query: "{me{email}}" }) +beforeAll(async () => { + initializeRollbackTransactions() + await createConnection(testingConnectionOptions()) + await createServer(port) +}) + +describe("server should", () => { + it( + "handle auth user me request", + runInRollbackTransaction(async () => { + const uri = `http://localhost:${port}/graphql` + let client = new ApolloClient({ uri, fetch }) + + const createUserMutation = gql` + mutation { + createUser(email: "email@email.com", password: "password") { + email + } + } + ` + await client.mutate({ mutation: createUserMutation }) + + const loginTokensQuery = gql` + query { + loginTokens(email: "email@email.com", password: "password") { + accessToken + } + } + ` + + const tokens = await client.query({ query: loginTokensQuery }) + const accessToken = tokens.data.loginTokens.accessToken + + client = new ApolloClient({ + uri, + fetch, + request: operation => { + operation.setContext({ + headers: { + authorization: "Bearer " + accessToken, + }, + }) + }, + }) + + const meQuery = gql` + query { + me { + email + } + } + ` + + const meResponse = await client.query({ query: meQuery }) + const meEmail = meResponse.data.me.email + + expect(meEmail).toBe("email@email.com") + }) + ) - expect(server).toBeInstanceOf(ApolloServer) - expect(spy).toHaveBeenCalledTimes(1) + it( + "receive no refresh token without auth header", + runInRollbackTransaction(async () => { + const uri = `http://localhost:${port}/refresh_token` - spy.mockRestore() + const response = await fetch(uri, { method: "POST" }) + const jsonResponse = await response.json() - await server.stop() - await getConnection().close() - }) + expect(jsonResponse.data).toBeNull() + expect(jsonResponse.errors).not.toBeUndefined() + }) + ) }) diff --git a/src/server.ts b/src/server.ts index a01c4e2..2dcf6c9 100644 --- a/src/server.ts +++ b/src/server.ts @@ -13,6 +13,9 @@ export const createServer = async (port: number) => { }) const app = express() + app.post("/refresh_token", (_req, res) => + res.send({ data: null, errors: "Invalid access token" }) + ) server.applyMiddleware({ app }) app.listen({ port }) diff --git a/src/server/UserResolver.spec.ts b/src/server/UserResolver.spec.ts index e551c5f..a7f8725 100644 --- a/src/server/UserResolver.spec.ts +++ b/src/server/UserResolver.spec.ts @@ -1,20 +1,19 @@ import { gql } from "apollo-server" import { Request, Response } from "express" import { createConnection, getConnection } from "typeorm" -import { - initializeTransactionalContext, - patchTypeORMRepositoryWithBaseRepository, -} from "typeorm-transactional-cls-hooked" import { callSchema } from "./schema" -import { runInTransaction, testingConnectionOptions } from "./testing" -import { ContextInterface, signAccessToken, verifyAccessToken } from "./userResolver/auth" +import { + initializeRollbackTransactions, + runInRollbackTransaction, + testingConnectionOptions, +} from "./testing" +import { signAccessToken, verifyAccessToken } from "./userResolver/auth" +import { ContextInterface } from "./userResolver/ContextInterface" import { LoginTokens } from "./userResolver/LoginTokens" import { User } from "./userResolver/User" beforeAll(async () => { - initializeTransactionalContext() - patchTypeORMRepositoryWithBaseRepository() - + initializeRollbackTransactions() await createConnection(testingConnectionOptions()) }) @@ -26,7 +25,7 @@ describe("resolver of user", () => { describe("createUser mutation should", () => { it( "return email as it creates user with mutation", - runInTransaction(async () => { + runInRollbackTransaction(async () => { const createUserMutation = gql` mutation { createUser(email: "email@email.com", password: "password") { @@ -48,7 +47,7 @@ describe("resolver of user", () => { describe("users query should", () => { it( "return emails of registered users", - runInTransaction(async () => { + runInRollbackTransaction(async () => { const usersQuery = gql` query { users { @@ -82,7 +81,7 @@ describe("resolver of user", () => { it( "return error for non-existent user", - runInTransaction(async () => { + runInRollbackTransaction(async () => { const response = await callSchema(loginTokensQuery) expect(response.errors).not.toBeUndefined() @@ -92,7 +91,7 @@ describe("resolver of user", () => { it( "return error for bad password", - runInTransaction(async () => { + runInRollbackTransaction(async () => { await User.create({ email: "email@email.com", password: "BAD-password", @@ -107,7 +106,7 @@ describe("resolver of user", () => { it( "return a valid access token with good credentials", - runInTransaction(async () => { + runInRollbackTransaction(async () => { await User.create({ email: "email@email.com", password: "good-password", @@ -115,12 +114,17 @@ describe("resolver of user", () => { const response = await callSchema(loginTokensQuery) const accessToken = response.data!.loginTokens.accessToken + const accessTokenPayload = verifyAccessToken(accessToken) const loginTokens = new LoginTokens() loginTokens.accessToken = accessToken + const fifteenMinutes = 900 + const accessTokenLifetime = + accessTokenPayload.exp! - accessTokenPayload.iat! + expect(accessTokenLifetime).toBe(fifteenMinutes) + expect(accessTokenPayload).toBeTruthy() expect(response.errors).toBeUndefined() expect(response.data).toMatchObject({ loginTokens }) - expect(verifyAccessToken(accessToken)).toBeTruthy() }) ) }) @@ -136,7 +140,7 @@ describe("resolver of user", () => { it( "return an error without a valid jwt token", - runInTransaction(async () => { + runInRollbackTransaction(async () => { const contextWithInvalidToken = contextWithAuthHeader( "Bearer INVALID-TOKEN" ) @@ -149,7 +153,7 @@ describe("resolver of user", () => { it( "return an user with a valid jwt token", - runInTransaction(async () => { + runInRollbackTransaction(async () => { const user = await User.create({ email: "email@email.com", }).save() diff --git a/src/server/UserResolver.ts b/src/server/UserResolver.ts index 3a4c367..aa13a7a 100644 --- a/src/server/UserResolver.ts +++ b/src/server/UserResolver.ts @@ -1,6 +1,7 @@ import "reflect-metadata" import { Arg, Authorized, Ctx, Mutation, Query } from "type-graphql" -import { comparePassword, ContextInterface, signAccessToken } from "./userResolver/auth" +import { comparePassword, signAccessToken } from "./userResolver/auth" +import { ContextInterface } from "./userResolver/ContextInterface" import { LoginTokens } from "./userResolver/LoginTokens" import { User } from "./userResolver/User" diff --git a/src/server/testing.ts b/src/server/testing.ts index 5a06444..f7f7b57 100644 --- a/src/server/testing.ts +++ b/src/server/testing.ts @@ -1,5 +1,10 @@ import { ConnectionOptions } from "typeorm" -import { Propagation, Transactional } from "typeorm-transactional-cls-hooked" +import { + initializeTransactionalContext, + patchTypeORMRepositoryWithBaseRepository, + Propagation, + Transactional, +} from "typeorm-transactional-cls-hooked" import { connectionOptions } from "./connection" export const testingConnectionOptions = () => { @@ -8,6 +13,11 @@ export const testingConnectionOptions = () => { return { ...connectionOptions(), database } as ConnectionOptions } +export const initializeRollbackTransactions = () => { + initializeTransactionalContext() + patchTypeORMRepositoryWithBaseRepository() +} + type RunFunction = () => Promise | void class RollbackError extends Error { @@ -26,7 +36,7 @@ class TransactionCreator { } } -export function runInTransaction(func: RunFunction) { +export function runInRollbackTransaction(func: RunFunction) { return async () => { try { await TransactionCreator.run(func) diff --git a/src/server/userResolver/ContextInterface.ts b/src/server/userResolver/ContextInterface.ts new file mode 100644 index 0000000..d8c823b --- /dev/null +++ b/src/server/userResolver/ContextInterface.ts @@ -0,0 +1,8 @@ +import { Request, Response } from "express" +import { ContextPayload } from "./auth" + +export interface ContextInterface { + req: Request + res: Response + payload?: ContextPayload +} diff --git a/src/server/userResolver/auth.ts b/src/server/userResolver/auth.ts index 4a7ecaf..39c7755 100644 --- a/src/server/userResolver/auth.ts +++ b/src/server/userResolver/auth.ts @@ -1,16 +1,16 @@ import { argon2id, hash, verify as argonVerify } from "argon2" -import { Request, Response } from "express" import { sign, verify as jwtVerify } from "jsonwebtoken" import { AuthChecker } from "type-graphql" +import { ContextInterface } from "./ContextInterface" -export type Payload = { +export type ContextPayload = { userId: number } -export interface ContextInterface { - req: Request - res: Response - payload?: Payload +type AccessTokenPayload = { + userId: number + iat: number + exp?: number } export const hashPassword = async (password: string) => @@ -19,20 +19,26 @@ export const hashPassword = async (password: string) => export const comparePassword = async (hash: string, plain: string) => await argonVerify(hash, plain, { type: argon2id }) -export const signAccessToken = (payload: Payload) => { - return sign(payload, process.env.ACCESS_SECRET!) +export const signAccessToken = (payload: ContextPayload) => { + const accessTokenSecret = process.env.ACCESS_SECRET as string + + return sign(payload, accessTokenSecret, { + expiresIn: process.env.ACCESS_EXP, + }) } export const verifyAccessToken = (token: string) => { - return jwtVerify(token, process.env.ACCESS_SECRET!) + const accessTokenSecret = process.env.ACCESS_SECRET as string + + return jwtVerify(token, accessTokenSecret) as AccessTokenPayload } export const customAuthChecker: AuthChecker = ({ context }) => { try { const authHeader = context.req.headers["authorization"] const accessToken = authHeader!.split(" ")[1] - const payload = verifyAccessToken(accessToken) - context.payload = payload as any + const accessTokenPayload = verifyAccessToken(accessToken) + context.payload = accessTokenPayload as ContextPayload return true } catch (error) {