Appearance
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,
      }
    },
  },
)