Skip to content

fix: Derive expected access token issuer from apiHostname - #85

Open
m0tzy wants to merge 1 commit into
mainfrom
devin/1788577943-configurable-issuer
Open

fix: Derive expected access token issuer from apiHostname#85
m0tzy wants to merge 1 commit into
mainfrom
devin/1788577943-configurable-issuer

Conversation

@m0tzy

@m0tzy m0tzy commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #83.

verifyAccessToken pinned the expected iss claim to the literal https://api.workos.com, so any deployment with a non-default apiHostname (e.g. api.workos-test.com) failed verification on every request and silently fell into the refresh path in updateSession — rotating the refresh token and emitting a new Set-Cookie on each loader run.

The expected issuer is now configurable and defaults to the API host the JWKS URL already comes from:

// src/session.ts
function getExpectedIssuer(): string {
  return getConfig('issuer') ?? `https://${getConfig('apiHostname')}`;
}
await jwtVerify(accessToken, JWKS, { issuer: getExpectedIssuer() });

New AuthKitConfig.issuer?: string option (env WORKOS_ISSUER) for environments whose tokens carry a different iss than the API host (the API can mint iss from a custom auth domain, or with a /sso/<clientId> / /convex/<clientId> path suffix), e.g.:

configure({ issuer: 'https://auth.example.com' });

Behavior with default config is unchanged (https://api.workos.com).

Test plan

  • New specs in session.spec.ts: default stays https://api.workos.com; WORKOS_API_HOSTNAME=api.workos-test.comhttps://api.workos-test.com; WORKOS_ISSUER wins over apiHostname.
  • npm test, npm run lint, npm run typecheck, npm run prettier pass locally.

Link to Devin session: https://app.devin.ai/sessions/0ee38e859a9849658a7cdb2d215d89a6
Open in Devin Desktop: https://app.devin.ai/desktop/session/0ee38e859a9849658a7cdb2d215d89a6?variant=devin
Requested by: @m0tzy

Make the issuer used to validate the access token `iss` claim
configurable via the new `issuer` option (WORKOS_ISSUER), defaulting to
https://${apiHostname} instead of a hardcoded https://api.workos.com.

Fixes #83

Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Original prompt from madison.packer

can we patch this SDK so that the issuer can be either by default (if not passed) or passed a specific issuer?

const issuer = opts.issuer ?? https://${getConfig('apiHostname')}

#83

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

Devin Review

Comment thread src/session.ts
// to jwtVerify here; doing so would reject every token.
const WORKOS_JWT_ISSUER = 'https://api.workos.com';
function getExpectedIssuer(): string {
return getConfig('issuer') ?? `https://${getConfig('apiHostname')}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Custom API origins reject valid sessions

With apiHttps: false or apiPort set, getExpectedIssuer still uses HTTPS and omits the port. Valid tokens for that API origin fail verification, forcing repeated session refreshes.

Prompt for agents
Update src/session.ts getExpectedIssuer so its derived default reflects the complete configured API origin, including apiHttps and apiPort, while preserving an explicit issuer override. Add session tests covering HTTP and custom-port API configurations. Keep the default WorkOS configuration at https://api.workos.com.
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@greptile-apps

greptile-apps Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes access-token issuer verification follow the configured WorkOS environment instead of always requiring https://api.workos.com.

  • Adds an optional issuer configuration value mapped to WORKOS_ISSUER.
  • Defaults the expected issuer to https://{apiHostname} while allowing an explicit override.
  • Adds coverage for the default hostname, a non-production API hostname, and explicit issuer precedence.
  • Documents the new configuration option in the README.

Confidence Score: 5/5

The PR appears safe to merge, with issuer configuration, precedence, tests, and documentation aligned.

No actionable failure remains: explicit issuers resolve through the existing generic configuration system, the default preserves current production behavior, and the changed verification paths are covered by focused tests.

Important Files Changed

Filename Overview
src/session.ts Replaces the fixed issuer with a lazily resolved explicit issuer or API-hostname-derived default.
src/interfaces.ts Adds the optional public issuer configuration contract and documents its environment mapping and default.
src/session.spec.ts Verifies default issuer behavior, custom API-host derivation, and explicit issuer precedence.
README.md Documents the new issuer option, WORKOS_ISSUER, and hostname-derived default.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Verify stored access token] --> B{Explicit issuer configured?}
    B -->|Yes| C[Use issuer / WORKOS_ISSUER]
    B -->|No| D[Use https:// + apiHostname]
    C --> E[jwtVerify with WorkOS JWKS]
    D --> E
    E -->|Valid| F[Return authenticated session]
    E -->|Invalid| G[Enter session refresh path]
Loading

Reviews (1): Last reviewed commit: "fix: Derive expected access token issuer..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

verifyAccessToken hardcodes iss as https://api.workos.com; non-default apiHostname refreshes on every request

1 participant