Melody AuthMelody Auth
  • Auth Server Setup
  • Admin Panel Setup
  • Email Provider Setup
  • SMS Provider Setup
  • External Identity Providers

    • Social Sign-In Provider Setup
    • OIDC SSO Setup
    • SAML SSO Setup
  • Main Features

    • Authentication
    • JWT & JWKS
    • Multi-Factor Authentication
    • Role-Based Access Control
    • Policies
    • Organizations
  • Additional Features

    • User Attributes
    • App Banners
    • Organization Groups
    • Impersonation
    • Log Management
  • Customization

    • Auth Server Configuration
    • Branding
    • Localization
  • Frontend SDKs

    • React SDK
    • Angular SDK
    • Vue SDK
    • Web SDK
  • Backend APIs

    • S2S API Setup
    • S2S API Swagger
    • Embedded Auth API Setup
    • Embedded Auth API Swagger
  • Deployment Pipelines
  • Rotate JWT Secret
  • English
  • zh-CN
  • Auth Server Setup
  • Admin Panel Setup
  • Email Provider Setup
  • SMS Provider Setup
  • External Identity Providers

    • Social Sign-In Provider Setup
    • OIDC SSO Setup
    • SAML SSO Setup
  • Main Features

    • Authentication
    • JWT & JWKS
    • Multi-Factor Authentication
    • Role-Based Access Control
    • Policies
    • Organizations
  • Additional Features

    • User Attributes
    • App Banners
    • Organization Groups
    • Impersonation
    • Log Management
  • Customization

    • Auth Server Configuration
    • Branding
    • Localization
  • Frontend SDKs

    • React SDK
    • Angular SDK
    • Vue SDK
    • Web SDK
  • Backend APIs

    • S2S API Setup
    • S2S API Swagger
    • Embedded Auth API Setup
    • Embedded Auth API Swagger
  • Deployment Pipelines
  • Rotate JWT Secret
  • English
  • zh-CN
  • JWT & JWKS

JWT & JWKS

Melody Auth sign JWT access tokens using RS256 algorithm. You can verify the tokens with the JWKS endpoint published by your Auth Server.

Overview

  • Melody Auth issues RS256-signed JWT access tokens for SPAs.
  • Verify tokens with the Auth Server JWKS endpoint: [AUTH_SERVER]/.well-known/jwks.json.
  • Always validate the JWT signature, algorithm, and critical claims like aud (audience) and iss (issuer).

JWKS endpoint and key rotation

  • The JWKS endpoint exposes the current public keys by kid (key ID).
  • Keys may rotate. Use a JWKS client that supports caching and rate limiting.
  • On verification, use the token header kid to select the correct public key.

Node.js example (TypeScript)

import { verify, JwtHeader } from 'jsonwebtoken'
import jwksClient, { SigningKey } from 'jwks-rsa'

// Required environment variables
// NEXT_PUBLIC_SERVER_URI: base URL of your Auth Server (e.g. https://auth-server.example.com)
// EXPECTED_AUDIENCE: the audience your API expects

const client = jwksClient({
  jwksUri: `${process.env.NEXT_PUBLIC_SERVER_URI}/.well-known/jwks.json`,
  cache: true,
  cacheMaxEntries: 5,
  cacheMaxAge: 10 * 60 * 1000,
  rateLimit: true,
  jwksRequestsPerMinute: 10,
})

function getKey(header: JwtHeader, callback: (err: Error | null, key?: string) => void) {
  if (!header.kid) return callback(new Error('Missing kid in token header'))
  client.getSigningKey(header.kid, (err: Error | null, key?: SigningKey) => {
    if (err || !key) return callback(err || new Error('Signing key not found'))
    const signingKey = (key as any).publicKey || (key as any).rsaPublicKey
    callback(null, signingKey)
  })
}

export function verifyAccessTokenRaw(token: string): Promise<Record<string, any>> {
  return new Promise((resolve, reject) => {
    verify(
      token,
      getKey,
      {
        algorithms: ['RS256'],
      },
      (err, decoded) => {
        if (err) return reject(err)
        resolve(decoded as Record<string, any>)
      },
    )
  })
}

// Example Express middleware
export async function requireAuth(req: any, res: any, next: any) {
  try {
    const authHeader = req.headers['authorization'] as string | undefined
    const token = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : undefined
    if (!token) return res.status(401).json({ error: 'Missing access token' })
    const claims = await verifyAccessTokenRaw(token)
    // Optionally enforce additional checks (e.g., scopes/roles) here
    req.user = claims
    next()
  } catch (err: any) {
    return res.status(401).json({ error: 'Invalid or expired token', details: err?.message })
  }
}
Last Updated: 8/11/25, 11:27 PM
Contributors: Baozier