DEV Community

Cover image for JWT Auth in Apollo Server with TypeScript
Radzion Chachura
Radzion Chachura

Posted on • Originally published at radzion.com

JWT Auth in Apollo Server with TypeScript

Watch on YouTube

Since the JWT token comes in a header, we want to handle context in ApolloServer.

const server = new ApolloServer({
  // ...
  context: async ({ event: { headers } }): Promise<OperationContext> => {
    let userId = null
    try {
      userId = await userIdFromToken(
        headers["Authorization"].replace("Bearer ", "")
      )
    } catch {}

    return {
      userId,
    }
  },
})
Enter fullscreen mode Exit fullscreen mode

We take Authorization from the header, remove the "Bearer" word, and pass it to the userIdFromToken function.

It takes a secret from env variables. Then we verify the token against the secret with jwt.verify.

import jwt from "jsonwebtoken"
import { assertEnvVar } from "../shared/assertEnvVar"

interface DecodedToken {
  id: string
}

export const userIdFromToken = async (token: string) => {
  const secret = assertEnvVar("SECRET")

  const decoded = jwt.verify(token, secret)

  return decoded ? (decoded as DecodedToken).id : null
}
Enter fullscreen mode Exit fullscreen mode

If everything is alright, we should have an id in a decoded result.

Now, we should get the user id in the resolver as a part of the context argument.

export interface OperationContext {
  userId: string | null
  country: string | null
}
Enter fullscreen mode Exit fullscreen mode

To check for userId, I run the assertUserId function. It will throw an error if the user id is missing.

import { AuthenticationError } from "apollo-server-lambda"
import { OperationContext } from "../graphql/OperationContext"

export const assertUserId = ({ userId }: OperationContext) => {
  if (!userId) {
    throw new AuthenticationError("Invalid token")
  }

  return userId
}
Enter fullscreen mode Exit fullscreen mode

The API creates a token on the authorization request and sends it back to the user.

Here we have the generateAuthData function that takes the user id. It calculates token expiration time and signs the token with the secret from env variables.

import jwt from "jsonwebtoken"
import { assertEnvVar } from "../shared/assertEnvVar"

const jwtLifespanInSeconds = 15552000

const getTokenExpirationTime = (ms: number) =>
  Math.floor(Date.now() / 1000) + ms

export const generateAuthData = (id: string) => {
  const tokenExpirationTime = getTokenExpirationTime(jwtLifespanInSeconds)
  const secret = assertEnvVar("SECRET")

  return {
    token: jwt.sign({ id, exp: tokenExpirationTime }, secret),
    tokenExpirationTime,
  } as const
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)