Skip to content
On this page

JWT Authentication Example

In this example we create a custom composable to handle all the auth for our requests. This will allow any code to useAuth and get access to the current user.

We also create a custom error class, VisibleError. This will allow us to detect these custom errors and inspect them for a statusCode that should be returned to the client. This could be extended greatly for much more customized errors.

Lastly we create our own global error handler, onError. This could also be expanded by adding useAuth to this handler. If we have a user different error messages could be returned. For example Admin users could see full stack traces and detailed info.

VisibleError.ts

class VisibleError extends Error {
  statusCode: number

  constructor(message: string, statusCode: number) {
    super(message)
    this.statusCode = statusCode
  }
}

useAuth.ts

import { createSharedExecutionComposable } from '@serverless-use/core'
import { useQueryParameters, useRequestHeaders } from '@serverless-use/apigw'
import JWT from 'jwt-simple'

import VisibleError from '../error/VisibleError'

type User = {
  name: string
  email: string
  sub: string
}

const SECRET = process.env.AUTH_SECRET || 'secret'
export const useAuth = createSharedExecutionComposable(() => {
  let user: User | undefined = undefined

  const { get: getHeader } = useRequestHeaders()
  const { get: getQueryParameters } = useQueryParameters()

  const authHeader = getHeader('Authorization')
  const queryToken = getQueryParameters('token')

  const [type, token] = authHeader?.split(' ') || ['query', queryToken]

  if (token && type) {
    try {
      user = JWT.decode(token, SECRET) as User
    } catch (e) {
      throw new VisibleError((e as Error).message, 401)
    }
  }

  return {
    get user() {
      return user
    },
  }
})

handler.ts

import { useAuth } from '../composables/useAuth'

import VisibleError from '../error/VisibleError'

export default use(
  async () => {
    const { user } = useAuth()

    if (!user) throw new VisibleError('Unauthorized', 401)

    return {
      user,
    }
  },
  {
    onError: (e: Error) => {
      const statusCode = e instanceof VisibleError ? e.statusCode : 400

      return {
        statusCode,
        headers: {
          'Content-Type': 'text/html',
        },
        body:
          statusCode === 401
            ? '<body style="display: grid; place-content: center;"><img src="https://i.giphy.com/media/wSSooF0fJM97W/giphy.gif"></body>'
            : e.message,
      }
    },
  },
)