Skip to content

sasl: wire OAUTHBEARER (RFC 7628) alongside IRCV3BEARER#202

Merged
ValwareIRC merged 4 commits into
mainfrom
feat/sasl-oauthbearer
May 11, 2026
Merged

sasl: wire OAUTHBEARER (RFC 7628) alongside IRCV3BEARER#202
ValwareIRC merged 4 commits into
mainfrom
feat/sasl-oauthbearer

Conversation

@ValwareIRC
Copy link
Copy Markdown
Contributor

@ValwareIRC ValwareIRC commented May 11, 2026

Summary

`buildOauthBearerPayload` has existed in src/lib/saslFrames.ts since the OAuth feature landed, but it had no call site. The `SaslMech` union, `chooseMechanism`, and the AUTHENTICATE dispatch only knew `IRCV3BEARER`. So on networks that advertise the SASL-WG `OAUTHBEARER` (RFC 7628) but not the obbyircd-style `IRCV3BEARER`, OAuth login silently fell off the mech-selection path.

Changes

  • Add `OAUTHBEARER` to the `SaslMech` union.
  • Primary-mech selection prefers `IRCV3BEARER` (richer: carries `token_type` + provider hint, used for 2FA step-up) and falls back to `OAUTHBEARER` if only the latter is advertised.
  • Stash the server host/port on the `SaslSession` so we can populate the optional GS2 `host=`/`port=` keys when sending the bearer.
  • Dispatch branch builds the RFC 7628 GS2 payload via `buildOauthBearerPayload` and chunks it into 400-byte AUTHENTICATE blocks just like every other multi-line mech.

2FA step-up remains `IRCV3BEARER`-only since OAUTHBEARER doesn't carry the `token_type` + provider info obbyircd needs to route the bearer through opaque/userinfo resolution. Servers that only advertise OAUTHBEARER don't have obbyircd's step-up flow anyway.

