You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
demo-graphql-oauth/src/integration/refresh_token.spec.ts

145 lines
5.1 KiB

import cookie = require("cookie")
import { gql } from "apollo-server-express"
import { GraphQLClient, rawRequest } from "graphql-request"
import fetch from "node-fetch"
import { createConnection } from "typeorm"
import { createServer } from "../server"
import { gqlToStr } from "../server/schema"
import {
gqlUri,
refreshTokenUri,
testingConnectionOptions,
testingPort,
} from "../server/testing"
import {
rtCookieOptions,
signRefreshToken,
verifiedRefreshTokenPayload,
} from "../server/UserResolver/auth"
import { User } from "../server/UserResolver/User"
let user: User
describe("server should", () => {
it("reject refresh token 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()
})
it("reject refresh token with tokenVersion mismatch", async () => {
const oldRefreshToken = signRefreshToken({ uid: user.id, ver: 0 })
const cookieHeader = cookie.serialize("rt", oldRefreshToken, rtCookieOptions())
await user.invalidateTokens()
const response = await fetch(refreshTokenUri, {
method: "POST",
headers: { cookie: cookieHeader },
})
const jsonResponse = await response.json()
expect(jsonResponse.data).toBeNull()
expect(jsonResponse.errors).not.toBeUndefined()
})
it("provide access token given good crendentials and grant refresh token with it", async () => {
4 years ago
const halfADay = (60 * 60 * 24) / 2
const fifteenDays = 60 * 60 * 24 * 15
const accessTokenReponse = await rawRequest(gqlUri, gqlToStr(accessTokenMutation))
4 years ago
const accessToken: string = accessTokenReponse.data.accessToken
const headers: Headers = accessTokenReponse.headers
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 cookieHeader = headers.get("set-cookie") as string
4 years ago
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)
4 years ago
const client = new GraphQLClient(gqlUri, {
headers: {
Authorization: "Bearer " + accessToken,
},
})
const meResponse = await client.rawRequest(gqlToStr(meMutation))
4 years ago
const refreshTokenResponse = await fetch(refreshTokenUri, {
method: "POST",
headers: { cookie: cookieHeader },
})
const jsonResponse = await refreshTokenResponse.json()
4 years ago
expect(varyHeader).toBe("Origin")
expect(acacHeader).toBe("true")
expect(acaoHeader).toMatch(/http:/)
4 years ago
expect(cookieHeader).toMatch(/HttpOnly/)
expect(parsedCookie.Path).toBe("/refresh_token")
expect(refreshTokenPayload.uid).toBe(user.id)
expect(refreshTokenPayload.ver).toBe(user.tokenVersion)
expect(refreshTokenPayload.msc).toBeLessThan(1000)
4 years ago
expect(refreshCookieExpires).toBeCloseTo(refLifetime)
expect(jwtLifetime).toBeGreaterThanOrEqual(halfADay)
expect(jwtLifetime).not.toBeGreaterThan(fifteenDays)
4 years ago
expect(meResponse.data.me.email).toBe("auth@server.com")
4 years ago
expect(jsonResponse.data).toBeDefined()
expect(jsonResponse.errors).toBeUndefined()
})
it("provide an empty rt cookie on signOut mutation", async () => {
const signOutReponse = await rawRequest(gqlUri, gqlToStr(signOutMutation))
const headers: Headers = signOutReponse.headers
const cookieHeader = headers.get("set-cookie") as string
const parsedCookie = cookie.parse(cookieHeader)
expect(cookieHeader).toMatch(/HttpOnly/)
expect(parsedCookie.Path).toBe("/refresh_token")
expect(parsedCookie.rt).toBe("")
4 years ago
})
})
beforeAll(async () => {
4 years ago
await createConnection(testingConnectionOptions())
await createServer(testingPort)
await User.delete({ email: "auth@server.com" })
user = await User.create({ email: "auth@server.com", password: "password" }).save()
})
afterAll(async () => {
await User.delete({ email: "auth@server.com" })
})
const accessTokenMutation = gql`
4 years ago
mutation {
accessToken(email: "auth@server.com", password: "password")
}
`
const meMutation = gql`
mutation {
4 years ago
me {
email
}
}
`
const signOutMutation = gql`
mutation {
signOut
}
`
const dateInKiloSeconds = (date: string | number) => new Date(date).getTime() / 1000000