-
Notifications
You must be signed in to change notification settings - Fork 3
docs: add dynamic authentication guide for JWT signing in SDKs #1788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
docs: add dynamic authentication guide for JWT signing in SDKs #1788
Conversation
Co-Authored-By: Chris McDonnell <[email protected]>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
|
||
| ### Use the client | ||
|
|
||
| Your users can now use the client with automatic JWT signing: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[FernStyles.Current] Avoid time-relative terms like 'now' that become outdated
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
| ## Best practices | ||
|
|
||
| - **Cache when possible**: If your tokens are valid for longer periods, cache them to avoid regenerating on every request | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'gracefully' if it's not important to the meaning of the statement.
| - **Cache when possible**: If your tokens are valid for longer periods, cache them to avoid regenerating on every request | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures | ||
| - **Secure key storage**: Never hardcode private keys; use environment variables or secure key management systems | ||
| - **Test thoroughly**: Ensure your wrapper handles all edge cases, including concurrent requests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'thoroughly' if it's not important to the meaning of the statement.
|
For TypeScript, this is the best approach:
The fetcher function should wrap There's no need for Proxy's. It's not maintainable to customize every client class, which is why you need to override the single Fetcher function that is reused everywhere. |
- Add custom fetcher middleware as recommended approach per Swimburger feedback - Replace Proxy pattern with simpler method override alternative - Add comprehensive gotchas and security considerations - Update all examples to use plant-themed content (PlantStoreClient, plants.get, etc.) - Fix Vale linting issues: remove time-relative 'now', define HMAC acronym - Add allowCustomFetcher configuration example - Include token memoization pattern with grace period - Document import paths, header merging, and thread safety considerations Co-Authored-By: Chris McDonnell <[email protected]>
|
|
||
| ## Alternative: Method overrides | ||
|
|
||
| If you cannot enable `allowCustomFetcher` or prefer a simpler approach, you can override individual methods: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚫 [vale] reported by reviewdog 🐶
[Microsoft.Contractions] Use 'can't' instead of 'cannot'.
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC (Hash-based Message Authentication Code) signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC (Hash-based Message Authentication Code) signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
|
|
||
| - **Use custom fetcher for TypeScript**: The custom fetcher approach is the most maintainable solution for TypeScript SDKs when `allowCustomFetcher` is available | ||
| - **Cache tokens appropriately**: Balance between security (shorter token lifetime) and performance (less frequent regeneration) | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures and token refresh errors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'gracefully' if it's not important to the meaning of the statement.
| - **Use custom fetcher for TypeScript**: The custom fetcher approach is the most maintainable solution for TypeScript SDKs when `allowCustomFetcher` is available | ||
| - **Cache tokens appropriately**: Balance between security (shorter token lifetime) and performance (less frequent regeneration) | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures and token refresh errors | ||
| - **Test thoroughly**: Ensure your wrapper handles all edge cases, including concurrent requests, token expiration, and network failures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'thoroughly' if it's not important to the meaning of the statement.
|
Thanks for the feedback, @Swimburger! I've updated the documentation to feature the custom fetcher approach as the recommended solution. Changes made:
The custom fetcher example wraps Note: The code examples were written based on research of the Fern generator codebase but haven't been tested end-to-end in an actual SDK project. Would appreciate if someone could verify the import paths and implementation details are correct before merging. |
| import { createJwtFetcher } from "./jwtFetcher"; | ||
|
|
||
| export class PlantStoreClient extends FernClient { | ||
| constructor(options: { privateKey: string; environment: string }) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make sure you accept the same options as FernClient's ctor does, minus the custom fetcher, and dynamically pass it along.
- Use ConstructorParameters to infer exact options type from generated client - Accept all client options (headers, timeout, maxRetries, etc.) except fetcher - Extract privateKey and pass remaining options to parent constructor - Add explanatory note about ConstructorParameters pattern - Update both custom fetcher and method override examples for consistency Addresses Swimburger's feedback about dynamically passing all options. Co-Authored-By: Chris McDonnell <[email protected]>
|
Good catch! I've updated the wrapper client constructor to accept all the same options as Changes made:
This ensures the wrapper accepts all client options (headers, timeoutInSeconds, maxRetries, logging, etc.) without hardcoding them, making it compatible with current and future generator versions. |
| ``` | ||
|
|
||
| <Note> | ||
| 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[FernStyles.Current] Avoid time-relative terms like 'future' that become outdated
|
|
||
| </Steps> | ||
|
|
||
| ## Alternative: Method overrides |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove this section
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Put the TypeScript guidance under the TypeScript documentation, and the Python under the Python documentation.
|
Thanks for the feedback! Before I make these structural changes, I want to confirm the desired organization: Proposed restructure:
Alternative: If you prefer no cross-language overview hub, I can delete Which approach would you prefer? And where exactly should I place the TypeScript and Python pages in the docs structure? |
|
Implement your proposed structure |
- 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]>
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC (Hash-based Message Authentication Code) signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC (Hash-based Message Authentication Code) signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
|
|
||
| - **Override all methods**: Ensure you override all API methods to maintain consistent authentication across your SDK | ||
| - **Cache tokens appropriately**: Balance between security (shorter token lifetime) and performance (less frequent regeneration) | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures and token refresh errors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'gracefully' if it's not important to the meaning of the statement.
| - **Override all methods**: Ensure you override all API methods to maintain consistent authentication across your SDK | ||
| - **Cache tokens appropriately**: Balance between security (shorter token lifetime) and performance (less frequent regeneration) | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures and token refresh errors | ||
| - **Test thoroughly**: Ensure your wrapper handles all edge cases, including concurrent requests, token expiration, and network failures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'thoroughly' if it's not important to the meaning of the statement.
| ``` | ||
|
|
||
| <Note> | ||
| 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[FernStyles.Current] Avoid time-relative terms like 'future' that become outdated
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC (Hash-based Message Authentication Code) signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
| This same pattern works for other dynamic authentication scenarios: | ||
|
|
||
| - **OAuth token refresh**: Automatically refresh expired access tokens before each request | ||
| - **HMAC (Hash-based Message Authentication Code) signing**: Sign requests with HMAC signatures based on request content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Acronyms] 'HMAC' has no definition.
| ## Best practices | ||
|
|
||
| - **Cache tokens appropriately**: Balance between security (shorter token lifetime) and performance (less frequent regeneration) | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures and token refresh errors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'gracefully' if it's not important to the meaning of the statement.
|
|
||
| - **Cache tokens appropriately**: Balance between security (shorter token lifetime) and performance (less frequent regeneration) | ||
| - **Handle errors gracefully**: Implement retry logic for authentication failures and token refresh errors | ||
| - **Test thoroughly**: Ensure your wrapper handles all edge cases, including concurrent requests, token expiration, and network failures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 [vale] reported by reviewdog 🐶
[FernStyles.Adverbs] Remove 'thoroughly' if it's not important to the meaning of the statement.
|
Done! I've implemented the restructure as proposed: Changes made:
Preview URLs:
All CI checks passing ✅ |
Add dynamic authentication guide for JWT signing in SDKs (restructured by language)
Summary
Adds comprehensive documentation for implementing dynamic authentication patterns (like short-lived JWT signing) in Fern-generated SDKs. The documentation is now organized by language per Swimburger's feedback:
Structure:
/sdks/deep-dives/dynamic-authentication) - Lightweight landing page with cards linking to language-specific guides/sdks/generators/typescript/dynamic-authentication) - Custom fetcher middleware approach with JWT signing, token memoization, and comprehensive best practices/sdks/generators/python/dynamic-authentication) - Method override approach with JWT signing exampleKey changes:
allowCustomFetcherconfigurationsdks.ymlConstructorParameterspattern for type safetyReview & Testing Checklist for Human
allowCustomFetcher: true, implement the example code, and verify it works correctly with actual API callsimport { fetcher as defaultFetcher, type FetchFunction } from "../core/fetcher"matches the actual import path in generated TypeScript SDKs withallowCustomFetcherenabledallowCustomFetcherand whether documentation should mention version requirementsTest Plan
allowCustomFetcher: trueenabledNotes