Test plan

  • OAuth login on a server advertising `IRCV3BEARER` still uses `IRCV3BEARER` (obbyircd path) and reaches 903.
  • OAuth login on a server advertising `OAUTHBEARER` only succeeds via the new dispatch branch.
  • Server-side trace shows the GS2 envelope: `n,a=,\x01host=\x01port=\x01auth=Bearer \x01\x01`
  • 2FA step-up after SCRAM/PLAIN primary still picks IRCV3BEARER (since OAUTHBEARER can't carry the step-up routing info).

Summary by CodeRabbit

  • New Features
    • Added support for OAuth-only authentication with improved mechanism fallback
    • Enhanced authentication to intelligently select the optimal OAuth method based on server capabilities

Review Change Stack

The cap was originally REQ'd in commit 25bc958 (May 2025) but was
silently dropped during the refactor in 3508ddf and has been missing
ever since. We populate the member list from WHO/WHOX on JOIN, so the
implicit RPL_NAMREPLY burst is wasted bandwidth.

Request both the ratified `no-implicit-names` cap and the original
`draft/no-implicit-names` name for back-compat with servers that
haven't moved to the ratified form yet.
Ergo follows IRCv3 SASL-3.1 literally: the client must send
"AUTHENTICATE +" to indicate "no more data from me" before Ergo emits
903. Without that ack the SASL session hangs and the connection times
out. UnrealIRCd, by contrast, immediately follows server-final with
either 903 or "AUTHENTICATE 2FA-REQUIRED"; in the latter case our "+"
would be read as an empty TOTP code and trip 904 (regression that
prompted b096e0b "Don't send spurious AUTHENTICATE +").

Schedule the ack 750 ms after server-final and skip if either:
  - CAP negotiation has completed (903 already arrived)
  - The 2FA step-up handler marked stepupStarted on the session
An epoch counter on the session guards against a fresh SASL run on
the same serverId firing the stale timer.

Reported on irc.h4ks.com (Ergo); confirmed unaffected on obbyircd.
The buildOauthBearerPayload frame builder has existed in saslFrames.ts
since the OAuth feature landed, but nothing called it: the SaslMech
union, chooseMechanism, and the AUTHENTICATE dispatch all only knew
IRCV3BEARER. On networks that only advertise the SASL-WG OAUTHBEARER
(RFC 7628) -- as opposed to obbyircd-style IRCV3BEARER -- OAuth login
silently failed.

- Add "OAUTHBEARER" to SaslMech.
- Primary-mech selection prefers IRCV3BEARER (richer: token_type +
  provider hint, used for 2FA step-up) and falls back to OAUTHBEARER
  if only the latter is advertised.
- Stash the server host/port on the SaslSession so we can populate the
  optional gs2-header host=/port= keys at AUTHENTICATE time.
- Dispatch branch builds the RFC 7628 GS2 payload and chunks it into
  400-byte AUTHENTICATE blocks like every other multi-line mech.

2FA step-up remains IRCV3BEARER-only because OAUTHBEARER doesn't carry
the token_type / provider info obbyircd needs to route the bearer.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 11, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This change extends the authentication handler to support RFC 7628 OAUTHBEARER as a SASL mechanism for OAuth-only logins. During SASL negotiation, the code chooses between IRCV3BEARER (if advertised) or OAUTHBEARER (fallback), captures server host/port hints, and later constructs GS2-framed OAuth bearer payloads in the AUTHENTICATE handler.

Changes

OAUTHBEARER SASL Mechanism Support

Layer / File(s) Summary
SASL Session Data Model
src/store/handlers/auth.ts
SASL mechanism types expanded to include OAUTHBEARER; SaslSession interface adds optional oauthHost and oauthPort fields for RFC 7628 framing hints.
OAuth Mechanism Selection During CAP SASL
src/store/handlers/auth.ts
OAuth-only negotiation path chooses between IRCV3BEARER (preferred) and OAUTHBEARER (fallback) based on server advertisement; initializes session with mechanism-specific OAuth state including bearer token, token kind, provider, and host/port hints.
General SASL Session Initialization
src/store/handlers/auth.ts
Stashes OAuth bearer context and server hints into session only when OAuth is active and a compatible OAuth mechanism is available, aligning step-up context with the selected mechanism.
AUTHENTICATE Event Handler for OAUTHBEARER
src/store/handlers/auth.ts
New handler branch for OAUTHBEARER mechanism: on server +, constructs GS2-framed RFC 7628 OAuth bearer payload with optional authzid and host/port hints, then sends in chunked AUTHENTICATE blocks.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Server
  participant AuthHandler
  
  Client->>Server: CAP REQ sasl
  Server->>Client: CAP ACK sasl (advertises mechanisms)
  
  AuthHandler->>AuthHandler: Check OAuth bearer + advertised mechs
  alt IRCV3BEARER advertised
    AuthHandler->>AuthHandler: Select IRCV3BEARER
  else OAUTHBEARER advertised
    AuthHandler->>AuthHandler: Select OAUTHBEARER
  end
  
  AuthHandler->>AuthHandler: Capture oauthHost, oauthPort from config
  AuthHandler->>AuthHandler: Initialize SaslSession with chosen mech
  
  AuthHandler->>Server: AUTHENTICATE <chosen-mech>
  Server->>Client: +
  
  alt mech === OAUTHBEARER
    AuthHandler->>AuthHandler: Build GS2-framed payload (bearer token + hints)
    AuthHandler->>AuthHandler: Chunk payload
    AuthHandler->>Server: AUTHENTICATE <chunk>
  end
  
  Server->>Client: SASL negotiation completes
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A bearer token hops through frames so true,
GS2-framed OAuth, fresh and new!
When IRCV3 won't show its face,
OAUTHBEARER takes its place.
RFC 7628 shines with delight,
Authentication, perfectly right! 🌟

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: adding OAUTHBEARER (RFC 7628) support alongside existing IRCV3BEARER in the SASL mechanism handler, which is the primary focus of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 feat/sasl-oauthbearer

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.

@github-actions
Copy link
Copy Markdown

Pages Preview
Preview URL: https://feat-sasl-oauthbearer.obsidianirc.pages.dev

Automated deployment preview for the PR in the Cloudflare Pages.

@ValwareIRC ValwareIRC merged commit d80339c into main May 11, 2026
3 of 4 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.

2 participants