Aura Auth
API Reference

@aura-stack/jose

JOSE utilities for JWT signing, verification, and encryption

JOSE API Reference

@aura-stack/jose provides a curated API surface for working with JWS, JWE, and JWT standards.
It also includes a convenience re-export of the official jose library via the path @aura-stack/jose/jose, allowing consumers to access low-level cryptographic primitives when needed.

import { createJWT, createJWS, createJWE } from "@aura-stack/jose"

// JWSs (Json Web Signature)
export const { signJWS, verifyJWS } = createJWS("secret-key")

// JWEs (Json Web Encryption)
export const { encryptJWE, decryptJWE } = createJWE("secret-key")

// JWTs (Json Web Token)
export const { encodeJWT, decodeJWT } = createJWT("secret-key")

What you'll learn

Through this api reference documentation you are going to learn and understand from basic to advanced about the JOSE API Reference:


Features

  • Direct re-export of jose — access all primitives from the upstream package via @aura-stack/jose/jose.
  • JWE utilities — use createJWE, encryptJWE, and decryptJWE for encryption/decryption flows.
  • JWS utilities — use createJWS, signJWS, and verifyJWS for signature and verification.
  • JWT helpers — use createJWT, encodeJWT, and decodeJWT for signing and encrypting JSON Web Tokens.
  • HKDF for key derivation — use deriveKey and createDeriveKey for key derivation.
  • Secure by design — built on top of modern JOSE standards (RFC 7515, 7516, 7519).

Installation

If you haven’t installed the package yet:

npm install @aura-stack/jose

The @aura-stack/jose package is already included as a dependency of @aura-stack/auth. You only need to install it separately if you want to use it standalone.


API Reference

Signing and Encrypting a JWT

By default, Aura Auth uses the same secret value to encrypt and sign JWTs. However, a stronger version is provided that accepts separate secret keys for encryption and signing. The encodeJWT, decodeJWT, and createJWT functions accept an object with jws and jwe fields to specify separate keys. The second argument extends the DerivedKeyInput type.

import { DerivedKeyInput } from "@aura-stack/jose"

const derivedKeys: DerivedKeyInput = {
  jwe: process.env.AURA_AUTH_JWE_SECRET,
  jws: process.env.AURA_AUTH_JWS_SECRET,
}

encodeJWT(payload, secret)

Encodes a JWT by first signing it (JWS) and then encrypting it (JWE). This ensures both integrity and confidentiality as recommended by RFC 7519 #11.2.

Signature

type encodeJWT = (token: JWTPayload, secret: SecretInput | DerivedKeyInput) => Promise<string>

Parameters

ParameterTypeDescription
tokenJWTPayloadPayload data to encode in the JWT
secretSecretInput | DerivedKeyInputSecret key for signing and encrypting

Returns

ReturnTypeDescription
EncodedPromise<string>A signed and encrypted JWT string

Example

import { encodeJWT } from "@aura-stack/jose"

const token = await encodeJWT(
  {
    sub: "user-123",
    email: "user@example.com",
    role: "admin",
  },
  process.env.JWT_SECRET!
)

console.log(token) // eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIi...
The token is first signed using HS256, then encrypted using A256GCM.

Using Derived Keys

import { encodeJWT } from "@aura-stack/jose"

const token = await encodeJWT(
  {
    sub: "user-123",
    email: "user@example.com",
    role: "admin",
  },
  {
    jwe: process.env.AURA_AUTH_JWE_SECRET,
    jws: process.env.AURA_AUTH_JWS_SECRET,
  }
)

console.log(token) // eyJhbGciOiJkaXIiLCJlbmMiOiJBMjU2R0NNIi...

decodeJWT(token, secret)

Decodes a JWT by first decrypting it (JWE) and then verifying it (JWS). This validates both confidentiality and integrity.

Signature

function decodeJWT(token: string, secret: SecretInput | DerivedKeyInput): Promise<JWTPayload>

Parameters

ParameterTypeDescription
tokenstringEncoded JWT string to decode
secretSecretInput | DerivedKeyInputSecret key for decrypting and verifying

Returns

ReturnTypeDescription
DecodedPromise<JWTPayload>The decoded JWT payload content

Example

import { decodeJWT } from "@aura-stack/jose"

