Skip to content

[MM-68986][MM-69203] Add module to collect Session Attributes from Mobile App - #9830

Merged
devinbinnie merged 6 commits into
mainfrom
MM-68986
Aug 18, 2026
Merged

[MM-68986][MM-69203] Add module to collect Session Attributes from Mobile App#9830
devinbinnie merged 6 commits into
mainfrom
MM-68986

Conversation

@devinbinnie

@devinbinnie devinbinnie commented Jun 11, 2026

Copy link
Copy Markdown
Member

Summary

This PR introduces a SessionAttributesManager that reports client Session Attributes to enabled servers for use in ABAC policies.

The collection and header injection themselves live in @mattermost/react-native-network-client 1.11.0, which is bumped here: a request adapter attaches the base64-encoded X-MM-Session-Attributes header to authenticated traffic, including uploads and downloads, and honours each attribute's TTL so values are only re-sent once stale. It's opt-in per client via enableSessionAttributes, and the native paths that bypass the API client (share extension, notification service, notification reply) resolve the same header through a small hook in Gekidou.

Reporting the Wi-Fi SSID needs new platform capabilities: ACCESS_WIFI_STATE and ACCESS_FINE_LOCATION on Android, and the wifi-info entitlement plus a location usage string on iOS.

Ticket Link

MM-68986
MM-69203

Release Note

Added support for reporting client session attributes to the server.
Enable location when in use permissions on both iOS/Android to fetch the SSID when requested

@devinbinnie devinbinnie added 2: Dev Review Requires review by a core commiter 3: Security Review Review requested from Security Team labels Jun 11, 2026
@devinbinnie devinbinnie added this to the v2.43.0 milestone Jun 11, 2026
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Documentation Impact Analysis — updates needed

Documentation Impact Analysis

Overall Assessment: Documentation Updates Recommended

Changes Summary

This PR introduces a SessionAttributesManager in the Mattermost mobile app that collects client-side session attributes (such as OS platform and Wi-Fi SSID) and forwards them to the server via an X-MM-Session-Attributes request header, enabling ABAC policy evaluation against mobile client context. To read the Wi-Fi SSID, the app now requests new platform permissions: ACCESS_WIFI_STATE and ACCESS_FINE_LOCATION on Android, and NSLocationWhenInUseUsageDescription plus the com.apple.developer.networking.wifi-info entitlement on iOS. The feature is gated by the FeatureFlagSessionAttributes feature flag or an Enterprise Advanced license.

Documentation Impact Details

Change Type Files Changed Affected Personas Documentation Action Docs Location
New native platform permissions (Android) android/app/src/main/AndroidManifest.xml — adds ACCESS_WIFI_STATE, ACCESS_FINE_LOCATION System Administrator, Security/Compliance Officer, End User Document that Wi-Fi SSID session attribute collection on Android requires Fine Location permission; admins using MDM/EMM should pre-authorize this permission. Note this is triggered only when the server requests the attribute. docs/source/deployment-guide/mobile/mobile-security-features.rst or docs/source/administration-guide/manage/admin/attribute-based-access-control.rst
New native platform permissions (iOS) ios/Mattermost/Info.plist, ios/MattermostShare/Info.plist — adds NSLocationWhenInUseUsageDescription; ios/Mattermost/Mattermost.entitlements, ios/MattermostShare/MattermostShare.entitlements — adds com.apple.developer.networking.wifi-info System Administrator, Security/Compliance Officer, End User Document that iOS users will receive a "When In Use" location permission prompt when Wi-Fi SSID session attributes are configured. Note the wifi-info entitlement requirement for custom app builds. docs/source/deployment-guide/mobile/mobile-security-features.rst or docs/source/administration-guide/manage/admin/attribute-based-access-control.rst
New or changed API endpoint consumed app/client/rest/users.ts — adds getSessionAttributesManifest calling GET /sessions/attributes/manifest System Administrator The existing session attributes admin docs mention the server-side endpoint; update to confirm mobile client now fetches and respects this manifest. docs/source/administration-guide/manage/admin/attribute-based-access-control.rst
New or changed app configuration or feature flag app/actions/websocket/system.ts — reacts to FeatureFlagSessionAttributes toggle; types/api/config.d.ts adds FeatureFlagSessionAttributes System Administrator Document that session attribute collection on mobile activates/deactivates dynamically when the feature flag or Enterprise Advanced license changes at runtime, without requiring app restart. docs/source/administration-guide/manage/admin/attribute-based-access-control.rst
User-facing behavioral change (new permission prompt) android/app/src/main/AndroidManifest.xml, ios/Mattermost/Info.plist End User End users will see a new location permission prompt explaining Wi-Fi name reporting. The prompt text is self-explanatory, but the admin guide should note that enabling this session attribute will trigger the OS permission dialog on enrolled devices. docs/source/deployment-guide/mobile/mobile-security-features.rst

