Skip to content

Commit 577e9ea

Browse files
committed
feat: add jwt adapter using Jose as inspiration
1 parent d4fe3ff commit 577e9ea

File tree

9 files changed

+718
-4
lines changed

9 files changed

+718
-4
lines changed

libs/jwt-adapter/README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# JWT Adapter for Palmares
2+
3+
A flexible and secure JWT implementation for the Palmares authentication system, featuring a fluent API design.
4+
5+
## Credits
6+
7+
This implementation was inspired by the excellent [jose](https://github.com/panva/jose) package, particularly its fluent API design pattern. The method chaining approach and overall API structure were influenced by their work, which provides an elegant and developer-friendly way to work with JWTs. While our implementation is independent and uses different underlying mechanisms, we owe credit to the `jose` team for setting a high standard in JWT library design.
8+
9+
## Features
10+
11+
- 🔒 Secure JWT signing and verification
12+
- ⚡ Fluent API design for intuitive usage
13+
- 📝 Comprehensive TypeScript support
14+
- 🎯 Standard JWT claims support
15+
- ⏰ Flexible expiration handling
16+
- 🔍 Detailed verification options
17+
- 📚 Extensive documentation
18+
19+
## Installation
20+
21+
```bash
22+
npm install @palmares/jwt-adapter
23+
# or
24+
yarn add @palmares/jwt-adapter
25+
```
26+
27+
## Quick Start
28+
29+
### Signing a Token
30+
31+
```typescript
32+
import { jwtAdapter } from '@palmares/jwt-adapter';
33+
34+
const token = await jwtAdapter.methods.signJwt({ userId: '123' })
35+
.setIssuedAt()
36+
.setExpirationTime('1h')
37+
.sign(secret);
38+
```
39+
40+
### Verifying a Token
41+
42+
```typescript
43+
const payload = await jwtAdapter.methods.verifyJwt(token)
44+
.setAlgorithms(['HS256'])
45+
.verify(secret);
46+
```
47+
48+
## API Reference
49+
50+
### Signing Methods
51+
52+
#### `signJwt(payload: JwtPayload)`
53+
Creates a new JWT token with the given payload.
54+
55+
```typescript
56+
const token = await jwtAdapter.methods.signJwt({ role: 'admin' })
57+
.setProtectedHeader({ alg: 'HS256', typ: 'JWT' })
58+
.setIssuedAt()
59+
.setExpirationTime('1d')
60+
.sign(secret);
61+
```
62+
63+
Available methods:
64+
- `setProtectedHeader(header: Partial<JwtHeader>)` - Set JWT header
65+
- `setIssuedAt()` - Set issued at timestamp to current time
66+
- `setExpirationTime(time: number | string)` - Set expiration time
67+
68+
### Verification Methods
69+
70+
#### `verifyJwt(token: string)`
71+
Verifies a JWT token and returns its payload.
72+
73+
```typescript
74+
const payload = await jwtAdapter.methods.verifyJwt(token)
75+
.setAlgorithms(['HS256'])
76+
.setIssuer('my-app')
77+
.setAudience('api')
78+
.setClockTolerance(30)
79+
.setMaxTokenAge(3600)
80+
.verify(secret);
81+
```
82+
83+
Available methods:
84+
- `setAlgorithms(algorithms: string[])` - Set allowed algorithms
85+
- `setIssuer(issuer: string)` - Set expected issuer
86+
- `setAudience(audience: string)` - Set expected audience
87+
- `setClockTolerance(tolerance: number)` - Set time tolerance
88+
- `setMaxTokenAge(age: number)` - Set maximum token age
89+
- `setSubject(subject: string)` - Set expected subject
90+
- `setTyp(typ: string)` - Set expected token type
91+
92+
## Types
93+
94+
```typescript
95+
interface JwtPayload {
96+
exp?: number; // Expiration time
97+
iat?: number; // Issued at
98+
iss?: string; // Issuer
99+
aud?: string; // Audience
100+
sub?: string; // Subject
101+
[key: string]: any; // Custom claims
102+
}
103+
104+
interface JwtHeader {
105+
alg: string; // Algorithm
106+
typ: string; // Token type
107+
}
108+
109+
interface VerifyOptions {
110+
algorithms?: string[];
111+
issuer?: string;
112+
audience?: string;
113+
clockTolerance?: number;
114+
maxTokenAge?: number;
115+
subject?: string;
116+
typ?: string;
117+
}
118+
```
119+
120+
## Security Best Practices
121+
122+
1. **Always use HTTPS in production**
123+
- JWTs are signed but not encrypted by default
124+
- HTTPS ensures secure transmission
125+
126+
2. **Keep secrets secure**
127+
- Store JWT secrets in environment variables
128+
- Rotate secrets regularly
129+
- Never commit secrets to version control
130+
131+
3. **Set appropriate expiration times**
132+
- Short-lived tokens for sensitive operations
133+
- Longer-lived tokens with refresh mechanism for user sessions
134+
135+
4. **Validate all claims**
136+
- Use the verification options to validate issuer, audience, etc.
137+
- Check token age to prevent token reuse
138+
139+
5. **Use strong algorithms**
140+
- HS256 or better for HMAC
141+
- Consider RS256/ES256 for asymmetric signing
142+
143+
6. **Implement proper error handling**
144+
- Catch and handle verification errors
145+
- Log security-related events
146+
147+
7. **Consider token blacklisting**
148+
- Implement a mechanism to revoke tokens
149+
- Store revoked tokens in a database or cache
150+
151+
## Development
152+
153+
For detailed information about the development process, see [DEVELOPMENT.md](./DEVELOPMENT.md).
154+
155+
## License
156+
157+
MIT
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,9 @@
4242
"bugs": {
4343
"url": "https://github.com/palmaresHQ/palmares/issues"
4444
},
45-
"homepage": "https://github.com/palmaresHQ/palmares#readme"
45+
"homepage": "https://github.com/palmaresHQ/palmares#readme",
46+
"dependencies": {
47+
"@palmares/core": "workspace:*",
48+
"@palmares/auth": "workspace:*"
49+
}
4650
}

