Skip to content

feat: Add optional issuers check to session authentication - #9

Merged
gjtorikian merged 2 commits into
mainfrom
devin/1789066174-optional-issuer
Sep 17, 2026
Merged

gjtorikian merged 2 commits into
mainfrom
devin/1789066174-optional-issuer

Conversation

@m0tzy

@m0tzy m0tzy commented Sep 10, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Android counterpart of workos/workos-kotlin#433: lets callers require the access token's iss claim to match an allowlist, with no change in behavior when it is not supplied.

// hand-maintained helpers/Session.kt
internal class JwksVerifier(baseUrl, clientId, issuers: List<String>? = null)

// existing two-arg entry points are kept as-is (delegate with issuers = null)
fun Session.loadSealedSession(sessionData, cookiePassword): SessionCookie
fun Session.loadSealedSession(sessionData, cookiePassword, issuers: List<String>?): SessionCookie
suspend fun Session.authenticateWithSessionCookie(sessionData, cookiePassword)
suspend fun Session.authenticateWithSessionCookie(sessionData, cookiePassword, issuers: List<String>?)

The check runs after DefaultJWTProcessor.process() (signature + exp/nbf) on the verified claims, so it is an authenticated issuer check rather than a compare on an unverified payload. Semantics match the Kotlin SDK:

  • no issuers / null → issuer not checked, identical to today
  • iss in issuers → accepted; exact, case-sensitive match
  • iss mismatched or absent, or issuers empty → INVALID_JWT
  • the list is snapshotted (issuers?.toList()) so later mutation by the caller has no effect
  • enforced by authenticate() only; refresh() is untouched

WorkOS mints different iss shapes per environment (https://api.workos.com, https://api.workos.com/user_management/<clientId>, custom auth domains), so there is deliberately no default; the KDoc tells callers to pass the exact value(s) their tokens carry.

Binary compatibility: the new parameter is added as a separate overload rather than a default argument, so the previously published JVM descriptors loadSealedSession(String, String) and authenticateWithSessionCookie(String, String, Continuation) are unchanged (verified with javap). JwtVerifier (internal) and SessionCookie's constructor are unchanged.

Tests: JwksVerifier null/match/multi/mismatch/missing-iss/empty-list/snapshot cases against the in-process JWKS server, plus an end-to-end authenticateWithSessionCookie/loadSealedSession pass-through. ./script/ci passes locally (ktlint + 44 session tests).

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

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')}

workos/authkit-react-router#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/main/kotlin/com/workos/android/helpers/Session.kt Outdated
@devin-ai-integration devin-ai-integration Bot changed the title Add optional issuers check to session authentication feat: Add optional issuers check to session authentication Sep 10, 2026
@greptile-apps

greptile-apps Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the previously reported JVM API compatibility break is fully fixed by explicit two-argument overloads.

Summary

  • Snapshots the optional issuer list and validates iss only after normal JWT processing succeeds.
  • Passes issuer configuration through loadSealedSession and authenticateWithSessionCookie.
  • Restores explicit two-argument overloads to preserve compatibility with existing Kotlin, Java, and compiled JVM consumers.
  • Adds verifier and end-to-end coverage for disabled, matching, mismatching, missing, empty, multiple, and mutated issuer lists.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Session API receives sealed session] --> B[Create JwksVerifier]
    B --> C[Unseal session cookie]
    C --> D[DefaultJWTProcessor verifies JWT]
    D -->|Verification fails| E[INVALID_JWT]
    D -->|Verification succeeds| F{Issuer list configured?}
    F -->|No| G[Authentication succeeds]
    F -->|Yes| H{Verified iss exactly matches allowlist?}
    H -->|Yes| G
    H -->|No or missing| E
Loading

Reviews (2) · Last reviewed commit: "Preserve two-argument Session helper ove..."

Comment thread src/main/kotlin/com/workos/android/helpers/Session.kt Outdated
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@gjtorikian gjtorikian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks!

@gjtorikian
gjtorikian merged commit fa57545 into main Sep 17, 2026
8 checks passed
This was referenced Sep 17, 2026
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.

2 participants