try {
  const payload = await decodeJWT(token, process.env.JWT_SECRET!)

  console.log(payload.sub) // 'user-123'
  console.log(payload.email) // 'user@example.com'
  console.log(payload.exp) // 1234567890 (expiration timestamp)
} catch (error) {
  console.error("Invalid or expired token")
}

Using Derived Keys

import { decodeJWT } from "@aura-stack/jose"

try {
  const payload = await decodeJWT(token, {
    jwe: process.env.AURA_AUTH_JWE_SECRET,
    jws: process.env.AURA_AUTH_JWS_SECRET,
  })

  console.log(payload.sub) // 'user-123'
  console.log(payload.email) // 'user@example.com'
  console.log(payload.exp) // 1234567890 (expiration timestamp)
} catch (error) {
  console.error("Invalid or expired token")
}

createJWT(secret)

Creates a JWT handler with bound encodeJWT and decodeJWT methods.

Signature

function createJWT(secret: SecretInput | DerivedKeyInput): {
  encodeJWT: (payload: JWTPayload) => Promise<string>
  decodeJWT: (token: string) => Promise<JWTPayload>
}

Parameters

ParameterTypeDescription
secretSecretInput | DerivedKeyInputSecret key for all JWT operations

Returns

ReturnTypeDescription
encodedJWT(payload: JWTPayload) => Promise<string>Function that encodes a JWT that is signed and encrypted from payload data
decodedJWT(token: string) => Promise<JWTPayload>Function that decodes the JWT string by verifying and decrypting it

Example

import { createJWT } from "@aura-stack/jose"

const jwt = createJWT(process.env.JWT_SECRET!)

// Encode
const token = await jwt.encodeJWT({ sub: "user-123" })

// Decode
const payload = await jwt.decodeJWT(token)

Using Derived Keys

import { createJWT } from "@aura-stack/jose"

const jwt = createJWT({
  jwe: process.env.AURA_AUTH_JWE_SECRET,
  jws: process.env.AURA_AUTH_JWS_SECRET,
})

// Encode
const token = await jwt.encodeJWT({ sub: "user-123" })

// Decode
const payload = await jwt.decodeJWT(token)

Signing API (JWS)

signJWS(payload, secret)

Signs a JWT using HS256 algorithm with standard claims.

Signature

function signJWS(payload: JWTPayload, secret: SecretInput): Promise<string>

Parameters

ParameterTypeDescription
payloadJWTPayloadJWT payload data to sign
secretSecretInputSecret key for signing

Returns

ReturnTypeDescription
Signed JWTPromise<string>A signed JWT string

Generated Claims

The following claims are automatically added:

ClaimDescription
algAlgorithm: HS256
typType: JWT
iatIssued At: Current timestamp
nbfNot Before: Current timestamp
expExpiration: 15 days from now
jtiJWT ID: Random unique identifier

Example

import { signJWS } from "@aura-stack/jose"

const signed = await signJWS(
  {
    sub: "user-123",
    email: "user@example.com",
  },
  process.env.JWT_SECRET!
)

// Result: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

verifyJWS(token, secret, options)

Verifies a signed JWT and returns the payload if valid.

Signature

function verifyJWS(token: string, secret: SecretInput, options?: JWTVerifyOptions): Promise<JWTPayload>

Parameters

ParameterTypeDescription
tokenstringThe JWT string to be verified
secretSecretInputSecret key for verifying the integrity of the JWT
optionsJWTVerifyOptionsExtra JWT verification options

Returns

ReturnTypeDescription
Verified JWT payloadPromise<JWTPayload>The verified JWT payload content

Security

  • Rejects tokens using the "none" algorithm (unsecured JWTs)
  • Validates signature integrity
  • Checks expiration automatically

Example

import { verifyJWS } from "@aura-stack/jose"

try {
  const payload = await verifyJWS(signed, process.env.JWT_SECRET!)
  console.log("Valid token:", payload)
} catch (error) {
  console.error("Invalid token:", error.message)
}

Using extra JWT verification options

import { verifyJWS } from "@aura-stack/jose"

try {
  const payload = await verifyJWS(signed, process.env.JWT_SECRET!, {
    algorithms: ["HS256"],
    audience: "audience.value",
  })
  console.log("Valid token:", payload)
} catch (error) {
  console.error("Invalid token:", error.message)
}

createJWS(secret)

Creates a JWS handler with bound signing and verification methods.

