Skip to content
Open
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
49 changes: 49 additions & 0 deletions docs/ERROR_CODE_REGISTRY.md
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,55 @@ Error codes are defined in `src/constants/error.constants.ts` and should be trea
}
```

---
### TOKEN_EXPIRY_TAMPERED

**HTTP Status:** 401 Unauthorized

**Meaning:** JWT token expiry has been tampered with. The token's `exp` claim does not match the expected `iat + ttlSeconds`.

**When to use:**

- Token's `exp` is greater than `iat + ttlSeconds` (expiry was extended beyond the valid window)
- Token's `exp` is less than `iat + ttlSeconds` (expiry was shortened, token revoked early)

**Example:**

```json
{
"success": false,
"error": {
"code": "TOKEN_EXPIRY_TAMPERED",
"message": "Token expiry has been tampered with"
}
}
```

---

### MISSING_IAT

**HTTP Status:** 401 Unauthorized

**Meaning:** JWT token is missing the `iat` (issued-at) claim. The token cannot have its expiry validated without an issuance timestamp.

**When to use:**

- Token does not include an `iat` claim
- Token payload is decoded but `iat` is undefined

**Example:**

```json
{
"success": false,
"error": {
"code": "MISSING_IAT",
"message": "Token is missing the issued-at (iat) claim"
}
}
```

---

## Adding New Error Codes
Expand Down
2 changes: 2 additions & 0 deletions src/constants/error.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export const ErrorCode = {
JWT_ERROR: 'TOKEN_ERROR',
INSUFFICIENT_BALANCE: 'insufficient_balance',
NOT_A_CREATOR: 'not_a_creator',
TOKEN_EXPIRY_TAMPERED: 'token_expiry_tampered',
MISSING_IAT: 'missing_iat',
} as const;

export type ErrorCodeType = (typeof ErrorCode)[keyof typeof ErrorCode];
53 changes: 53 additions & 0 deletions src/utils/token-expiry.validator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { validateTokenExpiry } from '../utils/token-expiry.validator';

describe('validateTokenExpiry', () => {
it('accepts a token where exp === iat + ttl', () => {
const payload = {
iat: 1000,
exp: 1060,
ttlSeconds: 60,
};

const result = validateTokenExpiry(payload);

expect(result.valid).toBe(true);
});

it('rejects a token where exp > iat + ttl with token_expiry_tampered', () => {
const payload = {
iat: 1000,
exp: 1100,
ttlSeconds: 60,
};

const result = validateTokenExpiry(payload);

expect(result.valid).toBe(false);
expect(result.code).toBe('token_expiry_tampered');
});

it('rejects a token where exp < iat + ttl with token_expiry_tampered', () => {
const payload = {
iat: 1000,
exp: 1050,
ttlSeconds: 60,
};

const result = validateTokenExpiry(payload);

expect(result.valid).toBe(false);
expect(result.code).toBe('token_expiry_tampered');
});

it('rejects a token with no iat claim with missing_iat', () => {
const payload = {
exp: 1100,
ttlSeconds: 60,
};

const result = validateTokenExpiry(payload);

expect(result.valid).toBe(false);
expect(result.code).toBe('missing_iat');
});
});
35 changes: 35 additions & 0 deletions src/utils/token-expiry.validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Token expiry validator.
*
* Computes the expected expiry as iat + ttlSeconds and validates that
* the token's exp claim matches. Returns specific error codes for
* tampered expiry or missing iat claim.
*/
export interface TokenExpiryValidationResult {
valid: boolean;
code?: string;
}

export function validateTokenExpiry(
payload: { iat?: number; exp: number; ttlSeconds: number }
): TokenExpiryValidationResult {
if (payload.iat === undefined) {
return { valid: false, code: 'missing_iat' };
}

const expectedExp = payload.iat + payload.ttlSeconds;

if (payload.exp === expectedExp) {
return { valid: true };
}

if (payload.exp > expectedExp) {
return { valid: false, code: 'token_expiry_tampered' };
}

if (payload.exp < expectedExp) {
return { valid: false, code: 'token_expiry_tampered' };
}

return { valid: false };
}
Loading