diff --git a/src/server.spec.ts b/src/server.spec.ts index fa7c4f8..9e3c160 100644 --- a/src/server.spec.ts +++ b/src/server.spec.ts @@ -10,66 +10,72 @@ import { User } from "./server/userResolver/User" import cookie = require("cookie") describe("server should", () => { - it("perform the refresh tokens operation flawlessly", async () => { - const halfADay = (60 * 60 * 24) / 2 - const fifteenDays = 60 * 60 * 24 * 15 + it("perform the refresh tokens operation flawlessly", async () => { + const halfADay = (60 * 60 * 24) / 2 + const fifteenDays = 60 * 60 * 24 * 15 - const createUserResponse = await rawRequest(gqlUri, gqlToStr(createUserMutation)) - const userId = createUserResponse.data.createUser.id + const createUserResponse = await rawRequest(gqlUri, gqlToStr(createUserMutation)) + const userId = createUserResponse.data.createUser.id - const accessTokenReponse = await rawRequest(gqlUri, gqlToStr(accessTokenQuery)) - const accessToken: string = accessTokenReponse.data.accessToken - const headers: Headers = accessTokenReponse.headers - const cookieHeader = headers.get("set-cookie") as string - const parsedCookie = cookie.parse(cookieHeader) - const refreshCookieExpires = dateInKiloSeconds(parsedCookie.Expires) - const refreshTokenPayload = verifiedRefreshTokenPayload(parsedCookie.rt) - const jwtLifetime = refreshTokenPayload.exp! - refreshTokenPayload.iat! - const refLifetime = dateInKiloSeconds(new Date().getTime() + jwtLifetime * 1000) + const accessTokenReponse = await rawRequest(gqlUri, gqlToStr(accessTokenQuery)) + const accessToken: string = accessTokenReponse.data.accessToken + const headers: Headers = accessTokenReponse.headers + const cookieHeader = headers.get("set-cookie") as string + const varyHeader = headers.get("vary") as string + const acacHeader = headers.get("access-control-allow-credentials") as string + const acaoHeader = headers.get("access-control-allow-origin") as string + const parsedCookie = cookie.parse(cookieHeader) + const refreshCookieExpires = dateInKiloSeconds(parsedCookie.Expires) + const refreshTokenPayload = verifiedRefreshTokenPayload(parsedCookie.rt) + const jwtLifetime = refreshTokenPayload.exp! - refreshTokenPayload.iat! + const refLifetime = dateInKiloSeconds(new Date().getTime() + jwtLifetime * 1000) - const client = new GraphQLClient(gqlUri, { - headers: { - Authorization: "Bearer " + accessToken, - }, - }) - const meResponse = await client.rawRequest(gqlToStr(meQuery)) + const client = new GraphQLClient(gqlUri, { + headers: { + Authorization: "Bearer " + accessToken, + }, + }) + const meResponse = await client.rawRequest(gqlToStr(meQuery)) - const refreshTokenResponse = await fetch(refreshTokenUri, { - method: "POST", - headers: { cookie: cookieHeader }, - }) - const jsonResponse = await refreshTokenResponse.json() + const refreshTokenResponse = await fetch(refreshTokenUri, { + method: "POST", + headers: { cookie: cookieHeader }, + }) + const jsonResponse = await refreshTokenResponse.json() - expect(cookieHeader).toMatch(/HttpOnly/) - expect(parsedCookie.Path).toBe("/refresh_token") - expect(refreshTokenPayload.userId).toBe(userId) - expect(refreshCookieExpires).toBeCloseTo(refLifetime) - expect(jwtLifetime).toBeGreaterThanOrEqual(halfADay) - expect(jwtLifetime).not.toBeGreaterThan(fifteenDays) - expect(meResponse.data.me.email).toBe("auth@server.com") - expect(jsonResponse.data).toBeDefined() - expect(jsonResponse.errors).toBeUndefined() - }) + expect(varyHeader).toBe("Origin") + expect(acacHeader).toBe("true") + expect(acaoHeader).toMatch(/http:/) + expect(cookieHeader).toMatch(/HttpOnly/) + expect(parsedCookie.Path).toBe("/refresh_token") + expect(refreshTokenPayload.userId).toBe(userId) + expect(refreshCookieExpires).toBeCloseTo(refLifetime) + expect(jwtLifetime).toBeGreaterThanOrEqual(halfADay) + expect(jwtLifetime).not.toBeGreaterThan(fifteenDays) + expect(meResponse.data.me.email).toBe("auth@server.com") + expect(jsonResponse.data).toBeDefined() + expect(jsonResponse.errors).toBeUndefined() + }) - it("it doesnt perform refresh tokens without valid cookie", async () => { - const response = await fetch(refreshTokenUri, { - method: "POST", - headers: { cookie: "INVALID-COOKIE" }, - }) - const jsonResponse = await response.json() + it("it doesnt perform refresh tokens without valid cookie", async () => { + const response = await fetch(refreshTokenUri, { + method: "POST", + headers: { cookie: "INVALID-COOKIE" }, + }) + const jsonResponse = await response.json() - expect(jsonResponse.data).toBeNull() - expect(jsonResponse.errors).not.toBeUndefined() - }) + expect(jsonResponse.data).toBeNull() + expect(jsonResponse.errors).not.toBeUndefined() + }) }) beforeAll(async () => { - await createConnection(testingConnectionOptions()) - await createServer(port) + await createConnection(testingConnectionOptions()) + await createServer(port) }) afterAll(async () => { - User.delete({ email: "auth@server.com" }) + User.delete({ email: "auth@server.com" }) }) const port = 4001 @@ -77,26 +83,26 @@ const gqlUri = `http://localhost:${port}/graphql` const refreshTokenUri = `http://localhost:${port}/refresh_token` const createUserMutation = gql` - mutation { - createUser(email: "auth@server.com", password: "password") { - email - id - } - } + mutation { + createUser(email: "auth@server.com", password: "password") { + email + id + } + } ` const accessTokenQuery = gql` - query { - accessToken(email: "auth@server.com", password: "password") - } + query { + accessToken(email: "auth@server.com", password: "password") + } ` const meQuery = gql` - query { - me { - email - } - } + query { + me { + email + } + } ` const dateInKiloSeconds = (date: string | number) => new Date(date).getTime() / 1000000 diff --git a/src/server.ts b/src/server.ts index 9e3e3d7..83c1c36 100644 --- a/src/server.ts +++ b/src/server.ts @@ -2,34 +2,44 @@ import express = require("express") import { ApolloServer } from "apollo-server-express" import { createSchema } from "./server/schema" import { - accessTokenWithRefreshCookie, - contextFunction, - verifiedRefreshTokenPayload, + accessTokenWithRefreshCookie, + contextFunction, + verifiedRefreshTokenPayload, } from "./server/userResolver/auth" import cookie = require("cookie") +import cors = require("cors") export const createServer = async (port: number) => { - const server = new ApolloServer({ - schema: await createSchema(), - playground: true, - introspection: true, - debug: true, - context: contextFunction, - }) + const server = new ApolloServer({ + schema: await createSchema(), + playground: true, + introspection: true, + debug: true, + context: contextFunction, + }) - const app = express() - app.post("/refresh_token", (req, res) => { - try { - const parsedCookie = cookie.parse(req.headers.cookie!) - const refreshPayload = verifiedRefreshTokenPayload(parsedCookie.rt) - const accessToken = accessTokenWithRefreshCookie(refreshPayload.userId, res) - res.json({ data: accessToken }) - } catch (error) { - res.json({ data: null, errors: "Refresh failed: " + error }) - } - }) - server.applyMiddleware({ app }) - app.listen({ port }) + const app = express() + app.use( + cors({ + origin: "http://localhost:3000", + credentials: true, + }) + ) - return server + app.post("/refresh_token", (req, res) => { + try { + const parsedCookie = cookie.parse(req.headers.cookie!) + const refreshPayload = verifiedRefreshTokenPayload(parsedCookie.rt) + const accessToken = accessTokenWithRefreshCookie(refreshPayload.userId, res) + + res.json({ data: accessToken }) + } catch (error) { + res.json({ data: null, errors: "Refresh failed: " + error }) + } + }) + + server.applyMiddleware({ app, cors: false }) + app.listen({ port }) + + return server }