Signature

function createJWS(secret: SecretInput): {
  signJWS: (payload: JWTPayload) => Promise<string>
  verifyJWS: (token: string, options?: JWTVerifyOptions) => Promise<JWTPayload>
}

Parameters

ParameterTypeDescription
secretSecretInputSecret key for signing and verifying the JWT

Returns

ReturnTypeDescription
signJWS(payload: JWTPayload) => Promise<string>Function to sign a JWT using HS256 algorithm
verifyJWS(token: string, options?: JWTVerifyOptions) => Promise<JWTPayload>Function to verify the signature and JWT integrity signed using signJWS function

Example

import { createJWS } from "@aura-stack/jose"

const jws = createJWS(process.env.JWT_SECRET!)

const signed = await jws.signJWS({ sub: "user-123" })
const payload = await jws.verifyJWS(signed)

Encryption API (JWE)

encryptJWE(payload, secret, options)

Encrypts a JWT string using A256GCM encryption.

Signature

function encryptJWE(payload: string, secret: SecretInput, options?: EncryptOptions): Promise<string>

Parameters

ParameterTypeDescription
payloadstringThe string data content to be encrypted
secretSecretInputSecret key for encrypting and decrypting
optionsEncryptOptionsEncryption options to set nbf and exp claims

Returns

ReturnTypeDescription
EncryptedPromise<string>An encrypted JWT string

Generated Claims

ClaimDescription
algAlgorithm: dir (direct encryption)
encEncryption: A256GCM
typType: JWT
ctyContent Type: JWT
iatIssued At: Current timestamp
nbfNot Before: Current timestamp
expExpiration: 15 days from now
jtiJWT ID: Random unique identifier

Example

import { encryptJWE } from "@aura-stack/jose"

const encrypted = await encryptJWE("signed-jwt-token-here", process.env.ENCRYPTION_KEY!)

Using encryption options

import { encryptJWE } from "@aura-stack/jose"

const encrypted = await encryptJWE("signed-jwt-token-here", process.env.ENCRYPTION_KEY!, {
  nbf: Math.floor(Date.now() / 1000) + 60,
  exp: Math.floor(Date.now() / 1000) + 60 * 60 * 5,
})

decryptJWE(token, secret, options)

Decrypts an encrypted JWT and returns the original payload string.

Signature

function decryptJWE(token: string, secret: SecretInput, options?: JWTDecryptOptions): Promise<string>

Parameters

ParameterTypeDescription
tokenstringThe string data to be decrypted
secretSecretInputSecret key for decrypting the token string
optionsJWTDecryptOptionsExtra JWE decryption options

Returns

ReturnTypeDescription
DecryptedPromise<string>A decrypted token string

Example

import { decryptJWE } from "@aura-stack/jose"

try {
  const decrypted = await decryptJWE(encrypted, process.env.ENCRYPTION_KEY!)
  console.log("Decrypted JWT:", decrypted)
} catch (error) {
  console.error("Decryption failed:", error.message)
}

createJWE(secret)

Creates a JWE handler with bound encryption and decryption methods.

Signature

function createJWE(secret: SecretInput): {
  encryptJWE: (payload: string, options?: EncryptOptions) => Promise<string>
  decryptJWE: (token: string, options?: JWTDecryptOptions) => Promise<string>
}

Parameters

ParameterTypeDescription
secretSecretInputSecret key for encrypting and decrypting

Returns

ReturnTypeDescription
encryptJWE(payload: string, options?: EncryptOptions) => Promise<string>Function to encrypt a string content using A256GCM algorithm
decryptJWE(token: string, options?: JWTDecryptOptions) => Promise<string>Function to decrypt string data encrypted using encryptJWE

Example

import { createJWE } from "@aura-stack/jose"

const jwe = createJWE(process.env.ENCRYPTION_KEY!)

const encrypted = await jwe.encryptJWE("data-to-encrypt")
const decrypted = await jwe.decryptJWE(encrypted)

Key Derivation

deriveKey(secret, salt, info, len)

Create a Key derivation which implements HKDF

Signature

import type { SecretInput } from "@aura-stack/jose"

function deriveKey(
  secret: SecretInput,
  salt: BinaryLike,
  info: string,
  length: number
): {
  key: ArrayBuffer
  deriveKey: Buffer<ArrayBuffer>
}

