You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: reorganize dynamic authentication by language
- Remove 'Alternative: Method overrides' section from TypeScript docs
- Create TypeScript-specific page with custom fetcher approach only
- Create Python-specific page with method override approach
- Convert main guide to lightweight overview hub with language cards
- Add navigation entries for both language-specific pages
- Keep language-agnostic considerations in overview
Addresses Swimburger's feedback to organize content by language.
Co-Authored-By: Chris McDonnell <[email protected]>
description: Implement dynamic authentication patterns like short-lived JWT signing in your SDKs.
4
4
---
5
5
6
-
Your API may require dynamic authentication where credentials need to be generated or refreshed for each request, such as signing short-lived JWTs or rotating tokens. Fern-generated SDKs support this pattern through custom fetcher middleware.
6
+
Your API may require dynamic authentication where credentials need to be generated or refreshed for each request, such as signing short-lived JWTs or rotating tokens. Fern-generated SDKs support this pattern through language-specific approaches.
7
7
8
-
## Recommended: Custom fetcher middleware
8
+
## Language-specific guides
9
9
10
-
The best way to implement dynamic authentication in TypeScript SDKs is to use a custom fetcher. This acts as middleware for all requests, allowing you to inject authentication logic in a single place without overriding individual methods.
10
+
Each language has its own recommended approach for implementing dynamic authentication:
Use method overrides to inject authentication logic for each API call. Supports JWT signing, OAuth token refresh, and more.
18
+
</Card>
19
+
</Cards>
13
20
14
-
When you enable `allowCustomFetcher` in your generator configuration, the generated SDK accepts a `fetcher` parameter in the client options. This fetcher wraps all HTTP requests, giving you a single injection point for authentication logic.
21
+
## Common use cases
15
22
16
-
### TypeScript example: Short-lived JWT signing
17
-
18
-
Here's how to implement JWT signing with token memoization:
19
-
20
-
<Steps>
21
-
22
-
### Enable custom fetcher in generator configuration
23
-
24
-
Add `allowCustomFetcher: true` to your `generators.yml`:
25
-
26
-
```yaml title="generators.yml"
27
-
default-group: local
28
-
groups:
29
-
local:
30
-
generators:
31
-
- name: fernapi/fern-typescript-node-sdk
32
-
version: 0.x.x
33
-
output:
34
-
location: local-file-system
35
-
path: ../generated/typescript
36
-
config:
37
-
allowCustomFetcher: true
38
-
```
39
-
40
-
### Create a custom fetcher with JWT signing
41
-
42
-
Create a fetcher function that wraps the default fetcher and injects JWT authentication:
43
-
44
-
```typescript title="src/wrapper/jwtFetcher.ts"
45
-
import * as jwt from "jsonwebtoken";
46
-
import { fetcher as defaultFetcher, type FetchFunction } from "../core/fetcher";
47
-
48
-
export function createJwtFetcher(privateKey: string): FetchFunction {
49
-
// Cache token to avoid regenerating on every request
50
-
let cachedToken: { value: string; expiresAt: number } | undefined;
51
-
52
-
return async (args) => {
53
-
const now = Math.floor(Date.now() / 1000);
54
-
55
-
// Regenerate token if expired or about to expire (within 2 seconds)
56
-
if (!cachedToken || cachedToken.expiresAt - 2 <= now) {
// Extract privateKey and pass all other options to the parent
93
-
const { privateKey, ...clientOptions } =options;
94
-
super({
95
-
...clientOptions,
96
-
fetcher: createJwtFetcher(privateKey),
97
-
});
98
-
}
99
-
}
100
-
```
101
-
102
-
<Note>
103
-
This pattern uses `ConstructorParameters` to infer the exact options type from the generated client, ensuring compatibility with all client options (headers, timeoutInSeconds, maxRetries, etc.) without hardcoding them. This keeps the wrapper future-proof as the generator adds new options.
104
-
</Note>
105
-
106
-
### Export the wrapper client
107
-
108
-
Update your `index.ts` to export the wrapper instead of the generated client:
<Note>This approach requires overriding each method individually, which can be tedious for large APIs. The custom fetcher approach is recommended for better maintainability.</Note>
200
-
201
-
## Python example: Short-lived JWT signing
202
-
203
-
For Python SDKs, you can use a similar method override pattern:
204
-
205
-
```python title="src/wrapper/client.py"
206
-
from .client import PlantStoreClient as FernClient
This same pattern works for other dynamic authentication scenarios:
23
+
Dynamic authentication is useful for several scenarios:
244
24
25
+
-**Short-lived JWT signing**: Generate and sign JWTs that expire after a short period (e.g., 15 seconds) for enhanced security
245
26
-**OAuth token refresh**: Automatically refresh expired access tokens before each request
246
27
-**HMAC (Hash-based Message Authentication Code) signing**: Sign requests with HMAC signatures based on request content
247
-
-**Rotating API keys**: Switch between multiple API keys based on rate limits
248
-
-**Time-based tokens**: Generate tokens that include timestamps or nonces
28
+
-**Rotating API keys**: Switch between multiple API keys based on rate limits or other criteria
29
+
-**Time-based tokens**: Generate tokens that include timestamps or nonces for replay protection
249
30
250
31
## Important considerations
251
32
252
-
### Custom fetcher requirements
253
-
254
-
-**Generator configuration**: The `allowCustomFetcher` option must be enabled in your `generators.yml` for the `fetcher` parameter to be available in `BaseClientOptions`
255
-
-**Import path**: Import the default fetcher from `../core/fetcher` (or the appropriate path in your generated SDK) to wrap it with your custom logic
256
-
-**Preserve all arguments**: When wrapping the default fetcher, ensure you pass through all arguments to maintain compatibility with the SDK's internal behavior
257
-
258
-
### Security considerations
33
+
When implementing dynamic authentication, keep these language-agnostic considerations in mind:
259
34
260
-
-**Server-side only**: Never expose private keys in browser environments. JWT signing with private keys should only be done in server-side code (Node.js, Deno, Bun)
261
-
-**Secure key storage**: Never hardcode private keys; use environment variables or secure key management systems
262
-
-**Avoid double authentication**: If your API already uses bearer token authentication in the Fern definition, be careful not to override the existing `Authorization` header. Consider using a different header name or conditionally setting the header
35
+
### Security
263
36
264
-
### Performance and concurrency
37
+
-**Secure key storage**: Never hardcode private keys or secrets; use environment variables or secure key management systems
38
+
-**Server-side only**: For JWT signing with private keys, ensure this is only done in server-side code, never in browser environments
39
+
-**Avoid double authentication**: If your API already uses bearer token authentication in the Fern definition, be careful not to override existing authentication headers
265
40
266
-
-**Token memoization**: Cache tokens to avoid regenerating them on every request. The example above caches tokens and refreshes them 2 seconds before expiration
267
-
-**Thread safety**: The memoization pattern shown is safe for concurrent requests in JavaScript's single-threaded event loop, but be mindful of race conditions in other environments
268
-
-**Grace period**: Refresh tokens slightly before they expire (e.g., 2 seconds early) to avoid edge cases where a token expires during request processing
41
+
### Performance
269
42
270
-
### Header merging
271
-
272
-
-**Preserve existing headers**: When injecting authentication headers, always spread existing headers to avoid overwriting headers set by the SDK or user
273
-
-**Header precedence**: Headers are merged in order: SDK defaults → client options → request options → custom fetcher. Your custom fetcher runs last and can override previous headers
43
+
-**Token caching**: Cache tokens to avoid regenerating them on every request, balancing security (shorter token lifetime) with performance (less frequent regeneration)
44
+
-**Grace period**: Refresh tokens slightly before they expire to avoid edge cases where a token expires during request processing
45
+
-**Concurrency**: Be mindful of race conditions when caching tokens in multi-threaded environments
274
46
275
47
### Time synchronization
276
48
277
-
-**Clock drift**: Be aware of potential clock drift between your client and server. Consider adding tolerance to your token expiration checks
278
-
-**Timestamp precision**: Use Unix timestamps (seconds since epoch) for `iat` and `exp` claims to match JWT standards
279
-
280
-
## Best practices
281
-
282
-
-**Use custom fetcher for TypeScript**: The custom fetcher approach is the most maintainable solution for TypeScript SDKs when `allowCustomFetcher` is available
283
-
-**Cache tokens appropriately**: Balance between security (shorter token lifetime) and performance (less frequent regeneration)
284
-
-**Handle errors gracefully**: Implement retry logic for authentication failures and token refresh errors
285
-
-**Test thoroughly**: Ensure your wrapper handles all edge cases, including concurrent requests, token expiration, and network failures
286
-
-**Monitor token usage**: Log token generation and refresh events to help debug authentication issues in production
49
+
-**Clock drift**: Be aware of potential clock drift between your client and server; consider adding tolerance to token expiration checks
50
+
-**Timestamp precision**: Use Unix timestamps (seconds since epoch) for JWT `iat` and `exp` claims to match standards
287
51
288
52
## See also
289
53
290
-
-[Adding custom code](/sdks/capabilities/custom-code) - Learn more about extending generated SDKs
54
+
-[Adding custom code](/sdks/custom-code) - Learn more about extending generated SDKs
0 commit comments