Skip to content

feat(auth): add AlbertSSOTokenExchange for server-to-server OIDC token exchange#509

Open
prasad-albert wants to merge 4 commits into
mainfrom
feat/sso-token-exchange
Open

feat(auth): add AlbertSSOTokenExchange for server-to-server OIDC token exchange#509
prasad-albert wants to merge 4 commits into
mainfrom
feat/sso-token-exchange

Conversation

@prasad-albert
Copy link
Copy Markdown
Collaborator

@prasad-albert prasad-albert commented May 19, 2026

Summary

  • Adds AlbertSSOTokenExchange auth manager — exchanges an OIDC ID token for an Albert access token without browser interaction, using POST /api/v3/login/sso/exchange
  • Works with any OIDC-compliant IdP that emits preferred_username (Okta, Auth0, Entra ID, Cognito, Keycloak, etc.); uses expires_in from response to schedule refresh (falls back to 55 min until backend ships the field)
  • Albert.from_sso_exchange() and AsyncAlbert.from_sso_exchange() factory methods added; AlbertSSOTokenExchange exported from top-level albert package
  • Docs: new sso_exchange.md API reference, OIDC Token Exchange section in authentication.md with provider compatibility table, Google claim-mapping warning, SAML exclusion note, and usage examples

@claude
Copy link
Copy Markdown

claude Bot commented May 19, 2026

Code Review

Issues Found

  • [plans/sso-token-exchange.md] Planning artifact committed to the repo. The plans/ directory is not part of the project structure defined in AGENTS.md and this document should not be merged into the main branch.

  • [src/albert/core/auth/sso_exchange.py:74] response.json() is called outside the handle_http_errors() context manager, deviating from the pattern in AlbertSSOClient._request_access_token() where .json() is inside the with block. If the server returns a 200 with a non-JSON body, this raises a bare JSONDecodeError instead of being wrapped consistently with the rest of the auth layer.

Summary

2 issues found. Neither is runtime-critical, but the plans/ doc should be removed before merge and the response.json() call should be moved inside the handle_http_errors() block to match the existing pattern.

Adds a new auth strategy for applications that authenticate users via
Azure AD and want to access the Albert API without browser interaction.

- AlbertSSOTokenExchange exchanges an Azure AD ID token for an Albert
  access token via POST /api/v3/login/sso/exchange; accepts an
  oidc_token_provider callable so tokens are re-fetched on renewal
- Uses expires_in from response to schedule refresh; falls back to
  55 min until backend ships the field
- Albert.from_sso_exchange() and AsyncAlbert.from_sso_exchange() factory
  methods; AlbertSSOTokenExchange exported from top-level albert package
- Docs: sso_exchange.md API reference, Azure AD section in
  authentication.md with prerequisites and warnings, mkdocs.yml nav entry
@prasad-albert prasad-albert force-pushed the feat/sso-token-exchange branch from 75d62e8 to a2dd94f Compare May 19, 2026 09:24
Remove Azure AD-specific framing — the exchange endpoint supports any IdP
that emits preferred_username (Okta, Auth0, Cognito, Keycloak, Ping, etc).
Add provider compatibility table, Google claim-mapping warning, and SAML
exclusion note in docs. Update code docstrings accordingly.
@prasad-albert prasad-albert changed the title feat(auth): add AlbertSSOTokenExchange for Azure AD server-to-server OIDC token exchange feat(auth): add AlbertSSOTokenExchange for server-to-server OIDC token exchange May 20, 2026
@prasad-albert
Copy link
Copy Markdown
Collaborator Author

Code Review

Issues Found

  • [plans/sso-token-exchange.md] Planning artifact committed to the repo. The plans/ directory is not part of the project structure defined in AGENTS.md and this document should not be merged into the main branch.
  • [src/albert/core/auth/sso_exchange.py:74] response.json() is called outside the handle_http_errors() context manager, deviating from the pattern in AlbertSSOClient._request_access_token() where .json() is inside the with block. If the server returns a 200 with a non-JSON body, this raises a bare JSONDecodeError instead of being wrapped consistently with the rest of the auth layer.

Summary

2 issues found. Neither is runtime-critical, but the plans/ doc should be removed before merge and the response.json() call should be moved inside the handle_http_errors() block to match the existing pattern.

@claude all issues have been addressed.

@claude
Copy link
Copy Markdown

claude Bot commented May 20, 2026

Claude finished @prasad-albert's task in 48s —— View job


Verification Complete

  • Gather context
  • Verify plans/sso-token-exchange.md has been removed — confirmed, file no longer exists in the repo
  • Verify response.json() is inside handle_http_errors() block — confirmed, line 75 is now inside the with handle_http_errors(): block (lines 68–75 of sso_exchange.py)
  • Final review pass of sso_exchange.py

Both previously identified issues are resolved:

  1. Planning artifact removedplans/sso-token-exchange.md is gone from the branch.
  2. response.json() placement fixedresponse.json() (line 75) is now correctly inside the with handle_http_errors(): context manager alongside raise_for_status(), matching the pattern in the rest of the auth layer. Any non-JSON 200 response will now be handled consistently rather than raising a bare JSONDecodeError.

The code is clean and ready to merge.

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