Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 97 additions & 30 deletions docs/src/content/docs/api-reference/jose.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ Through this api reference documentation you are going to learn and understand f
- [Features](#features)
- [Installation](#installation)
- [API Reference](#api-reference)
- [Signing and Encripting a JWT](#signing-and-encripting-a-jwt)
- [encodeJWT](#encode-jwt)
- [decodeJWT](#decode-jwt)
- [Signing and Encrypting a JWT](#signing-and-encrypting-a-jwt)
- [Signing API](#signing-api-jws)
- [Encryption API](#encryption-api-jwe)
- [Key Derivation](#key-derivation)

---

Expand All @@ -43,6 +44,7 @@ Through this api reference documentation you are going to learn and understand f
- **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.
- **Secure by design** — built on top of modern JOSE standards (RFC 7515, 7516, 7519).
- **Key derivation** — use `deriveKey` for key derivation

---

Expand All @@ -63,13 +65,13 @@ npm install @aura-stack/jose

## API Reference

## Signing and Encripting a JWT
## Signing and Encrypting a JWT

### `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](https://datatracker.ietf.org/doc/html/rfc7519#section-11.2).

```ts
```ts lineNumbers
type encodeJWT = (token: JWTPayload, secret: SecretInput) => Promise<string>
```

Expand All @@ -86,7 +88,7 @@ Promise resolving to the signed and encrypted JWT string.

#### Example

```typescript
```ts lineNumbers
import { encodeJWT } from "@aura-stack/jose"

const token = await encodeJWT(
Expand All @@ -111,7 +113,7 @@ Decodes a JWT by first decrypting it (JWE) and then verifying it (JWS). This val

#### Signature

```typescript
```ts lineNumbers
function decodeJWT(token: string, secret: SecretInput): Promise<JWTPayload>
```

Expand All @@ -128,7 +130,7 @@ Promise resolving to the decoded JWT payload.

#### Example

```typescript
```ts lineNumbers
import { decodeJWT } from "@aura-stack/jose"

try {
Expand All @@ -150,7 +152,7 @@ Creates a JWT handler with bound `encodeJWT` and `decodeJWT` methods.

#### Signature

```typescript
```ts lineNumbers
function createJWT(secret: SecretInput): {
encodeJWT: (payload: JWTPayload) => Promise<string>
decodeJWT: (token: string) => Promise<JWTPayload>
Expand All @@ -169,7 +171,7 @@ Object with `encodeJWT` and `decodeJWT` methods.

#### Example

```typescript
```ts lineNumbers
import { createJWT } from "@aura-stack/jose"

const jwt = createJWT(process.env.JWT_SECRET!)
Expand All @@ -189,7 +191,7 @@ Signs a JWT using HS256 algorithm with standard claims.

#### Signature

```typescript
```ts lineNumbers
function signJWS(payload: JWTPayload, secret: SecretInput): Promise<string>
```

Expand All @@ -208,7 +210,7 @@ The following claims are automatically added:

#### Example

```typescript
```ts lineNumbers
import { signJWS } from "@aura-stack/jose"

const signed = await signJWS(
Expand All @@ -230,7 +232,7 @@ Verifies a signed JWT and returns the payload if valid.

#### Signature

```typescript
```ts lineNumbers
function verifyJWS(token: string, secret: SecretInput): Promise<JWTPayload>
```

Expand All @@ -242,7 +244,7 @@ function verifyJWS(token: string, secret: SecretInput): Promise<JWTPayload>

#### Example

```typescript
```ts lineNumbers
import { verifyJWS } from "@aura-stack/jose"

try {
Expand All @@ -261,7 +263,7 @@ Creates a JWS handler with bound signing and verification methods.

#### Signature

```typescript
```ts lineNumbers
function createJWS(secret: SecretInput): {
signJWS: (payload: JWTPayload) => Promise<string>
verifyJWS: (token: string) => Promise<JWTPayload>
Expand All @@ -270,7 +272,7 @@ function createJWS(secret: SecretInput): {

#### Example

```typescript
```ts lineNumbers
import { createJWS } from "@aura-stack/jose"

const jws = createJWS(process.env.JWT_SECRET!)
Expand All @@ -287,7 +289,7 @@ Encrypts a JWT string using A256GCM encryption.

#### Signature

```typescript
```ts lineNumbers
function encryptJWE(payload: string, secret: SecretInput): Promise<string>
```

Expand All @@ -306,7 +308,7 @@ function encryptJWE(payload: string, secret: SecretInput): Promise<string>

#### Example

```typescript
```ts lineNumbers
import { encryptJWE } from "@aura-stack/jose"

const encrypted = await encryptJWE("signed-jwt-token-here", process.env.ENCRYPTION_KEY!)
Expand All @@ -320,13 +322,13 @@ Decrypts an encrypted JWT and returns the original payload string.

#### Signature

```typescript
```ts lineNumbers
function decryptJWE(token: string, secret: SecretInput): Promise<string>
```

#### Example

```typescript
```ts lineNumbers
import { decryptJWE } from "@aura-stack/jose"

try {
Expand All @@ -345,7 +347,7 @@ Creates a JWE handler with bound encryption and decryption methods.

#### Signature

```typescript
```ts lineNumbers
function createJWE(secret: SecretInput): {
encryptJWE: (payload: string) => Promise<string>
decryptJWE: (token: string) => Promise<string>
Expand All @@ -354,7 +356,7 @@ function createJWE(secret: SecretInput): {

#### Example

```typescript
```ts lineNumbers
import { createJWE } from "@aura-stack/jose"

const jwe = createJWE(process.env.ENCRYPTION_KEY!)
Expand All @@ -363,19 +365,82 @@ const encrypted = await jwe.encryptJWE("data-to-encrypt")
const decrypted = await jwe.decryptJWE(encrypted)
```

## Key Derivation

### `deriveKey(secret, info, len)`

Create a Key derivation which implements HKDF

#### Signature

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

function deriveKey(
secret: SecretInput,
info: string,
len: number
): {
key: ArrayBuffer
deriveKey: Buffer<ArrayBuffer>
}
```

#### Example

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

const secret = "secret-key"

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

### `createDeriveKey(secret, info, len)`

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

#### Signature

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

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

#### Example

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

const secret = "secret-key"

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

---

## Types

### `SecretInput`

Flexible secret key input type.

```typescript
type SecretInput = CryptoKey | KeyObject | string | Uint8Array
```ts lineNumbers
type SecretInput = KeyObject | Uint8Array | string
```

Accepts:

- `CryptoKey` - Web Crypto API key
- `KeyObject` - Node.js crypto key
- `string` - String secret (converted internally)
- `Uint8Array` - Binary secret
Expand All @@ -384,7 +449,7 @@ Accepts:

Standard JWT payload with registered claims.

```typescript
```ts lineNumbers
interface JWTPayload {
iss?: string // Issuer
sub?: string // Subject (user ID)
Expand All @@ -409,7 +474,7 @@ interface JWTPayload {

Always use cryptographically secure random strings:

```typescript
```ts lineNumbers
// ✅ Good: Strong random secret
import crypto from "node:crypto"
const secret = crypto.randomBytes(32).toString("hex")
Expand All @@ -426,7 +491,7 @@ const secret = "password123"

Never hardcode secrets:

```typescript
```ts lineNumbers
// ✅ Good: Environment variable
const secret = process.env.JWT_SECRET

Expand Down Expand Up @@ -463,11 +528,13 @@ Always transmit JWTs over HTTPS in production to prevent token interception.

</Steps>

---

## Usage

### With Session Management

```typescript
```ts lineNumbers
import { encodeJWT, decodeJWT } from "@aura-stack/jose"

// Create session token
Expand Down Expand Up @@ -510,7 +577,7 @@ if (userId) {

This package is built on top of [`jose`](https://github.com/panva/jose) - a robust implementation of JOSE standards. For advanced use cases, you can access the underlying library directly:

```typescript
```ts lineNumbers
import * as jose from "jose"

// Full access to jose library
Expand Down
43 changes: 4 additions & 39 deletions packages/core/src/jose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,8 @@ export type { JWTPayload } from "@aura-stack/jose/jose"

const secretKey = process.env.AURA_AUTH_SECRET!

let jwtInstance: ReturnType<typeof createJWT> | null = null
let jwsInstance: ReturnType<typeof createJWS> | null = null
const { derivedKey: derivedSessionKey } = createDeriveKey(secretKey, "session")
const { derivedKey: derivedCsrfTokenKey } = createDeriveKey(secretKey, "csrfToken")

/**
* in ES2020 modules, is not allowed to have top-level await, so we create the instances lazily
* and cache them for future use. as well, cmjs modules do not support top-level await either.
*/
const createJoseInstance = async () => {
if (jwtInstance && jwsInstance) {
return { jwtInstance, jwsInstance }
}

const { derivedKey: derivedSessionKey } = await createDeriveKey(secretKey, "session")
const { derivedKey: derivedCsrfTokenKey } = await createDeriveKey(secretKey, "csrfToken")

jwtInstance = createJWT(derivedSessionKey)
jwsInstance = createJWS(derivedCsrfTokenKey)

return { jwtInstance, jwsInstance }
}

export const encodeJWT = async (...args: Parameters<ReturnType<typeof createJWT>["encodeJWT"]>) => {
const { jwtInstance } = await createJoseInstance()
return jwtInstance.encodeJWT(...args)
}

export const decodeJWT = async (...args: Parameters<ReturnType<typeof createJWT>["decodeJWT"]>) => {
const { jwtInstance } = await createJoseInstance()
return jwtInstance.decodeJWT(...args)
}

export const signJWS = async (...args: Parameters<ReturnType<typeof createJWS>["signJWS"]>) => {
const { jwsInstance } = await createJoseInstance()
return jwsInstance.signJWS(...args)
}

export const verifyJWS = async (...args: Parameters<ReturnType<typeof createJWS>["verifyJWS"]>) => {
const { jwsInstance } = await createJoseInstance()
return jwsInstance.verifyJWS(...args)
}
export const { decodeJWT, encodeJWT } = createJWT(derivedSessionKey)
export const { signJWS, verifyJWS } = createJWS(derivedCsrfTokenKey)
Loading