Skip to content

refactor: simplify token options API by flattening scope parameter - #4

Merged
developerkunal merged 4 commits into
mainfrom
refactor/simplify-token-options-api
Nov 7, 2025
Merged

developerkunal merged 4 commits into
mainfrom
refactor/simplify-token-options-api

Conversation

@developerkunal

Copy link
Copy Markdown
Contributor

Changes

This PR simplifies the token options API by flattening the scope parameter structure, following the same pattern as myaccount-js PR #4.

API Simplification:

  • Before: token: async ({ authorizationParams }) => { scope: authorizationParams.scope }
  • After: token: async ({ scope }) => { scope }

Classes and methods changed:

  • Auth0Token.TokenOptions interface - Flattened from nested structure to single scope: string property
  • Removed Auth0Token.TokenSupplierWithScopes and Auth0Token.SimpleTokenSupplier types
  • Consolidated into single Auth0Token.TokenSupplier type with optional parameter
  • createCoreTokenSupplier function - Simplified implementation (removed type guard logic)
  • Removed isTokenSupplierWithScopes type guard function

Files modified:

  • example.ts - Updated 3 examples with flattened scope structure
  • src/wrappers/MyOrganizationClient.ts - Updated JSDoc examples (6 occurrences)
  • src/wrappers/auth/Token.ts - Major refactoring of type system and implementation
  • tests/unit/wrappers/MyOrganizationClient.test.ts - Updated test expectations (2 occurrences)
  • tests/unit/wrappers/auth/Token.test.ts - NEW: Comprehensive test suite for Token module
  • .gitignore - Added docs/ to exclusions
  • .fernignore - Added .gitignore

Usage example (new API):

const client = new MyOrganizationClient({
  domain: 'your-tenant.auth0.com',
  token: async ({ scope }) => {
    return await auth0.getTokenSilently({
      authorizationParams: {
        scope: `openid profile email ${scope}`
      }
    });
  }
});

Benefits:

  1. More intuitive API - users can destructure { scope } directly
  2. Simpler type system - single TokenSupplier type instead of multiple variants
  3. Cleaner implementation - removed type guard logic
  4. Better developer experience - more natural JavaScript/TypeScript patterns

Breaking Change Migration:

// Old (v1.0.0-beta.0)
token: async ({ authorizationParams }) => {
  return await auth0.getTokenSilently({
    authorizationParams: {
      scope: `openid profile email ${authorizationParams.scope}`
    }
  });
}

// New
token: async ({ scope }) => {
  return await auth0.getTokenSilently({
    authorizationParams: {
      scope: `openid profile email ${scope}`
    }
  });
}

References

  • Following pattern from myaccount-js PR #4
  • API simplification to improve developer experience

Testing

All changes are fully tested and validated:

  • This change adds unit test coverage
  • This change adds integration test coverage

Test coverage:

  • ✅ All 199 unit tests passing (including MyOrganizationClient and Token tests)
  • ✅ All wire/integration tests passing
  • ✅ Build succeeds with no TypeScript errors
  • ✅ Added comprehensive Token.test.ts with 16 test cases covering:
    • Static string tokens
    • Simple function token suppliers
    • Async function token suppliers
    • Scope-aware token suppliers with scopes
    • Token suppliers with empty scopes
    • Multiple security schemes
    • Scope deduplication
    • Error handling for invalid suppliers
    • Metadata extraction edge cases

Manual testing:

  • Verified token supplier is called correctly with scope parameter
  • Verified backward compatibility path (functions can ignore optional parameter)
  • Validated all example code patterns work as documented

Checklist

- Flatten token options structure from { authorizationParams: { scope } } to { scope }
- Remove TokenSupplierWithScopes and SimpleTokenSupplier types in favor of single TokenSupplier
- Remove isTokenSupplierWithScopes type guard
- Simplify createCoreTokenSupplier to always call token supplier with scope when available
- Update all examples and documentation to use new flattened structure
- Add comprehensive Token.test.ts test suite
- Update .gitignore and .fernignore for better file management

This change makes the API more intuitive by allowing users to destructure
{ scope } directly instead of { authorizationParams: { scope } }. The SDK
always calls the function with a scope object when scopes are present, or
no argument when there are no scopes. JavaScript's flexibility allows
functions to ignore extra parameters, supporting both patterns seamlessly.

Following the same refactoring pattern as myaccount-js PR #4.
@developerkunal
developerkunal requested a review from a team as a code owner November 7, 2025 08:47
Update example functions to properly handle optional parameter:
- Use options?.scope with fallback to empty string
- Update getAccessToken function signature to accept optional parameter
- Fixes TypeScript errors from destructuring potentially undefined value
@developerkunal

Copy link
Copy Markdown
Contributor Author

@claude

@github-actions

github-actions Bot commented Nov 7, 2025

Copy link
Copy Markdown

Claude finished @developerkunal's task —— View job


Overall Assessment

❌ The PR contains a critical logic flaw in src/wrappers/auth/Token.ts:126-133 that causes inconsistent behavior and potential runtime errors for token suppliers. The implementation conditionally calls the token supplier with or without arguments based on whether scopes are present, which breaks the expected API contract and can cause destructuring errors.


Job Run | Branch

Comment thread src/wrappers/auth/Token.ts Outdated
Comment on lines +126 to +133
return async ({ endpointMetadata }) => {
if (isTokenSupplierWithScopes(tokenSupplier)) {
const scopes = extractScopesFromMetadata(endpointMetadata);
const scope = scopes.join(" ");
return await tokenSupplier({
authorizationParams: { scope },
});
const scopes = extractScopesFromMetadata(endpointMetadata);
const scope = scopes.join(" ");

if (scope) {
return await tokenSupplier({ scope });
}
return await (tokenSupplier as Auth0Token.SimpleTokenSupplier)();
return await tokenSupplier();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical Issue: Logic flaw in scope handling

The current implementation only calls tokenSupplier({ scope }) when scope is non-empty, but calls tokenSupplier() when scope is empty. This is problematic because:

  1. Type inconsistency: The function signature is (options?: TokenOptions), meaning it expects either an object parameter or no parameter. However, when called without arguments, functions that destructure { scope } will receive undefined for their parameter, not an empty object.

  2. Breaking change for existing code: Functions written as ({ scope }) => ... will fail when called with no arguments, since destructuring undefined throws an error.

  3. Inconsistent behavior: Token suppliers that check for the presence of the parameter will behave differently based on whether scopes are present or not.

Recommended fix:

if (typeof tokenSupplier === "function") {
    return async ({ endpointMetadata }) => {
        const scopes = extractScopesFromMetadata(endpointMetadata);
        const scope = scopes.join(" ");
        
        // Always call with options object for consistency
        return await tokenSupplier({ scope });
    };
}

This ensures:

  • Consistent behavior regardless of scope presence
  • Functions can safely destructure { scope }
  • The scope will be an empty string when no scopes are required, which is semantically correct

@developerkunal

Copy link
Copy Markdown
Contributor Author

@claude

@github-actions

github-actions Bot commented Nov 7, 2025

Copy link
Copy Markdown

Claude finished @developerkunal's task —— View job


Overall Assessment

✅ This PR successfully simplifies the token options API and is ready to merge after addressing minor documentation clarity issues. The core implementation is sound, well-tested, and the critical issue from the previous review has been correctly resolved.

Strengths:

  • Correct implementation: The token supplier is always called with { scope } object (line 138 in Token.ts), ensuring safe destructuring
  • Comprehensive testing: 16 new test cases with excellent coverage of edge cases
  • Consistent API: Flattened structure is more intuitive and follows established patterns
  • Backward compatibility: Functions without parameters work correctly due to JavaScript's flexible argument handling

Minor Issues (Documentation Clarity):

  • Some comments could be more precise about JavaScript's parameter handling mechanics
  • Test comments could better explain why parameter-less functions work with the API

Code Quality: ✅ Excellent

  • Clean refactoring that removes unnecessary complexity
  • Type safety maintained throughout
  • Proper error handling for invalid inputs

Security: ✅ No concerns identified

  • Token handling remains secure
  • No injection vulnerabilities introduced
  • Proper validation of token supplier types

Performance: ✅ No concerns identified

  • Simplified implementation is more efficient (removed type guard logic)
  • Scope deduplication using Set is optimal
  • No unnecessary allocations or operations

Breaking Change Management: ✅ Well documented

  • Clear migration guide provided in PR description
  • Examples updated consistently across all files

Job Run | Branch

Comment thread tests/unit/wrappers/auth/Token.test.ts
Comment thread example.ts
Comment thread src/wrappers/auth/Token.ts Outdated
Comment thread src/wrappers/MyOrganizationClient.ts Outdated
Comment thread tests/unit/wrappers/auth/Token.test.ts
- Clarify that JS ignores extra arguments for functions without parameters
- Make it explicit that SDK always passes { scope }, not conditional behavior
- Add comment explaining empty scope split edge case in deduplication test
- Add dedicated test for empty scope string edge case (split behavior)
- Address bot review feedback for better documentation precision
@developerkunal
developerkunal merged commit fb3c40b into main Nov 7, 2025
13 checks passed
@developerkunal
developerkunal deleted the refactor/simplify-token-options-api branch November 7, 2025 10:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants