Skip to content

Authentication Example

JWT-based authentication for REST and WebSocket.

Setup

typescript
import { defineConfig } from 'vite'
// import mockApi from '@ndriadev/vite-plugin-universal-api' //Default export
import { universalApi } from '@ndriadev/vite-plugin-universal-api' // Named export

const users = [{ username: 'admin', password: 'admin123' }]
const tokens = new Map()

function generateToken(username) {
  const token = `token-${Date.now()}`
  tokens.set(token, { username, exp: Date.now() + 3600000 })
  return token
}

function verifyToken(token) {
  const data = tokens.get(token)
  if (!data || data.exp < Date.now()) {
    throw new Error('Invalid token')
  }
  return data
}

export default defineConfig({
  plugins: [
    universalApi({
      endpointPrefix: '/api',

      handlers: [
        // Login
        {
          pattern: '/login',
          method: 'POST',
          handle: async (req, res) => {
            const user = users.find(
              u => u.username === req.body.username &&
                   u.password === req.body.password
            )

            if (!user) {
              res.writeHead(401)
              res.end(JSON.stringify({ error: 'Invalid credentials' }))
              return
            }

            const token = generateToken(user.username)
            res.writeHead(200)
            res.end(JSON.stringify({ token }))
          }
        },

        // Protected endpoint
        {
          pattern: '/protected',
          method: 'GET',
          handle: async (req, res) => {
            try {
              const token = req.headers.authorization?.replace('Bearer ', '')
              const user = verifyToken(token)

              res.writeHead(200)
              res.end(JSON.stringify({ message: 'Protected data', user }))
            } catch (err) {
              res.writeHead(401)
              res.end(JSON.stringify({ error: 'Unauthorized' }))
            }
          }
        }
      ],

      // Authenticated WebSocket
      enableWs: true,
      wsHandlers: [
        {
          pattern: '/ws/private',

          authenticate: async (req) => {
            const url = new URL(req.url, 'ws://localhost')
            const token = url.searchParams.get('token')

            try {
              const user = verifyToken(token)
              return { success: true, data: { user } }
            } catch (err) {
              return { success: false, code: 4001, reason: 'Invalid token' }
            }
          },

          onConnect: (conn) => {
            conn.send({ type: 'authenticated', user: conn.authData.user })
          }
        }
      ]
    })
  ]
})

Full authentication with REST and WebSocket!

Released under the MIT License.