Skip to content

Conversation

rhamzeh
Copy link
Member

@rhamzeh rhamzeh commented Sep 12, 2025

…uration

This is a backport of openfga/python-sdk#213

Description

What problem is being solved?

How is it being solved?

What changes are made to solve it?

References

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

  • New Features

    • Python client now supports OAuth2 scopes for client-credentials authentication. You can provide scopes as a list or a space-delimited string; they’re automatically included in token requests across async and sync flows.
  • Bug Fixes

    • More robust issuer URL handling during token retrieval in the sync OAuth2 flow, improving reliability when constructing the token endpoint URL.
@rhamzeh rhamzeh requested a review from a team as a code owner September 12, 2025 18:13
Copy link
Contributor

coderabbitai bot commented Sep 12, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Adds optional scopes support to credentials and OAuth2 token requests across async and sync clients. Introduces a scopes property in CredentialConfiguration, includes scope in POST params when provided, and adjusts sync token URL derivation via issuer parsing. Updates and adds unit tests covering scopes as list or string and a sync test signature change.

Changes

Cohort / File(s) Summary
Credentials configuration (scopes support)
config/clients/python/template/src/credentials.py.mustache, config/clients/python/template/test/credentials_test.py.mustache
Adds scopes parameter to CredentialConfiguration.__init__, internal _scopes, and scopes property (getter/setter). Tests added for client_credentials with scopes as list and string.
OAuth2 async client (scope param)
config/clients/python/template/src/oauth2.py.mustache, config/clients/python/template/test/oauth2_test.py.mustache
Appends scope to token POST params when configured; joins lists with spaces. Tests assert scope handling, Authorization header, request shape, and token storage.
OAuth2 sync client (issuer parsing and scopes)
config/clients/python/template/src/sync/oauth2.py.mustache, config/clients/python/template/test/sync/oauth2_test.py.mustache
Builds token URL via self._credentials._parse_issuer(configuration.api_issuer) and adds optional scope to POST params (list joined). Converts one retry test to synchronous; adds two scope-handling tests validating request/response and client closure.

Sequence Diagram(s)

sequenceDiagram autonumber participant App as Application participant Cred as CredentialConfiguration participant OAuth as OAuth2 Client participant REST as Token Endpoint App->>Cred: Initialize (client_id, client_secret, issuer, audience, scopes?) App->>OAuth: get_authentication_header() note over OAuth: Build token request\n- issuer via parser (sync)\n- include scope if provided\n- list -> space-delimited OAuth->>REST: POST /oauth/token\n{grant_type, client_id, client_secret, audience, [scope]} REST-->>OAuth: {access_token, expires_in} OAuth-->>App: Authorization: Bearer <token> 
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks (3 passed)

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly and accurately summarizes the primary change—adding OAuth2 scopes parameter support to CredentialConfiguration—aligning with the PR objectives and the provided file-level summaries; it is concise, specific, and follows conventional commit style suitable for a reviewer scanning history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/python-scopes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
config/clients/python/template/src/oauth2.py.mustache (1)

72-78: Normalize scopes and avoid sending empty/invalid scope param

Guard against empty strings and support common iterables (tuple/set). Keeps payload clean across providers.

- # Add scope parameter if scopes are configured - if configuration.scopes is not None: - if isinstance(configuration.scopes, list): - post_params["scope"] = " ".join(configuration.scopes) - else: - post_params["scope"] = configuration.scopes + # Add scope parameter if scopes are configured (normalize and skip empty) + scopes = configuration.scopes + if scopes: + if isinstance(scopes, str): + scope_str = scopes.strip() + elif isinstance(scopes, (list, tuple, set)): + scope_str = " ".join(s for s in scopes if isinstance(s, str) and s.strip()) + else: + scope_str = str(scopes).strip() + if scope_str: + post_params["scope"] = scope_str
config/clients/python/template/src/sync/oauth2.py.mustache (1)

72-78: Mirror async normalization for scopes

Apply the same normalization/empty-skip logic here to keep clients consistent.

- # Add scope parameter if scopes are configured - if configuration.scopes is not None: - if isinstance(configuration.scopes, list): - post_params["scope"] = " ".join(configuration.scopes) - else: - post_params["scope"] = configuration.scopes + # Add scope parameter if scopes are configured (normalize and skip empty) + scopes = configuration.scopes + if scopes: + if isinstance(scopes, str): + scope_str = scopes.strip() + elif isinstance(scopes, (list, tuple, set)): + scope_str = " ".join(s for s in scopes if isinstance(s, str) and s.strip()) + else: + scope_str = str(scopes).strip() + if scope_str: + post_params["scope"] = scope_str
config/clients/python/template/src/credentials.py.mustache (1)

112-125: Optional: normalize on set to reduce caller burden

Trimming strings and filtering empty items helps avoid sending empty scope later.

 @scopes.setter def scopes(self, value): - """ - Update the scopes - """ - self._scopes = value + """ + Update the scopes (normalize; keep None for empty) + """ + if value is None: + self._scopes = None + elif isinstance(value, str): + self._scopes = value.strip() or None + elif isinstance(value, (list, tuple, set)): + cleaned = [s for s in value if isinstance(s, str) and s.strip()] + self._scopes = cleaned or None + else: + self._scopes = str(value).strip() or None
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 992ff16 and 54aeebb.

📒 Files selected for processing (6)
  • config/clients/python/template/src/credentials.py.mustache (3 hunks)
  • config/clients/python/template/src/oauth2.py.mustache (1 hunks)
  • config/clients/python/template/src/sync/oauth2.py.mustache (2 hunks)
  • config/clients/python/template/test/credentials_test.py.mustache (1 hunks)
  • config/clients/python/template/test/oauth2_test.py.mustache (1 hunks)
  • config/clients/python/template/test/sync/oauth2_test.py.mustache (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
config/**/*.mustache

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Validate mustache syntax and variable references across all template files, including CHANGELOG.md.mustache

Files:

  • config/clients/python/template/src/oauth2.py.mustache
  • config/clients/python/template/src/sync/oauth2.py.mustache
  • config/clients/python/template/test/credentials_test.py.mustache
  • config/clients/python/template/src/credentials.py.mustache
  • config/clients/python/template/test/oauth2_test.py.mustache
  • config/clients/python/template/test/sync/oauth2_test.py.mustache
config/**/*.{json,mustache}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never hardcode API keys or credentials in configuration or template files

Files:

  • config/clients/python/template/src/oauth2.py.mustache
  • config/clients/python/template/src/sync/oauth2.py.mustache
  • config/clients/python/template/test/credentials_test.py.mustache
  • config/clients/python/template/src/credentials.py.mustache
  • config/clients/python/template/test/oauth2_test.py.mustache
  • config/clients/python/template/test/sync/oauth2_test.py.mustache
🔇 Additional comments (10)
config/clients/python/template/src/sync/oauth2.py.mustache (1)

63-63: Good: derive token URL via issuer parser

Switching to _parse_issuer centralizes issuer handling and aligns with async client behavior.

config/clients/python/template/test/oauth2_test.py.mustache (2)

488-544: LGTM: covers scopes as list

Solid assertions on Authorization header and payload shape (including scope).


545-601: LGTM: covers scopes as string

Ensures space-delimited string is forwarded verbatim.

config/clients/python/template/src/credentials.py.mustache (2)

22-22: Docstring addition looks good

Parameter description is clear and matches behavior.


32-40: Constructor wiring is correct

scopes plumbed into _scopes without side effects.

config/clients/python/template/test/credentials_test.py.mustache (2)

134-151: LGTM: list scopes path validated

Confirms method and stored scopes.


152-169: LGTM: string scopes path validated

Covers alternate input type.

config/clients/python/template/test/sync/oauth2_test.py.mustache (3)

215-215: Good change: sync retry test converted to sync def

Matches synchronous client API.


266-321: LGTM: sync client with list scopes

Parallels async coverage and asserts scope in post params.


323-379: LGTM: sync client with string scopes

Covers string-input path thoroughly.

@rhamzeh rhamzeh added this pull request to the merge queue Sep 16, 2025
Merged via the queue into main with commit da284d3 Sep 16, 2025
19 checks passed
@rhamzeh rhamzeh deleted the feat/python-scopes branch September 16, 2025 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

4 participants