Parameters

ParameterTypeDescription
secretSecretInputSecret key for HKDF key derivation
saltBinaryLikeThe salt value
infostringAdditional information value
lengthnumberThe length of the key to generate

Returns

ReturnTypeDescription
keyArrayBufferThe derived key as ArrayBuffer
deriveKeyBuffer<ArrayBuffer>The derived key as Buffer type

Example

import { deriveKey } from "@aura-stack/jose/hkdf"

const secret = "secret-key"
const salt = "salt"

const sessionKey = deriveKey(secret, salt, "session key", 32)
const databaseKey = deriveKey(secret, salt, "database key", 64)

createDeriveKey(secret, salt, info, len)

Creates a key derivation function that includes verification for the key length. It implements the deriveKey function.

Signature

import type { SecretInput } from "@aura-stack/jose"

function createDeriveKey(
  secret: SecretInput,
  salt?: BinaryLike
  info?: string,
  len?: number
): {
  key: ArrayBuffer
  deriveKey: Buffer<ArrayBuffer>
}

Parameters

ParameterTypeDescription
secretSecretInputSecret key for HKDF key derivation
saltBinaryLikeThe salt value
infostringAdditional information value
lengthnumberThe length of the key to generate

Returns

ReturnTypeDescription
keyArrayBufferThe derived key as ArrayBuffer
deriveKeyBuffer<ArrayBuffer>The derived key as Buffer type

Example

import { createDeriveKey } from "@aura-stack/jose/hkdf"

const secret = "secret-key"
const salt = "salt"

const sessionKey = createDeriveKey(secret, salt, "session key", 32)
const databaseKey = createDeriveKey(secret, salt, "database key", 64)

Types

SecretInput

Flexible secret key input type.

type SecretInput = KeyObject | Uint8Array | string

Accepts:

  • KeyObject - Node.js crypto key
  • string - String secret (converted internally)
  • Uint8Array - Binary secret

JWTPayload

Standard JWT payload with registered claims.

interface JWTPayload {
  iss?: string // Issuer
  sub?: string // Subject (user ID)
  aud?: string | string[] // Audience
  exp?: number // Expiration time
  nbf?: number // Not before
  iat?: number // Issued at
  jti?: string // JWT ID
  [key: string]: any // Custom claims
}

Security Best Practices

Critical Security Guidelines

Strong Secrets

Always use cryptographically secure random strings:

// ✅ Good: Strong random secret
import crypto from "node:crypto"
const secret = crypto.randomBytes(32).toString("hex")

// ❌ Bad: Weak predictable secret
const secret = "password123"

Environment Variables

Never hardcode secrets:

// ✅ Good: Environment variable
const secret = process.env.JWT_SECRET

// ❌ Bad: Hardcoded secret
const secret = "my-secret-key-12345"

Token Expiration

Tokens automatically expire after 15 days. For custom expiration, use the underlying jose library directly.

HTTPS Only

Always transmit JWTs over HTTPS in production to prevent token interception.

Token Storage

  • Backend: Store in secure HTTP-only cookies
  • Frontend: Avoid localStorage - use cookies or session storage with caution

Usage

With Session Management

import { encodeJWT, decodeJWT } from "@aura-stack/jose"

// Create session token
async function createSession(userId: string) {
  return await encodeJWT(
    {
      sub: userId,
      type: "session",
      createdAt: Date.now(),
    },
    process.env.SESSION_SECRET!
  )
}

// Validate session token
async function validateSession(token: string) {
  try {
    const payload = await decodeJWT(token, process.env.SESSION_SECRET!)

    if (payload.type !== "session") {
      throw new Error("Invalid token type")
    }

    return payload.sub // Return user ID
  } catch {
    return null // Invalid or expired
  }
}

// Usage
const token = await createSession("user-123")
const userId = await validateSession(token)

if (userId) {
  console.log("Valid session for user:", userId)
}

Underlying Library

This package is built on top of jose - a robust implementation of JOSE standards. For advanced use cases, you can access the underlying library directly via the @aura-stack/jose/jose entry point.

import * as jose from "@aura-stack/jose/jose"

// Full access to jose library
const key = await jose.generateSecret("HS256")
const jwt = await new jose.SignJWT({ foo: "bar" }).setProtectedHeader({ alg: "HS256" }).sign(key)

On this page