libs/jwt-adapter/src/adapter.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { authAdapter } from '@palmares/auth';
2+
3+
import { signJwt, verifyJwt } from './utils/jwt-utils';
4+
5+
import type { JwtHeader, JwtPayload, VerifyOptions } from './types/jwt';
6+
7+
/**
8+
* JWT adapter for Palmares authentication system
9+
* Provides methods for signing and verifying JWT tokens with a fluent API
10+
*
11+
* @example
12+
* ```typescript
13+
* // Sign a token
14+
* const token = await jwtAdapter.methods.signJwt({ userId: '123' })
15+
* .setIssuedAt()
16+
* .setExpirationTime('1h')
17+
* .sign(secret);
18+
*
19+
* // Verify a token
20+
* const payload = await jwtAdapter.methods.verifyJwt(token)
21+
* .setAlgorithms(['HS256'])
22+
* .verify(secret);
23+
* ```
24+
*/
25+
export const jwtAdapter = authAdapter(() => ({
26+
name: 'jwt',
27+
methods: {
28+
signJwt,
29+
verifyJwt
30+
}
31+
}));
32+
33+
// Export types for use in other parts of the application
34+
export type { JwtHeader, JwtPayload, VerifyOptions };
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Represents the payload of a JWT token
3+
* @interface JwtPayload
4+
* @property {number} [exp] - Unix timestamp when the token expires
5+
* @property {number} [iat] - Unix timestamp when the token was issued
6+
* @property {string} [iss] - Token issuer
7+
* @property {string} [aud] - Token audience
8+
* @property {string} [sub] - Token subject
9+
* @property {any} [key: string] - Any additional custom claims
10+
*/
11+
export interface JwtPayload {
12+
exp?: number;
13+
iat?: number;
14+
iss?: string;
15+
aud?: string;
16+
sub?: string;
17+
[key: string]: any;
18+
}
19+
20+
/**
21+
* Represents the header of a JWT token
22+
* @interface JwtHeader
23+
* @property {string} alg - The signing algorithm used (e.g., 'HS256')
24+
* @property {string} typ - The type of token (usually 'JWT')
25+
*/
26+
export interface JwtHeader {
27+
alg: string;
28+
typ: string;
29+
}
30+
31+
/**
32+
* Options for JWT verification
33+
* @interface VerifyOptions
34+
* @property {string[]} [algorithms] - List of allowed signing algorithms
35+
* @property {string} [issuer] - Expected token issuer
36+
* @property {string} [audience] - Expected token audience
37+
* @property {number} [clockTolerance] - Time tolerance in seconds for expiration checks
38+
* @property {number} [maxTokenAge] - Maximum allowed age of the token in seconds
39+
* @property {string} [subject] - Expected token subject
40+
* @property {string} [typ] - Expected token type
41+
*/
42+
export interface VerifyOptions {
43+
algorithms?: string[];
44+
issuer?: string;
45+
audience?: string;
46+
clockTolerance?: number;
47+
maxTokenAge?: number;
48+
subject?: string;
49+
typ?: string;
50+
}
51+
52+
/**
53+
* Internal state for JWT signing operations
54+
*/
55+
export interface SignJWTState {
56+
payload: JwtPayload;
57+
header: JwtHeader;
58+
issuedAt?: number;
59+
expirationTime?: number;
60+
}
61+
62+
/**
63+
* Internal state for JWT verification operations
64+
*/
65+
export interface VerifyJWTState {
66+
token: string;
67+
options: VerifyOptions;
68+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* Represents the payload of a JWT token
3+
* @interface JwtPayload
4+
* @property {number} [exp] - Unix timestamp when the token expires
5+
* @property {number} [iat] - Unix timestamp when the token was issued
6+
* @property {string} [iss] - Token issuer
7+
* @property {string} [aud] - Token audience
8+
* @property {string} [sub] - Token subject
9+
* @property {any} [key: string] - Any additional custom claims
10+
*/
11+
export interface JwtPayload {
12+
exp?: number;
13+
iat?: number;
14+
iss?: string;
15+
aud?: string;
16+
sub?: string;
17+
[key: string]: any;
18+
}
19+
20+
/**
21+
* Represents the header of a JWT token
22+
* @interface JwtHeader
23+
* @property {string} alg - The signing algorithm used (e.g., 'HS256')
24+
* @property {string} typ - The type of token (usually 'JWT')
25+
*/
26+
export interface JwtHeader {
27+
alg: string;
28+
typ: string;
29+
}
30+
31+
/**
32+
* Options for JWT verification
33+
* @interface VerifyOptions
34+
* @property {string[]} [algorithms] - List of allowed signing algorithms
35+
* @property {string} [issuer] - Expected token issuer
36+
* @property {string} [audience] - Expected token audience
37+
* @property {number} [clockTolerance] - Time tolerance in seconds for expiration checks
38+
* @property {number} [maxTokenAge] - Maximum allowed age of the token in seconds
39+
* @property {string} [subject] - Expected token subject
40+
* @property {string} [typ] - Expected token type
41+
*/
42+
export interface VerifyOptions {
43+
algorithms?: string[];
44+
issuer?: string;
45+
audience?: string;
46+
clockTolerance?: number;
47+
maxTokenAge?: number;
48+
subject?: string;
49+
typ?: string;
50+
}
51+
52+
/**
53+
* Internal state for JWT signing operations
54+
*/
55+
export interface SignJWTState {
56+
payload: JwtPayload;
57+
header: JwtHeader;
58+
issuedAt?: number;
59+
expirationTime?: number;
60+
}
61+
62+
/**
63+
* Internal state for JWT verification operations
64+
*/
65+
export interface VerifyJWTState {
66+
token: string;
67+
options: VerifyOptions;
68+
}

0 commit comments

Comments
 (0)