Recommended Actions

  • Update docs/source/administration-guide/manage/admin/attribute-based-access-control.rst to add a mobile-specific note: when a session attribute targeting the mobile platform is enabled (e.g., Wi-Fi SSID / network name), the Mattermost mobile app will request the OS location permission ("When In Use") on first use. Admins deploying via MDM/EMM should pre-grant this permission to avoid user-facing prompts.
  • Update docs/source/deployment-guide/mobile/mobile-security-features.rst (or mobile-app-deployment.rst) to list the new platform permissions added in this release: Android ACCESS_FINE_LOCATION + ACCESS_WIFI_STATE; iOS NSLocationWhenInUseUsageDescription + com.apple.developer.networking.wifi-info entitlement. Explain these are required only when the server requests Wi-Fi SSID session attributes and are not requested otherwise.
  • If a custom mobile build guide exists or is planned (e.g., docs/source/deployment-guide/mobile/distribute-custom-mobile-apps.rst), add a note that custom forks must include the new Android permissions and iOS Info.plist key and entitlement to support session attribute collection.

Confidence

Medium — The server-side session attributes and ABAC infrastructure is already documented in the v11 changelog and the admin guide. The gaps identified here are mobile-specific: the new OS permission requests and their conditional trigger are not covered anywhere in the current docs, and admins managing enrolled devices via MDM/EMM will need this information. The feature flag / license-driven activation is also not described in existing mobile documentation.


@github-actions github-actions Bot added the Docs/Needed Requires documentation label Jun 11, 2026
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

Coverage Comparison Report

Generated on August 18, 2026 at 15:04:45 UTC

+-----------------+------------+------------+-----------+
| Metric          | Main       | This PR    | Diff      |
+-----------------+------------+------------+-----------+
| Lines           |     88.39% |     88.39% |     0.00% |
| Statements      |     88.25% |     88.25% |     0.00% |
| Branches        |     77.33% |     77.33% |     0.00% |
| Functions       |     87.70% |     87.73% |     0.03% |
+-----------------+------------+------------+-----------+
| Total           |     85.41% |     85.42% |     0.01% |
+-----------------+------------+------------+-----------+

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

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
  • ✅ Review completed - (🔄 Check again to review again)
📝 Walkthrough

Walkthrough

Adds end-to-end session attributes support. The change defines manifest contracts, manages server and device data, synchronizes websocket updates, refreshes state during lifecycle events, and forwards outbound headers through Android and iOS networking paths.

Changes

Session Attributes Integration

Layer / File(s) Summary
Contracts and manifest API
types/api/session_attributes.d.ts, types/api/config.d.ts, app/constants/session_attributes.ts, app/client/rest/users.ts, app/actions/remote/session_attributes.ts, app/actions/remote/session_attributes.test.ts
Defines session attribute field types and constants. Adds the optional feature flag and manifest endpoint. Adds manifest retrieval with error results and tests.
Manifest state and static values
app/managers/session_attributes_manager/index.ts, app/managers/session_attributes_manager/index.test.ts, app/init/app.ts
Adds SessionAttributesManager for static device values, manifest refresh, server removal, and field operations. Synchronizes static values during application initialization.
Websocket manifest field updates
app/actions/websocket/users.ts, app/actions/websocket/event.ts, app/actions/websocket/users.test.ts
Processes session property-field events. Enabled mobile fields are upserted with metadata. Disabled or unsupported fields are removed.
Connection and session lifecycle integration
app/actions/websocket/index.ts, app/actions/websocket/system.ts, app/managers/session_manager.ts, app/actions/websocket/index.test.ts, app/actions/websocket/system.test.ts, app/managers/session_manager.test.ts
Refreshes manifests during connection and feature-flag enablement. Removes server state during feature-flag disablement, logout, and session expiry.
Native network and sharing headers
android/..., ios/..., libraries/@mattermost/rnshare/..., app/managers/network_manager.ts, package.json
Enables session attributes in network adapters. Registers outbound header providers and adds headers to Android and iOS requests, uploads, replies, notifications, and share flows. Adds required platform permissions and dependency support.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant App
  participant SessionAttributesManager
  participant WebSocket
  participant ClientUsers
  participant NativeNetwork
  App->>SessionAttributesManager: syncStaticValues()
  WebSocket->>SessionAttributesManager: refreshManifest(serverUrl)
  SessionAttributesManager->>ClientUsers: fetch session attributes manifest
  ClientUsers-->>SessionAttributesManager: SAField[] manifest
  NativeNetwork->>SessionAttributesManager: get outbound header
  SessionAttributesManager-->>NativeNetwork: X-MM-Session-Attributes
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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
Title check ✅ Passed The title clearly identifies the main change: adding a module to collect session attributes from the mobile app.
Description check ✅ Passed The description directly explains the SessionAttributesManager, session attribute reporting, header injection, permissions, and related implementation changes.
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.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MM-68986

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 5

🧹 Nitpick comments (7)
app/managers/session_attributes_manager/collector/index.ts (7)

13-15: ⚡ Quick win

Prefer nullish coalescing (??) for the fallback.

As per coding guidelines, use ?? instead of || for fallbacks to avoid converting falsy values incorrectly.

