Skip to content

feat(server): introduce a token issuer abstraction#4893

Open
nabokihms wants to merge 1 commit into
masterfrom
token-issuer-proto
Open

feat(server): introduce a token issuer abstraction#4893
nabokihms wants to merge 1 commit into
masterfrom
token-issuer-proto

Conversation

@nabokihms

@nabokihms nabokihms commented Jul 14, 2026

Copy link
Copy Markdown
Member

Overview

A token-issuance core for the server package. Every grant reduces to producing an Authorization, and the issuer turns it into a TokenSet.

What this PR does / why we need it

Token issuance was spread across every grant as long positional calls to Server.newIDToken/newAccessToken, plus refresh-token creation that was copy-pasted between two grants. token_issuer.go gives it a small domain:

  • Authorization (the authenticated subject and the client permission) and TokenSet (the tokens produced from it) value objects.
  • tokenSigner for access and id JWT minting, owning cross-client audience resolution internally.
  • refreshTokens for refresh-token persistence, unifying the create-and-record-offline-session logic that the authorization_code and password grants had copied. Rollback on a failed offline-session write is preserved.
  • tokenIssuer.Issue(auth, wantRefresh) composing them, with writeTokenResponse as a separate transport helper.

Every grant is migrated: password, client_credentials, token_exchange, authorization_code, the approval implicit/hybrid path and the refresh grant. Server.newIDToken/newAccessToken are removed; callers and tests build an Authorization and go through the signer. Unit tests cover the issuer and the refresh store.

Special notes for your reviewer

One deliberate behaviour change. Unifying refreshTokens.create aligns the password grant with the authorization_code grant's connector-data handling:

  • Password-issued refresh tokens now persist ConnectorData (previously left empty). In the common case this is a no-op: the refresh token and the offline session get the same identity.ConnectorData, and the refresh grant reads the same value from either.
  • The offline-session update is now conditional (if len(ConnectorData) > 0) instead of unconditionally overwriting, so an empty re-auth no longer clobbers previously stored connector data. This matches both the authorization_code grant and the refresh grant's own read logic.

Refresh rotation (updateRefreshToken) and the refreshContext removal, which are entangled with introspection's shared token lookup, come in a follow-up.

@nabokihms nabokihms changed the title wip(server): token issuer prototype — Authorization → TokenSet wip(server): token issuer — Authorization → TokenSet Jul 14, 2026
@nabokihms nabokihms changed the title wip(server): token issuer — Authorization → TokenSet feat(server): introduce a token issuer abstraction Jul 14, 2026
@nabokihms nabokihms marked this pull request as ready for review July 14, 2026 21:31
@nabokihms nabokihms force-pushed the token-issuer-proto branch from fcf9f5c to f144e45 Compare July 14, 2026 21:31
@nabokihms nabokihms added the release-note/enhancement Release note: Enhancements label Jul 14, 2026
Token issuance was spread across every grant as long positional calls to
Server.newIDToken/newAccessToken plus copy-pasted refresh-token creation. This
introduces a small domain around it:

  Authorization  the authenticated subject and the client permission
  TokenSet       the tokens produced from it
  tokenSigner    access and id JWT minting, owning cross-client audience
  refreshTokens  refresh-token persistence and offline-session bookkeeping
  tokenIssuer    composes the above; writeTokenResponse handles transport

Every grant now builds an Authorization and hands it to the issuer: password,
client_credentials, token_exchange, authorization_code, the approval
implicit/hybrid path and the refresh grant. Server.newIDToken/newAccessToken are
gone; callers and tests build an Authorization and go through the signer.

refreshTokens.create unifies the create-and-record-offline-session logic that
the authorization_code and password grants had copied with a subtle divergence:
password now persists connector data on the refresh token and stops overwriting
a stored offline session with empty connector data, matching the
authorization_code grant and the refresh grant's own read path.

Refresh rotation and the refreshContext removal are a separate change.

Signed-off-by: maksim.nabokikh <max.nabokih@gmail.com>
@nabokihms nabokihms force-pushed the token-issuer-proto branch from f144e45 to d94e6bb Compare July 14, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/enhancement Release note: Enhancements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant