Skip to content

fix: decode and encode extras on presence messages (TP3i) - #2186

Merged
lawrence-forooghian merged 2 commits into
mainfrom
fix/presence-message-extras-TP3i
Feb 24, 2026
Merged

fix: decode and encode extras on presence messages (TP3i)#2186
lawrence-forooghian merged 2 commits into
mainfrom
fix/presence-message-extras-TP3i

Conversation

@AndyTWF

@AndyTWF AndyTWF commented Feb 12, 2026

Copy link
Copy Markdown
Contributor

Summary

ARTPresenceMessage inherits an extras property from ARTBaseMessage, but it was never populated on inbound messages or encoded on outbound messages.

Defining the method overloads to include extras will happen in a separate PR.

To unblock: ably/ably-chat-swift#478

CHA-1243

Changes

Source/ARTJsonLikeEncoder.m

  • presenceMessageFromDictionary: now reads extras from the wire
  • presenceMessageToDictionary: now writes extras to the wire when set

Test/Tests/RealtimeClientPresenceTests.swift

  • Integration test that signs an Ably JWT with a user claim, connects, enters presence, and verifies extras.userClaim is populated on the received presence message

Spec points

  • TP3i

Summary by CodeRabbit

  • New Features

    • Presence messages now support an optional extras field for carrying additional metadata; extras are preserved when sending and receiving and exposed on presence messages.
    • Extras can be populated from JWT user claims and will round-trip through presence exchanges.
  • Tests

    • Added tests validating extras are populated from JWT user claims and retained in presence messages.
    • Test utilities added to generate and fetch JWTs (including embedded JWT via test server) and helper utilities for signing and encoding JWTs.

@coderabbitai

coderabbitai Bot commented Feb 12, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds support for an extras payload on presence messages (round-tripped during encode/decode) and exposes extras on ARTPresenceMessage. Adds a test validating extras populated from JWT user claims and JWT utilities to create/obtain signed tokens for testing.

Changes

Cohort / File(s) Summary
Presence extras implementation
Source/ARTJsonLikeEncoder.m, Source/ARTPresenceMessage.h
Decode input[@"extras"] into ARTPresenceMessage.extras and serialize extras during encoding; added public extras property to ARTPresenceMessage.
JWT test & utilities
Test/AblyTests/Tests/RealtimeClientPresenceTests.swift, Test/AblyTests/Test Utilities/TestUtilities.swift
Added test test__119__Presence__presence_message_attributes__extras_should_be_populated_from_user_claims_in_JWT() and JWT helpers: getJWTToken extended with extraClaims, embedded-JWT fetch via echo server, signJWT, base64urlEncode, and CommonCrypto import to produce/validate JWT-embedded claims.

Sequence Diagram(s)

sequenceDiagram
    participant TestClientA as Client A (test subscriber)
    participant TestClientB as Client B (jwt-authenticated publisher)
    participant AuthEcho as Echo/Auth server
    participant Realtime as Realtime service

    Note over TestClientB,AuthEcho: Test obtains JWT with user claim
    TestClientB->>AuthEcho: request embedded JWT (extraClaims: userClaim)
    AuthEcho-->>TestClientB: JWT (contains extras.userClaim)

    TestClientB->>Realtime: connect with JWT and enter presence (includes extras)
    Realtime-->>TestClientA: presence enter event delivered (payload includes extras)
    TestClientA->>TestClientA: assert presenceMessage.extras.userClaim == expected
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇
I stitched a claim into a tiny token,
Hopped on channels where presence is spoken.
A nibble of extras, carried along,
Two clients met and hummed the same song. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: decode and encode extras on presence messages (TP3i)' clearly describes the main changes: decoding and encoding extras on presence messages, with a specific reference to spec requirement TP3i.
Linked Issues check ✅ Passed The PR addresses CHA-1243 by enabling extras.userClaim population on presence messages through proper decoding/encoding, unblocking the exposure of user claims in the Chat SDK.
Out of Scope Changes check ✅ Passed All changes are directly scoped to decoding/encoding extras on presence messages and testing JWT-based user claims, with no extraneous modifications.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/presence-message-extras-TP3i

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.

@AndyTWF AndyTWF changed the title Decode and encode extras on presence messages (TP3i) fix: decode and encode extras on presence messages (TP3i) Feb 12, 2026
@AndyTWF
AndyTWF force-pushed the fix/presence-message-extras-TP3i branch from da96304 to e9dc9ef Compare February 12, 2026 19:07
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 12, 2026 19:08 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/jazzydoc February 12, 2026 19:10 Inactive
@AndyTWF
AndyTWF force-pushed the fix/presence-message-extras-TP3i branch from e9dc9ef to 7f48187 Compare February 12, 2026 23:35
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 12, 2026 23:36 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/jazzydoc February 12, 2026 23:38 Inactive
@AndyTWF
AndyTWF marked this pull request as ready for review February 13, 2026 00:13

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@Test/AblyTests/Tests/RealtimeClientPresenceTests.swift`:
- Around line 3913-3927: subscribeOptions clears the test channel prefix but
jwtOptions does not, so client2 ends up using a different channel; after
obtaining jwtOptions (from AblyTests.clientOptions(for:)), set
jwtOptions.testOptions.channelNamePrefix = nil so client2 uses the exact same
channel name as client1 (affecting jwtOptions/client2/channelNamePrefix).
🧹 Nitpick comments (1)
Test/AblyTests/Tests/RealtimeClientPresenceTests.swift (1)

3957-3988: Consider extracting JWT helpers into a shared test utility.

signJWT and base64urlEncode are general-purpose test helpers. If other test files need JWT signing in the future (e.g., for message-level user claims), having them in a shared location would avoid duplication.

Not blocking — fine to defer to a follow-up if the scope is intentionally limited.

Comment thread Test/AblyTests/Tests/RealtimeClientPresenceTests.swift
Comment thread Test/AblyTests/Tests/RealtimeClientPresenceTests.swift Outdated
@AndyTWF
AndyTWF force-pushed the fix/presence-message-extras-TP3i branch from 7f48187 to c68836b Compare February 13, 2026 10:53
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 13, 2026 10:54 Inactive
Comment thread Test/AblyTests/Tests/RealtimeClientPresenceTests.swift Outdated
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/jazzydoc February 13, 2026 10:57 Inactive
@lawrence-forooghian
lawrence-forooghian force-pushed the fix/presence-message-extras-TP3i branch from c68836b to 0ea6710 Compare February 13, 2026 11:00
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 13, 2026 11:01 Inactive
@lawrence-forooghian
lawrence-forooghian force-pushed the fix/presence-message-extras-TP3i branch from 0ea6710 to 89ee33f Compare February 13, 2026 11:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@Source/ARTJsonLikeEncoder.m`:
- Around line 448-449: Remove the trailing whitespace characters reported by the
linter: delete the extra spaces at the end of the line containing
"message.extras = [input objectForKey:@\"extras\"];" and at the other
trailing-whitespace occurrences around the block at lines corresponding to
757-760 in Source/ARTJsonLikeEncoder.m so the file has no trailing spaces on
those lines; ensure you save the file with those spaces removed.

In `@Test/AblyTests/Tests/RealtimeClientPresenceTests.swift`:
- Around line 3933-3940: The test incorrectly asserts that presence messages
carry a JWT-derived userClaim (it inspects message.extras as NSDictionary and
checks userClaim), but Ably docs state userClaim injection does not apply to
presence/metadata; either remove this presence-related test entirely or change
the assertion to reflect server behavior (e.g., assert userClaim is nil on
presence messages) and add a brief comment referencing the Ably docs; update the
code paths involving message.extras and the userClaim variable
(presenceMessage/message.extras/userClaim) accordingly so the test matches
actual Ably behavior.
🧹 Nitpick comments (1)
Test/AblyTests/Tests/RealtimeClientPresenceTests.swift (1)

3929-3951: Test could hang indefinitely if presence enter fails before subscribe fires.

If channel2.presence.enter fails (e.g., due to the channel prefix mismatch noted above, or an auth issue), the error handler calls done() but the XCTFail message might be missed since done() is only called once. More importantly, if enter succeeds but the subscribe callback never fires (e.g., wrong channel), the test hangs until timeout with no diagnostic. Consider adding a timeout-based failure message or subscribing before entering to ensure ordering.

Comment thread Source/ARTJsonLikeEncoder.m Outdated
Comment thread Test/AblyTests/Tests/RealtimeClientPresenceTests.swift
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 13, 2026 11:03 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/jazzydoc February 13, 2026 11:06 Inactive
Comment thread Test/AblyTests/Tests/RealtimeClientPresenceTests.swift
Comment thread Test/AblyTests/Tests/RealtimeClientPresenceTests.swift
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 14, 2026 00:07 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@Test/AblyTests/Test` Utilities/TestUtilities.swift:
- Around line 589-590: Update the incomplete comment above the JWT helper in
TestUtilities.swift so the sentence reads correctly; replace the fragment "it
will the echoserver" with a proper verb (for example "it will use the
echoserver") so the full comment becomes: "By default this will sign the token
locally, but for some cases (e.g. embedded JWTs), it will use the echoserver
that has this functionality." Target the comment block that starts "Gets a JWT
for use in tests." to ensure clarity.

Comment thread Test/AblyTests/Test Utilities/TestUtilities.swift Outdated
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/jazzydoc February 14, 2026 00:09 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 20, 2026 00:16 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/jazzydoc February 20, 2026 00:21 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/markdown-api-reference February 20, 2026 00:21 Inactive

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
Test/AblyTests/Test Utilities/TestUtilities.swift (1)

636-674: Mark signJWT and base64urlEncode as private.

Both helpers are only called within this file—signJWT is called once from getJWTToken, and base64urlEncode is called only from within signJWT. Making them private tightens encapsulation and prevents accidental coupling from other test files.

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

In `@Test/AblyTests/Test` Utilities/TestUtilities.swift around lines 636 - 674,
Make the helper functions signJWT and base64urlEncode private to limit their
visibility to this file: update the declarations of
signJWT(claims:keyName:keySecret:) and base64urlEncode(_:) to use the private
access modifier so they remain usable by getJWTToken in this file but cannot be
referenced from other test files, keeping encapsulation tight.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Test/AblyTests/Test` Utilities/TestUtilities.swift:
- Around line 601-615: The current early return when jwtType is non-empty calls
getEmbeddedJWTTokenViaEchoServer and skips merging extraClaims into the JWT,
silently dropping them; update the logic in the function containing this block
so that either (a) you merge extraClaims into the claims passed to
getEmbeddedJWTTokenViaEchoServer (i.e., compute and pass the combined claims) or
(b) add an explicit precondition/guard that extraClaims.isEmpty when jwtType is
non-empty and fail fast with a clear error message; reference jwtType,
extraClaims, getEmbeddedJWTTokenViaEchoServer, and signJWT to locate and modify
the code path accordingly.

---

Nitpick comments:
In `@Test/AblyTests/Test` Utilities/TestUtilities.swift:
- Around line 636-674: Make the helper functions signJWT and base64urlEncode
private to limit their visibility to this file: update the declarations of
signJWT(claims:keyName:keySecret:) and base64urlEncode(_:) to use the private
access modifier so they remain usable by getJWTToken in this file but cannot be
referenced from other test files, keeping encapsulation tight.

Comment thread Test/AblyTests/Test Utilities/TestUtilities.swift
@lawrence-forooghian

Copy link
Copy Markdown
Contributor

Looks good, please could you sort out the commits (put f2ea7aa before 89ee33f and incorporate 6e3f426 into respective commits)?

@AndyTWF
AndyTWF force-pushed the fix/presence-message-extras-TP3i branch from 6e3f426 to f81375b Compare February 23, 2026 19:45
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/features February 23, 2026 19:46 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/jazzydoc February 23, 2026 19:50 Inactive
@github-actions
github-actions Bot temporarily deployed to staging/pull/2186/markdown-api-reference February 23, 2026 19:50 Inactive
getJWTToken now signs tokens locally using HMAC-SHA256 for the common
case (no jwtType). When jwtType is set (e.g. "embedded"), it falls
back to the echo server via a private helper since those require
server-side token embedding.

Also adds precondition guards to fail fast when unsupported parameter
combinations are used: extraClaims on the echo-server path and
encrypted on the local signing path.
- presenceMessageFromDictionary: now reads extras from the wire
- presenceMessageToDictionary: now writes extras to the wire
- Add integration test: JWT with user claim -> presence enter -> verify
  extras.userClaim on subscribed message
@lawrence-forooghian
lawrence-forooghian merged commit 4bd83ea into main Feb 24, 2026
11 of 13 checks passed
@lawrence-forooghian
lawrence-forooghian deleted the fix/presence-message-extras-TP3i branch February 24, 2026 12:32
@AndyTWF AndyTWF mentioned this pull request Feb 24, 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