Skip to content

Support orgs for refresh tokens#678

Merged
markusahlstrand merged 2 commits intomainfrom
refresh-token-org
Apr 1, 2026
Merged

Support orgs for refresh tokens#678
markusahlstrand merged 2 commits intomainfrom
refresh-token-org

Conversation

@markusahlstrand
Copy link
Copy Markdown
Owner

@markusahlstrand markusahlstrand commented Apr 1, 2026

Summary by CodeRabbit

  • New Features

    • Organization switching during token refresh: specify a target organization or preserve the login-session organization; refreshed tokens reflect the chosen organization.
  • Documentation

    • Expanded OAuth token endpoint docs and added guides describing the optional organization parameter, expected behaviors, and examples across docs.
  • Tests

    • Added tests covering organization-aware refresh flows, successful switches, preservation, and error scenarios.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 1, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
authero-docs Ready Ready Preview, Comment Apr 1, 2026 10:31am
authhero-react-admin Ready Ready Preview, Comment Apr 1, 2026 10:31am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

📝 Walkthrough

Walkthrough

A new feature enables organization switching during OAuth refresh-token exchanges via an optional organization parameter; the implementation resolves the effective organization from the request or prior login session, validates existence and membership, and returns the resolved organization in the grant result. Docs and tests were added/updated.

Changes

Cohort / File(s) Summary
Changeset
\.changeset/giant-mice-dance.md
Added a changeset announcing minor releases for authhero and @authhero/docs with message "Support organizations for refresh tokens".
Documentation
apps/docs/api/endpoints.md, apps/docs/auth0-comparison/index.md, apps/docs/entities/identity/organizations.md, apps/docs/entities/security/tokens.md, apps/docs/features/authentication-flows.md
Documented optional organization parameter for POST /oauth/token (refresh_token grant), described behaviors (use param or fallback to login session), membership requirement, request/response examples, and updated expires_in example.
Auth Flow Implementation
packages/authhero/src/authentication-flows/refresh-token.ts
Extended refreshTokenParamsSchema with optional organization; resolve effectiveOrganization from request or login session; fetch and validate organization existence; verify user membership; include resolved organization in grant result; throw 400/403 on invalid org or missing membership.
Token Endpoint Schema
packages/authhero/src/routes/auth-api/token.ts
Updated token request validation schema to accept optional organization for the refresh_token grant path.
Integration Tests
packages/authhero/test/routes/auth-api/token.spec.ts
Added tests covering organization preservation when omitted, switching when provided, 400 for nonexistent org, 403 for non-membership, and omission of org claims when none present.

Sequence Diagram

sequenceDiagram
    participant Client
    participant TokenEndpoint as Token Endpoint
    participant RefreshGrant as Refresh Grant Handler
    participant OrgStore as Organizations/Data Layer

    Client->>TokenEndpoint: POST /oauth/token (grant_type=refresh_token, organization=?)
    TokenEndpoint->>RefreshGrant: Parse & validate request
    RefreshGrant->>RefreshGrant: Determine effectiveOrganization (param or login session)
    alt organization provided
        RefreshGrant->>OrgStore: Lookup organization by id/name
        OrgStore-->>RefreshGrant: Organization found / not found
        alt found
            RefreshGrant->>OrgStore: Verify user membership
            OrgStore-->>RefreshGrant: Member / not member
            alt member
                RefreshGrant-->>TokenEndpoint: Grant result with resolved organization
            else not member
                RefreshGrant-->>TokenEndpoint: Error 403 access_denied
            end
        else not found
            RefreshGrant-->>TokenEndpoint: Error 400 invalid_request
        end
    else no organization provided
        RefreshGrant-->>TokenEndpoint: Grant result using login session org (or none)
    end
    TokenEndpoint->>Client: 200 OK (access_token, id_token, org claims as applicable)
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly Related Issues

Possibly Related PRs

  • first step in refresh token refactor #595: Modifies packages/authhero/src/authentication-flows/refresh-token.ts with related login_id/refresh-token linkage changes that overlap with this PR's refresh-token flow edits.
  • Add scopes and permissions #206: Adds organization-aware token handling across grant flows, aligning with this PR's goal of propagating organization in token results.
  • Add org support to token endpoint #218: Implements organization support for the authorization_code grant, applying similar schema/validation patterns to a different OAuth flow.

Poem

🐰
I nibble tokens, soft and light,
Switch organizations in one hop tonight,
A nibble here, a hop to there,
Tokens bloom with org claims fair,
Hooray — fresh tokens, safe and bright!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Support orgs for refresh tokens' clearly and concisely summarizes the main change: adding organization support to the refresh token grant flow.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refresh-token-org

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
Copy Markdown
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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/authhero/src/authentication-flows/refresh-token.ts`:
- Line 16: The schema currently allows an empty organization string via
organization: z.string().optional(), which lets "" bypass the login-session org
fallback; update the schema to reject blank values by using
z.string().nonempty().optional() (or z.string().min(1).optional()) for the
organization field in refresh-token input schema so empty strings are treated as
invalid and the fallback logic (the code referencing organization with ??) will
work as intended.
- Around line 89-107: The code currently attaches an organization to the grant
result after only checking that the organization exists (effectiveOrganization →
ctx.env.data.organizations.get) which can let tokens be issued without verifying
the subject is a member; ensure membership is validated whenever an organization
is set by adding an unconditional membership check: after resolving
effectiveOrganization in refreshTokenGrant (the block that uses
ctx.env.data.organizations.get) call the memberships lookup used elsewhere
(e.g., ctx.env.data.memberships.get or the same membership check logic used in
packages/authhero/src/routes/auth-api/token.ts) to confirm the user
(loginSession.subject or grantResult.subject) belongs to that organization, and
throw a 400/401 JSONHTTPException if not; alternatively, if you prefer
centralizing the check, move the unconditional membership validation into the
token route just before createAuthTokens so createAuthTokens never receives an
organization unless membership has been verified.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f069c154-4d17-4211-aa90-ca21fd662766

📥 Commits

Reviewing files that changed from the base of the PR and between ea2a16f and 4e95762.

📒 Files selected for processing (9)
  • .changeset/giant-mice-dance.md
  • apps/docs/api/endpoints.md
  • apps/docs/auth0-comparison/index.md
  • apps/docs/entities/identity/organizations.md
  • apps/docs/entities/security/tokens.md
  • apps/docs/features/authentication-flows.md
  • packages/authhero/src/authentication-flows/refresh-token.ts
  • packages/authhero/src/routes/auth-api/token.ts
  • packages/authhero/test/routes/auth-api/token.spec.ts

@markusahlstrand markusahlstrand changed the title Support orgs for refresh tokesn Support orgs for refresh tokens Apr 1, 2026
@markusahlstrand
Copy link
Copy Markdown
Owner Author

Implements #677

Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
packages/authhero/src/authentication-flows/refresh-token.ts (1)

109-119: Consider using fetchAll() to handle pagination for users with >1000 organization memberships.

The current hardcoded per_page: 1000 limit could miss memberships if a user belongs to more than 1000 organizations. Instead of a static limit, use the existing fetchAll() utility (available from the authhero package) to properly paginate through all results:

const userOrgs = await fetchAll<Organization>(
  (params) => ctx.env.data.userOrganizations.listUserOrganizations(client.tenant.id, user.user_id, params),
  'organizations'
);

const isMember = userOrgs.some((org) => org.id === organization!.id);

This pattern is also used elsewhere in the codebase (e.g., packages/multi-tenancy/src/routes/tenants.ts) for similar membership checks and properly handles pagination without a fixed limit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/authhero/src/authentication-flows/refresh-token.ts` around lines 109
- 119, Replace the hardcoded paginated call to
ctx.env.data.userOrganizations.list (which sets per_page: 1000) with the
fetchAll helper to ensure all memberships are retrieved; specifically, call
fetchAll with a function that invokes listUserOrganizations (passing
client.tenant.id and user.user_id) to collect all organizations into userOrgs,
then compute isMember by checking whether any organization in that returned
array matches organization!.id (replace the existing
userOrgs.userOrganizations.some(...) logic). Ensure you import/require fetchAll
if not already available.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/authhero/src/authentication-flows/refresh-token.ts`:
- Around line 109-119: Replace the hardcoded paginated call to
ctx.env.data.userOrganizations.list (which sets per_page: 1000) with the
fetchAll helper to ensure all memberships are retrieved; specifically, call
fetchAll with a function that invokes listUserOrganizations (passing
client.tenant.id and user.user_id) to collect all organizations into userOrgs,
then compute isMember by checking whether any organization in that returned
array matches organization!.id (replace the existing
userOrgs.userOrganizations.some(...) logic). Ensure you import/require fetchAll
if not already available.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a6d6797c-df0a-4143-ac6e-5f68ec2c0546

📥 Commits

Reviewing files that changed from the base of the PR and between 4e95762 and c5fa4a7.

📒 Files selected for processing (2)
  • apps/docs/auth0-comparison/index.md
  • packages/authhero/src/authentication-flows/refresh-token.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/docs/auth0-comparison/index.md

@markusahlstrand markusahlstrand merged commit fa38c62 into main Apr 1, 2026
5 checks passed
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.

1 participant