♻️ Proposed fix
 getOSVersion() {
-    return osVersion || '';
+    return osVersion ?? '';
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/collector/index.ts` around lines 13 -
15, The getOSVersion method uses the logical OR fallback (return osVersion ||
'') which can incorrectly treat valid falsy values as absent; update
getOSVersion to use the nullish coalescing operator (return osVersion ?? '') so
only null or undefined trigger the fallback, keeping the method behavior the
same otherwise and referencing the getOSVersion function and the osVersion
variable.

Source: Coding guidelines


66-82: 💤 Low value

Add explicit return type annotation.

For clarity, add : string return type to the helper method.

♻️ Proposed fix
-protected mapNetInfoType(state: NetInfoState) {
+protected mapNetInfoType(state: NetInfoState): string {
     switch (state.type) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/collector/index.ts` around lines 66 -
82, The helper method mapNetInfoType lacks an explicit return type; update its
signature (mapNetInfoType) to declare a return type of string (e.g., add ":
string") so the function signature clearly indicates it returns a string and
helps with type checking and readability while leaving the existing switch
behavior intact.

42-45: 💤 Low value

Add explicit return type annotation.

For consistency and type safety, add : Promise<string> return type annotation.

♻️ Proposed fix
-async getClientIPAddress() {
+async getClientIPAddress(): Promise<string> {
     const state = await NetInfo.fetch();
     return this.extractIpAddress(state);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/collector/index.ts` around lines 42 -
45, The method getClientIPAddress lacks an explicit return type; update its
signature to include an explicit Promise<string> return type (i.e., change
getClientIPAddress() to getClientIPAddress(): Promise<string>) and ensure the
returned value from this.extractIpAddress(state) is compatible with string
(adjust extractIpAddress’s return type if needed) so the compiler enforces type
safety.

84-90: 💤 Low value

Add explicit return type annotation.

For clarity, add : string return type to the helper method.

♻️ Proposed fix
-protected extractIpAddress(state: NetInfoState) {
+protected extractIpAddress(state: NetInfoState): string {
     const details = state.details;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/collector/index.ts` around lines 84 -
90, The helper method extractIpAddress currently lacks an explicit return type;
update its signature to declare a string return type (i.e., add : string) so the
function signature reads with a clear return type, keeping the implementation
unchanged; locate the extractIpAddress method in the class (method name
extractIpAddress and parameter NetInfoState) and add the explicit return type
annotation.

37-40: 💤 Low value

Add explicit return type annotation.

For consistency and type safety, add : Promise<string> return type annotation.

♻️ Proposed fix
-async getNetworkInterfaceType() {
+async getNetworkInterfaceType(): Promise<string> {
     const state = await NetInfo.fetch();
     return this.mapNetInfoType(state);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/collector/index.ts` around lines 37 -
40, The getNetworkInterfaceType method lacks an explicit return type; update its
signature to declare a Promise<string> return type and ensure the implementation
still returns a string via this.mapNetInfoType(state) so the signature matches;
locate the async method getNetworkInterfaceType in the collector index and add
": Promise<string>" to the method declaration.

47-54: 💤 Low value

Add explicit return type annotations.

Both methods should have explicit return types for consistency: getIsVpnActive(): Promise<string> and getOSPlatform(): string.

♻️ Proposed fix
-async getIsVpnActive() {
+async getIsVpnActive(): Promise<string> {
     const state = await NetInfo.fetch();
     return state.type === NetInfoStateType.vpn ? 'true' : 'false';
 }

-getOSPlatform() {
+getOSPlatform(): string {
     return Platform.OS;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/collector/index.ts` around lines 47 -
54, The two methods lack explicit return type annotations; update the method
signatures for getIsVpnActive to declare a Promise<string> return type and
getOSPlatform to declare a string return type (i.e., change getIsVpnActive() to
getIsVpnActive(): Promise<string> and getOSPlatform() to getOSPlatform():
string) so their declarations are explicit and consistent with their returned
values.

33-35: ⚡ Quick win

Add explicit return type annotation.

The manager's collectAttribute method expects all attribute getters to return Promise<string>, but this method's return type is inferred. Add an explicit : Promise<string> annotation to ensure type safety and clarity.

♻️ Proposed fix
-async getClientDeviceId() {
+async getClientDeviceId(): Promise<string> {
     return getDeviceToken();
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/collector/index.ts` around lines 33 -
35, The getClientDeviceId method currently has an inferred return type; add an
explicit Promise<string> return annotation to match the expectations of
collectAttribute and ensure type safety by changing the signature of
getClientDeviceId to async getClientDeviceId(): Promise<string> (keeping the
existing body that returns getDeviceToken()); verify no other callers rely on a
different signature.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/actions/websocket/users.ts`:
- Around line 211-222: The handler handleSessionAttributesPropertyFieldEvent is
synchronous but calls the async
SessionAttributesManager.refreshManifest(serverUrl) without awaiting it, so
exceptions escape the try/catch and it behaves fire-and-forget; make
handleSessionAttributesPropertyFieldEvent an async function (returning
Promise<void>) and await SessionAttributesManager.refreshManifest(serverUrl)
inside the existing try block so any errors are caught by the catch and behavior
matches other handlers like handleUserUpdatedEvent.

In `@app/managers/session_attributes_manager/index.ts`:
- Line 57: The log message incorrectly names the method as fetchManifest; update
the debug call inside SessionAttributesManager.refreshManifest to reference the
correct method name so the log reads
'[SessionAttributesManager.refreshManifest]'. Locate the logDebug invocation
that passes getFullErrorMessage(error) and change the static string token to
reflect refreshManifest, keeping the rest of the call intact.
- Around line 84-90: The loop updates state.lastSentAt for every field even when
collectAttribute(field.name, serverUrl) returns an empty value and the attribute
is not added to payload; change the logic in the Promise.all mapped async
function inside the SessionAttributesManager (the block iterating over
fieldsToSend) so that state.lastSentAt.set(field.name, now) is executed only
when value is non-empty and payload[field.name] is assigned (i.e., move the
lastSentAt update into the same conditional that checks if (value) so
failed/empty collections do not advance the TTL).
- Around line 66-99: The getOutboundHeader method can leave state.isSending true
if collectAttribute throws; wrap the Promise.all block in a try/finally so
state.isSending is always reset (set state.isSending = true before collection,
perform the await Promise.all(...) inside try, and set state.isSending = false
in finally). Ensure you still update state.lastSentAt inside the try only after
successful attribute reads (as currently done using collectAttribute) and keep
the rest of the payload/return logic unchanged.

In `@types/api/session_attributes.d.ts`:
- Around line 4-9: The SAField type defined in types/api/session_attributes.d.ts
is not exported but is referenced elsewhere (e.g., app/client/rest/users.ts),
causing TypeScript errors; update the declaration for SAField to be exported by
adding the export keyword (export type SAField = { ... }) so other modules can
import and use the type; ensure the exported name matches all usages (SAField)
across the codebase.

---

Nitpick comments:
In `@app/managers/session_attributes_manager/collector/index.ts`:
- Around line 13-15: The getOSVersion method uses the logical OR fallback
(return osVersion || '') which can incorrectly treat valid falsy values as
absent; update getOSVersion to use the nullish coalescing operator (return
osVersion ?? '') so only null or undefined trigger the fallback, keeping the
method behavior the same otherwise and referencing the getOSVersion function and
the osVersion variable.
- Around line 66-82: The helper method mapNetInfoType lacks an explicit return
type; update its signature (mapNetInfoType) to declare a return type of string
(e.g., add ": string") so the function signature clearly indicates it returns a
string and helps with type checking and readability while leaving the existing
switch behavior intact.
- Around line 42-45: The method getClientIPAddress lacks an explicit return
type; update its signature to include an explicit Promise<string> return type
(i.e., change getClientIPAddress() to getClientIPAddress(): Promise<string>) and
ensure the returned value from this.extractIpAddress(state) is compatible with
string (adjust extractIpAddress’s return type if needed) so the compiler
enforces type safety.
- Around line 84-90: The helper method extractIpAddress currently lacks an
explicit return type; update its signature to declare a string return type
(i.e., add : string) so the function signature reads with a clear return type,
keeping the implementation unchanged; locate the extractIpAddress method in the
class (method name extractIpAddress and parameter NetInfoState) and add the
explicit return type annotation.
- Around line 37-40: The getNetworkInterfaceType method lacks an explicit return
type; update its signature to declare a Promise<string> return type and ensure
the implementation still returns a string via this.mapNetInfoType(state) so the
signature matches; locate the async method getNetworkInterfaceType in the
collector index and add ": Promise<string>" to the method declaration.
- Around line 47-54: The two methods lack explicit return type annotations;
update the method signatures for getIsVpnActive to declare a Promise<string>
return type and getOSPlatform to declare a string return type (i.e., change
getIsVpnActive() to getIsVpnActive(): Promise<string> and getOSPlatform() to
getOSPlatform(): string) so their declarations are explicit and consistent with
their returned values.
- Around line 33-35: The getClientDeviceId method currently has an inferred
return type; add an explicit Promise<string> return annotation to match the
expectations of collectAttribute and ensure type safety by changing the
signature of getClientDeviceId to async getClientDeviceId(): Promise<string>
(keeping the existing body that returns getDeviceToken()); verify no other
callers rely on a different signature.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3aca214b-0d20-4011-9586-a94459674eeb

📥 Commits

Reviewing files that changed from the base of the PR and between 99bb8d9 and fddd864.

📒 Files selected for processing (31)
  • app/actions/remote/entry/login.ts
  • app/actions/remote/file.test.ts
  • app/actions/remote/file.ts
  • app/actions/remote/user.test.ts
  • app/actions/remote/user.ts
  • app/actions/websocket/event.ts
  • app/actions/websocket/index.test.ts
  • app/actions/websocket/index.ts
  • app/actions/websocket/system.test.ts
  • app/actions/websocket/system.ts
  • app/actions/websocket/users.test.ts
  • app/actions/websocket/users.ts
  • app/client/rest/constants.ts
  • app/client/rest/files.test.ts
  • app/client/rest/files.ts
  • app/client/rest/tracking.test.ts
  • app/client/rest/tracking.ts
  • app/client/rest/users.ts
  • app/constants/session_attributes.ts
  • app/constants/websocket.ts
  • app/managers/draft_upload_manager/index.test.ts
  • app/managers/draft_upload_manager/index.ts
  • app/managers/session_attributes_manager/collector/index.test.ts
  • app/managers/session_attributes_manager/collector/index.ts
  • app/managers/session_attributes_manager/index.test.ts
  • app/managers/session_attributes_manager/index.ts
  • app/managers/session_manager.test.ts
  • app/managers/session_manager.ts
  • app/screens/channel_bookmark/components/bookmark_file/bookmark_file.tsx
  • types/api/config.d.ts
  • types/api/session_attributes.d.ts

Comment thread app/actions/websocket/users.ts
Comment thread app/managers/session_attributes_manager/index.ts Outdated
Comment thread app/managers/session_attributes_manager/index.ts Outdated
Comment thread app/managers/session_attributes_manager/index.ts Outdated
Comment thread types/api/session_attributes.d.ts

@enahum enahum 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.

A few pointers to improve on this, happy to discuss if you disagree on anything

Comment thread app/actions/remote/entry/login.ts Outdated
return {error: clData.error};
}

await SessionAttributesManager.refreshManifest(serverUrl);

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.

looks like this is being called too early as you are calling it regardless if the check for the credentials just after this, and is also being executed twice, once here and once when the WS calls doReconnect.

Also, can this be fire and forget? if not why not ?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fetching it extra times doesn't hurt, we do need to ensure that it is there otherwise the app won't know which session attributes to send and when. And we don't want to default to sending them all all the time or server performance will degrade.

We could fire and forget but then the error case would mean session attributes are never sent and the user would not have access to certain resources/permissions.

Given all that, where is the best place to do this call correctly? I wasn't fully sure.

Comment thread app/client/rest/tracking.ts Outdated
prepareRequestHeaders = async (requestMethod: string) => {
const headers = this.getRequestHeaders(requestMethod);

if (headers[ClientConstants.HEADER_AUTH]) {

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.

have you confirm that this true for all the requests? I ask because the network library injects the Authorization token on the native side for all requests that match the url but I'm unsure the Authorization header is added for all requests

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Which native requests might we need to inject this for? I assume this matches most of the applications functionality and should make sure the attributes are kept up to date. Is there any native function that gets data that might require some kind of permission?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Looking at Daniel's comment, replying to a push notification might be the only one that needs this since we feasibly might have permissions around being able to post in a channel based on session attributes.

This is where my mobile knowledge falls short - can we somehow use the same hooks in the native code that we're using in react-native so we don't need to duplicate all of the gathering and header code?

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.

Ok let me explain here.

In cases where the request does not include an Authorization header, the native network layer would check that the request is being made to a server where the Authorization header is stored in the keychain and will include it, that way we really remove that burden from the JS side, it does apply more commonly for images and videos being loaded.

So the reality is that here you are gating the session headers when the request includes an Authorization token, and I don't know if that is the correct gate, specially as I said for things like images and videos.

To answer your question about native side, we do perform some requests from native, for push notifications and the Share Extension on both Android and iOS. (the extension for Android uses the code in libraries/@mattermost/rnshare, these requests are not including the headers you are adding here and NO, they do NOT share the same code, you need to write the code in Swift or Objective-C for iOS and write the code in Kotlin or Java from Android. OR you could dive into the react-native-network-client library, see what is done with the Authorization header and potentially do something similar so that every requests includes whatever session attributes you set from JS which is probably WORTH looking into.

If you want to explore this network-client approach, I'm more than happy to have a convo and see what can we do, considering the fact that we also open sourced the library and is being used by many.

Comment thread app/managers/session_attributes_manager/collector/index.ts Outdated
Comment thread app/managers/session_attributes_manager/collector/index.ts Outdated
Comment thread app/managers/session_attributes_manager/collector/index.ts Outdated
Comment thread app/actions/remote/user.test.ts Outdated
Comment thread app/actions/remote/user.ts Outdated
Comment thread app/client/rest/tracking.ts Outdated
Comment thread app/actions/websocket/index.ts
Comment thread app/actions/websocket/users.ts Outdated

@edgarbellot edgarbellot 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.

@devinbinnie looking good! I only have one suggestion

Comment thread app/managers/session_attributes_manager/index.ts Outdated

@larkox larkox 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.

Apart of Elias' comments, we haven't done anything on the native side (Push Notifications, Share Extension, and Reply from notification features). Those will not add the correct headers.

That being said, I think after Elias' proposed changes, the PR will be quite different, so feel free to re-request my review.

@mattermost-build mattermost-build added E2E/Run Triggers E2E tests on both iOS and Android via Matterwick and removed E2E/Run Triggers E2E tests on both iOS and Android via Matterwick labels Jul 6, 2026

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/managers/session_attributes_manager/index.ts (1)

34-68: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Await cache priming before saving the manifest. getOutboundHeader() can run before NetInfo.fetch(), isRootedExperimentalAsync(), or getDeviceToken() resolve, so the first request can mark empty/default security attributes as sent and suppress the real values until the TTL window ends.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/managers/session_attributes_manager/index.ts` around lines 34 - 68,
`SessionAttributesManager.refreshManifest` is saving the manifest before the
security-attribute cache has finished priming, which lets `getOutboundHeader()`
send empty/default values first and cache them as sent. Update the manifest
refresh flow to wait for the cache priming work triggered by `NetInfo.fetch()`,
`isRootedExperimentalAsync()`, and `getDeviceToken()` before calling
`this.servers.set(...)`. Keep the existing early-return behavior, but ensure the
priming promise(s) are awaited before the manifest is stored so the first header
generation uses the real values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/managers/session_attributes_manager/index.ts`:
- Around line 34-68: `SessionAttributesManager.refreshManifest` is saving the
manifest before the security-attribute cache has finished priming, which lets
`getOutboundHeader()` send empty/default values first and cache them as sent.
Update the manifest refresh flow to wait for the cache priming work triggered by
`NetInfo.fetch()`, `isRootedExperimentalAsync()`, and `getDeviceToken()` before
calling `this.servers.set(...)`. Keep the existing early-return behavior, but
ensure the priming promise(s) are awaited before the manifest is stored so the
first header generation uses the real values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9a4198d3-f4dd-4def-91d5-3ff44f228a6b

📥 Commits

Reviewing files that changed from the base of the PR and between a35f641 and 79766d9.

📒 Files selected for processing (12)
  • app/actions/remote/file.test.ts
  • app/actions/remote/file.ts
  • app/actions/remote/session_attributes.test.ts
  • app/actions/remote/session_attributes.ts
  • app/actions/remote/user.ts
  • app/client/rest/files.test.ts
  • app/client/rest/files.ts
  • app/client/rest/tracking.test.ts
  • app/client/rest/tracking.ts
  • app/client/rest/users.ts
  • app/managers/session_attributes_manager/index.test.ts
  • app/managers/session_attributes_manager/index.ts
✅ Files skipped from review due to trivial changes (2)
  • app/client/rest/files.ts
  • app/actions/remote/user.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • app/client/rest/users.ts
  • app/client/rest/tracking.test.ts

@mattermost-build mattermost-build added E2E/Run Triggers E2E tests on both iOS and Android via Matterwick and removed E2E/Run Triggers E2E tests on both iOS and Android via Matterwick labels Jul 6, 2026
@devinbinnie
devinbinnie requested review from enahum and larkox July 7, 2026 14:20
@devinbinnie

Copy link
Copy Markdown
Member Author

@larkox @enahum I've re-requested review since I had a few questions about the mobile app that I'd like to get straightened out before the full review, I've responded to the above comments. We can also hop on a call if that's easier.

@devinbinnie
devinbinnie requested a review from edgarbellot July 7, 2026 14:21
@mattermost-build mattermost-build added E2E/Run Triggers E2E tests on both iOS and Android via Matterwick and removed E2E/Run Triggers E2E tests on both iOS and Android via Matterwick labels Jul 7, 2026
@devinbinnie devinbinnie changed the title [MM-68986] Add module to collect Session Attributes from Mobile App [MM-68986][MM-69203] Add module to collect Session Attributes from Mobile App Jul 7, 2026
@mattermost-build mattermost-build removed the E2E/Run Triggers E2E tests on both iOS and Android via Matterwick label Jul 7, 2026
@mattermost-build mattermost-build added the E2E/Run Triggers E2E tests on both iOS and Android via Matterwick label Aug 14, 2026
@mm-cloud-bot

Copy link
Copy Markdown

❌ E2E Test Setup Failed

Failed to create E2E test instances: installation wait cancelled: context canceled

@devinbinnie

Copy link
Copy Markdown
Member Author

/update-branch

@mattermost-build mattermost-build added E2E/Run Triggers E2E tests on both iOS and Android via Matterwick and removed E2E/Run Triggers E2E tests on both iOS and Android via Matterwick labels Aug 14, 2026
@amyblais amyblais added CherryPick/Approved Meant for the quality or patch release tracked in the milestone and removed CherryPick/Approved Meant for the quality or patch release tracked in the milestone labels Aug 17, 2026
@amyblais amyblais removed this from the v2.44.0 milestone Aug 18, 2026
@amyblais amyblais added the CherryPick/Approved Meant for the quality or patch release tracked in the milestone label Aug 18, 2026
@amyblais amyblais added this to the v2.44.0 milestone Aug 18, 2026
@devinbinnie devinbinnie added E2E/Run Triggers E2E tests on both iOS and Android via Matterwick and removed E2E/Run Triggers E2E tests on both iOS and Android via Matterwick labels Aug 18, 2026
@devinbinnie

Copy link
Copy Markdown
Member Author

/update-branch

@mattermost-build mattermost-build added E2E/Run Triggers E2E tests on both iOS and Android via Matterwick and removed E2E/Run Triggers E2E tests on both iOS and Android via Matterwick labels Aug 18, 2026

@enahum enahum 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.

One confirmation needed the reat seems fine

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.

Nothing major but this sequence is possible with this code

Thread A Thread B
reads handler #1
unlocks
sets handler #2
calls handler #1

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think it's likely to hit this, and if it does, it's always the same code path so I don't think there's a problem here.

Comment thread ios/Mattermost/Info.plist
<key>NSFaceIDUsageDescription</key>
<string>Enabling access to your Face ID means we can restrict unauthorized users from accessing $(PRODUCT_NAME) on your device.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Your location can be used to report the Wi-Fi network name to your administrator when required by your organization's security policy.</string>

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.

Is this accurate? Location is needed for the Wifi ssid?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yeah I think you need it to get any wi-fi information.

import UIKit
import os.log
import Sentry
import react_native_network_client

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.

Does this add the entire RN framework as a dep of the share extension? Based on the Podfile it shouldn't but please confirm

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Confirmed it doesn't.

import Intents
import os.log
import TurboLogIOSNative
import react_native_network_client

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.

Same question as share extension

@devinbinnie
devinbinnie requested a review from enahum August 18, 2026 15:47

@enahum enahum 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.

Approving with the promise that deltas will be added in the next release

@devinbinnie devinbinnie added 4: Reviews Complete All reviewers have approved the pull request and removed 2: Dev Review Requires review by a core commiter labels Aug 18, 2026
@devinbinnie
devinbinnie enabled auto-merge (squash) August 18, 2026 17:47
@devinbinnie devinbinnie removed the E2E/Run Triggers E2E tests on both iOS and Android via Matterwick label Aug 18, 2026
@mattermost-build

Copy link
Copy Markdown
Contributor

Cherry pick is scheduled.

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

Labels

4: Reviews Complete All reviewers have approved the pull request CherryPick/Done Successfully cherry-picked to the quality or patch release tracked in the milestone Docs/Needed Requires documentation E2E/Override Skip Running E2E tests release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants