Skip to content

Fix recording access not working in restrictive browsers#2901

Open
Sabr1n4W wants to merge 13 commits intodevelopfrom
2851-recording-access-not-working-in-restrictive-browsers
Open

Fix recording access not working in restrictive browsers#2901
Sabr1n4W wants to merge 13 commits intodevelopfrom
2851-recording-access-not-working-in-restrictive-browsers

Conversation

@Sabr1n4W
Copy link
Contributor

@Sabr1n4W Sabr1n4W commented Mar 2, 2026

Fixes #2851

Type

  • Bugfix
  • Feature
  • Documentation
  • Refactoring (e.g. Style updates, Test implementation, etc.)
  • Other (please describe):

Checklist

  • Code updated to current develop branch head
  • Passes CI checks
  • Is a part of an issue
  • Tests added for the bugfix or newly implemented feature, describe below why if not
  • Changelog is updated
  • Documentation of code and features exists

Changes

  • Move the access recording route from api to web and redirect directly instead of returning a URL

Other information

Summary by CodeRabbit

  • New Features

    • Recording viewers now use cross-window messaging and direct link navigation for formats, improving how viewers open in new tabs and surface errors.
    • 404 error page signals file-not-found to the opener window for better UX when viewing recordings.
  • Bug Fixes

    • Addressed issues with browsers blocking recording access when opening new windows without user interaction.
  • Security

    • Added Cross-Origin-Opener-Policy header to strengthen cross-window isolation.

@Sabr1n4W Sabr1n4W self-assigned this Mar 2, 2026
@Sabr1n4W Sabr1n4W linked an issue Mar 2, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 2, 2026

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

Refactors recording-format access from API/window.open flows to web-route navigation and cross-window messaging, renames four CustomErrorMessages enum cases and matching JS constants (removing ROOM_/FILE_ prefixes), adds a 404 view that posts messages to window.opener, and updates controllers, routes, frontend components, and tests accordingly.

Changes

Cohort / File(s) Summary
Error Enum & Constants
app/Enums/CustomErrorMessages.php, resources/js/constants/httpCustomErrorMessages.js
Renamed four enum cases and matching JS constants by removing ROOM_/FILE_ prefixes while preserving their string values: GUESTS_NOT_ALLOWED, GUESTS_ONLY, FORBIDDEN, FILE_NOT_FOUND.
Recording Format Access
app/Http/Controllers/RecordingFormatController.php, app/Http/Controllers/api/v1/RecordingFormatController.php, routes/web.php, routes/api.php
Removed API v1 controller and route; added web controller + web route to authorize and redirect to player or resource routes (replacing JSON responses with redirects/views).
Error Handling & Views
app/Http/Controllers/RoomFileController.php, app/Services/RoomFileService.php, app/Http/Controllers/RecordingController.php, resources/views/errors/404.blade.php
Updated controllers/services to use renamed error enum values and replaced aborts with view-based new-tab-error responses; added 404 blade that posts file_not_found to window.opener and closes the window.
Auth & Middleware Updates
app/Http/Controllers/api/v1/RoomController.php, app/Http/Middleware/RoomAuthenticate.php
Replaced enum references for guest authentication flows to new names (GUESTS_NOT_ALLOWED, GUESTS_ONLY) with no control-flow changes.
Frontend Components
resources/js/components/RoomJoinButton.vue, resources/js/components/RoomTabFiles.vue, resources/js/components/RoomTabRecordings.vue, resources/js/components/RoomTabRecordingsViewButton.vue, resources/js/views/RoomsView.vue
Updated imports/usages to new HTTP error constants; RoomTabRecordings now listens for window postMessage events to handle errors; RoomTabRecordingsViewButton simplified to static anchor links opening in new tabs.
Tests — Backend & Frontend
tests/Backend/Feature/api/v1/RecordingTest.php, tests/Backend/Feature/api/v1/Room/FileTest.php, tests/Backend/Feature/api/v1/Room/RoomTest.php, tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js
Backend tests updated to expect redirects/views and renamed enum messages; e2e tests moved from network intercepts/window.open stubs to postMessage-driven flows and assert anchor link attributes.
Routing, Security & Changelog
routes/web.php, routes/api.php, docker/app/nginx/snippets/security-header.conf, CHANGELOG.md
Moved recording-format route to web routes, removed API import/route, added Cross-Origin-Opener-Policy: same-origin header, and updated changelog entries describing recording access behavior and related issues.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

refactor, tests, frontend, UI

Suggested reviewers

  • SamuelWei
  • danielmachill
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive While most changes align with the stated objective, the refactoring of enum constant names (ROOM_* to shorter names) and the addition of a Cross-Origin-Opener-Policy security header appear tangential. Clarify whether enum renaming and security header addition are necessary for the recording access fix or represent scope creep from related refactoring work.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main objective: fixing recording access in restrictive browsers by moving from async API calls to direct redirects.
Linked Issues check ✅ Passed The PR directly addresses the core requirements of issue #2851: moving recording access from async API response to direct user-initiated redirect, eliminating browser pop-up restrictions on async actions.
Description check ✅ Passed PR description is mostly complete with issue reference, type selection, all checklist items marked complete, changelog updated, and clear summary of changes.

✏️ 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 2851-recording-access-not-working-in-restrictive-browsers
📝 Coding Plan
  • Generate coding plan for human review comments

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.

Tip

You can get early access to new features in CodeRabbit.

Enable the early_access setting to enable early access features such as new models, tools, and more.

@codecov
Copy link

codecov bot commented Mar 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.71%. Comparing base (6806be4) to head (0826fde).
⚠️ Report is 2 commits behind head on develop.

Additional details and impacted files
@@            Coverage Diff             @@
##             develop    #2901   +/-   ##
==========================================
  Coverage      96.71%   96.71%           
- Complexity      1906     1907    +1     
==========================================
  Files            445      445           
  Lines          12878    12895   +17     
  Branches        2072     2069    -3     
==========================================
+ Hits           12455    12472   +17     
  Misses           423      423           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cypress
Copy link

cypress bot commented Mar 2, 2026

PILOS    Run #2881

Run Properties:  status check passed Passed #2881  •  git commit 0826fde10b: Fix recording access not working in restrictive browsers
Project PILOS
Branch Review 2851-recording-access-not-working-in-restrictive-browsers
Run status status check passed Passed #2881
Run duration 07m 37s
Commit git commit 0826fde10b: Fix recording access not working in restrictive browsers
Committer Sabrina Wüst
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 621
View all changes introduced in this branch ↗︎

@Sabr1n4W Sabr1n4W marked this pull request as ready for review March 2, 2026 10:49
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
app/Http/Controllers/RecordingFormatController.php (1)

30-30: Use session()->put instead of push for a boolean access flag.

At Line 30, push grows an array entry on every access, but RecordingController::resource only checks key existence. put avoids unnecessary session growth.

Proposed refactor
-        session()->push('access-format-'.$format->id, true);
+        session()->put('access-format-'.$format->id, true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/RecordingFormatController.php` at line 30, Replace the
session array append call with a boolean put: in RecordingFormatController where
session()->push('access-format-'.$format->id, true) is used, change it to
session()->put('access-format-'.$format->id, true) so the session stores a
single boolean flag (RecordingController::resource checks key existence) and
avoids growing an array on every access.
tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js (1)

28-67: Consolidate repeated format-link assertions into a helper.

The same 4-link assertion block is duplicated multiple times, which increases maintenance cost and drift risk.

♻️ Example Cypress helper pattern
+  const assertFormatLink = ({ selector, label, href, rel = "opener" }) => {
+    cy.get(selector)
+      .should("be.visible")
+      .and("include.text", label)
+      .and("have.attr", "href", href)
+      .and("have.attr", "rel", rel)
+      .and("have.attr", "target", "_blank");
+  };

Also applies to: 132-171, 352-391

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

In `@tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js` around lines 28
- 67, The test repeats four identical assertion blocks for format links
(selectors '[data-test="notes-button"]', '[data-test="podcast-button"]',
'[data-test="presentation-button"]', '[data-test="screenshare-button"]') in
RoomsViewRecordingsRecordingActions.cy.js; extract a reusable helper (e.g.,
assertFormatLink or checkFormatLink) that accepts the button selector, the
expected i18n text key (like "rooms.recordings.format_types.notes"), and the
expected href suffix (formats/1 .. formats/4) and replace each duplicated block
with calls to that helper; ensure the helper performs the same checks
(.should("be.visible"), .and("include.text", ...), .and("have.attr","href",
...), .and("have.attr","rel","opener"), .and("have.attr","target","_blank")) so
you can reuse it wherever the same pattern appears (also at the other ranges
noted).
tests/Backend/Feature/api/v1/RecordingTest.php (1)

535-655: Extract shared helpers to reduce repetitive route/error assertions.

The repeated route param arrays and new-tab-error assertion blocks make updates error-prone and noisy.

♻️ Example helper extraction
+    private function formatShowRouteParams(Recording $recording, RecordingFormat $format, array $extra = []): array
+    {
+        return array_merge([
+            'room' => $recording->room->id,
+            'recording' => $recording->id,
+            'format' => $format->id,
+        ], $extra);
+    }
+
+    private function assertNewTabError($response, string $type, int $code, string $title, string $message): void
+    {
+        $response->assertStatus($code)
+            ->assertViewIs('new-tab-error')
+            ->assertViewHasAll(compact('type', 'code', 'title', 'message'));
+    }

Also applies to: 679-697, 761-805, 842-849, 878-885, 936-955, 972-1007

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

In `@tests/Backend/Feature/api/v1/RecordingTest.php` around lines 535 - 655, The
test repeats building the route parameter arrays and asserting the same
"new-tab-error" view responses and redirects; extract reusable helpers in the
test class to reduce duplication and make changes safe. Add a helper like
requestFormatShow(array $params, array $cookies = []) that builds the
route('rooms.recordings.formats.show', $params) and performs $this->get() (using
withCookies when $cookies supplied), a helper assertNewTabError(array $expected)
that calls ->assertForbidden()/->assertUnauthorized() as appropriate then
->assertViewIs('new-tab-error')->assertViewHasAll($expected), and a helper
assertRecordingResourceRedirect($format, $recording, $resource) that wraps
->assertRedirectToRoute('recording.resource', [...]); then replace repeated
blocks that reference route('rooms.recordings.formats.show'),
->assertViewIs('new-tab-error'), ->assertViewHasAll([...]), $currentSession,
$roomAuthToken, $recording, and $format with these helpers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Http/Controllers/RecordingFormatController.php`:
- Around line 38-41: The code assumes explode($recording->id.'/', $format->url,
2)[1] always exists causing a potential undefined offset; update the parsing in
the RecordingFormatController to safely extract $resource from $format->url
(e.g., use explode with limit and check isset on index 1 or use
strpos/substring) and handle the missing case by returning a controlled response
(abort/404 or empty resource handling) before building $resourceRoute and
calling route('recording.resource').

In `@resources/js/components/RoomTabRecordingsViewButton.vue`:
- Around line 57-59: In RoomTabRecordingsViewButton.vue update the anchor tag
used in the view action (the element that calls viewFormatUrl(format)) to
replace rel="opener" with rel="noopener noreferrer" so external redirect targets
cannot access window.opener; locate the anchor that sets
:href="viewFormatUrl(format)" and change its rel attribute accordingly.

---

Nitpick comments:
In `@app/Http/Controllers/RecordingFormatController.php`:
- Line 30: Replace the session array append call with a boolean put: in
RecordingFormatController where session()->push('access-format-'.$format->id,
true) is used, change it to session()->put('access-format-'.$format->id, true)
so the session stores a single boolean flag (RecordingController::resource
checks key existence) and avoids growing an array on every access.

In `@tests/Backend/Feature/api/v1/RecordingTest.php`:
- Around line 535-655: The test repeats building the route parameter arrays and
asserting the same "new-tab-error" view responses and redirects; extract
reusable helpers in the test class to reduce duplication and make changes safe.
Add a helper like requestFormatShow(array $params, array $cookies = []) that
builds the route('rooms.recordings.formats.show', $params) and performs
$this->get() (using withCookies when $cookies supplied), a helper
assertNewTabError(array $expected) that calls
->assertForbidden()/->assertUnauthorized() as appropriate then
->assertViewIs('new-tab-error')->assertViewHasAll($expected), and a helper
assertRecordingResourceRedirect($format, $recording, $resource) that wraps
->assertRedirectToRoute('recording.resource', [...]); then replace repeated
blocks that reference route('rooms.recordings.formats.show'),
->assertViewIs('new-tab-error'), ->assertViewHasAll([...]), $currentSession,
$roomAuthToken, $recording, and $format with these helpers.

In `@tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js`:
- Around line 28-67: The test repeats four identical assertion blocks for format
links (selectors '[data-test="notes-button"]', '[data-test="podcast-button"]',
'[data-test="presentation-button"]', '[data-test="screenshare-button"]') in
RoomsViewRecordingsRecordingActions.cy.js; extract a reusable helper (e.g.,
assertFormatLink or checkFormatLink) that accepts the button selector, the
expected i18n text key (like "rooms.recordings.format_types.notes"), and the
expected href suffix (formats/1 .. formats/4) and replace each duplicated block
with calls to that helper; ensure the helper performs the same checks
(.should("be.visible"), .and("include.text", ...), .and("have.attr","href",
...), .and("have.attr","rel","opener"), .and("have.attr","target","_blank")) so
you can reuse it wherever the same pattern appears (also at the other ranges
noted).

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e87249 and 498d42d.

📒 Files selected for processing (21)
  • app/Enums/CustomErrorMessages.php
  • app/Http/Controllers/RecordingFormatController.php
  • app/Http/Controllers/RoomFileController.php
  • app/Http/Controllers/api/v1/RecordingFormatController.php
  • app/Http/Controllers/api/v1/RoomController.php
  • app/Http/Middleware/RoomAuthenticate.php
  • app/Services/RoomFileService.php
  • resources/js/components/RoomJoinButton.vue
  • resources/js/components/RoomTabFiles.vue
  • resources/js/components/RoomTabFilesViewButton.vue
  • resources/js/components/RoomTabRecordings.vue
  • resources/js/components/RoomTabRecordingsViewButton.vue
  • resources/js/constants/httpCustomErrorMessages.js
  • resources/js/views/RoomsView.vue
  • resources/views/errors/404.blade.php
  • routes/api.php
  • routes/web.php
  • tests/Backend/Feature/api/v1/RecordingTest.php
  • tests/Backend/Feature/api/v1/Room/FileTest.php
  • tests/Backend/Feature/api/v1/Room/RoomTest.php
  • tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js
💤 Files with no reviewable changes (3)
  • app/Http/Controllers/api/v1/RecordingFormatController.php
  • routes/api.php
  • resources/js/components/RoomTabFilesViewButton.vue

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
tests/Backend/Feature/api/v1/RecordingTest.php (1)

679-682: Minor: Unusual method chain formatting.

The method chain break between lines 681-682 is syntactically valid but unconventional. Consider reformatting for consistency with the rest of the file.

Suggested formatting
         // Access as guest with valid room auth token
-        $this->withCookies([
-            session()->getName() => $currentSession->id,
-        ])->
-        get(route('rooms.recordings.formats.show', [
+        $this->withCookies([
+            session()->getName() => $currentSession->id,
+        ])->get(route('rooms.recordings.formats.show', [
             'room' => $recording->room->id,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/Backend/Feature/api/v1/RecordingTest.php` around lines 679 - 682, The
test's method chaining for withCookies() -> get() is split across lines in an
unconventional way; locate the call to withCookies(...) followed by
->get(route('rooms.recordings.formats.show', ...)) and reformat the chain so the
arrow and method are consistently placed (either keep the -> at the end of the
withCookies(...) line or put get(...) on the same line) to match the file's
existing chaining style (adjust the call wrapping of withCookies() and the
subsequent get() invocation accordingly).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Http/Controllers/RecordingController.php`:
- Around line 52-53: Replace the insecure substring check with a proper
realpath-based containment check: resolve realpath() for both the requested
$absFilePath and the allowed directory
(Storage::disk('recordings')->path($allowedDir)), verify neither realpath call
returned false (reject if they did), normalize directory path with a trailing
DIRECTORY_SEPARATOR, and then ensure the file path begins with the canonical
allowed-dir path (e.g. check that strpos($fileRealPath, $allowedDirReal) === 0
or use Str::startsWith on the resolved values) to prevent prefix-similar
bypasses; update the condition around $absFilePath/$allowedDir in
RecordingController to implement this.

In `@docker/app/nginx/snippets/security-header.conf`:
- Around line 17-19: The global Cross-Origin-Opener-Policy header
("same-origin") breaks popup->opener signaling used by new-tab-error.blade.php
and 404.blade.php; update the add_header directive that sets
Cross-Origin-Opener-Policy to use "same-origin-allow-popups" instead of
"same-origin" so window.opener.postMessage() continues to work for popups while
maintaining COOP protections.

---

Nitpick comments:
In `@tests/Backend/Feature/api/v1/RecordingTest.php`:
- Around line 679-682: The test's method chaining for withCookies() -> get() is
split across lines in an unconventional way; locate the call to withCookies(...)
followed by ->get(route('rooms.recordings.formats.show', ...)) and reformat the
chain so the arrow and method are consistently placed (either keep the -> at the
end of the withCookies(...) line or put get(...) on the same line) to match the
file's existing chaining style (adjust the call wrapping of withCookies() and
the subsequent get() invocation accordingly).

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 498d42d and d9aef78.

📒 Files selected for processing (3)
  • app/Http/Controllers/RecordingController.php
  • docker/app/nginx/snippets/security-header.conf
  • tests/Backend/Feature/api/v1/RecordingTest.php

Comment on lines 52 to 53
if (! str_contains($absFilePath, realpath(Storage::disk('recordings')->path($allowedDir)))) {
// prevent path transversal
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix path traversal check: str_contains is bypassable.

Line 52 uses substring matching for directory containment. That can be bypassed by prefix-similar paths (e.g., .../presentation2/...) and is unsafe if realpath($allowedDir) resolves to false. This is a file access boundary break.

🔒 Proposed fix
-        // Check if resolved requested file path is in allowed directory
-        if (! str_contains($absFilePath, realpath(Storage::disk('recordings')->path($allowedDir)))) {
+        $allowedAbsDir = realpath(Storage::disk('recordings')->path($allowedDir));
+
+        // Check if resolved requested file path is in allowed directory
+        if (
+            $allowedAbsDir === false ||
+            ! str_starts_with(
+                $absFilePath,
+                rtrim($allowedAbsDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR
+            )
+        ) {
             // prevent path transversal
             Log::notice('Attempted to access recording file outside of allowed directory', ['requestedFile' => $requestedFile]);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (! str_contains($absFilePath, realpath(Storage::disk('recordings')->path($allowedDir)))) {
// prevent path transversal
$allowedAbsDir = realpath(Storage::disk('recordings')->path($allowedDir));
// Check if resolved requested file path is in allowed directory
if (
$allowedAbsDir === false ||
! str_starts_with(
$absFilePath,
rtrim($allowedAbsDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR
)
) {
// prevent path transversal
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Http/Controllers/RecordingController.php` around lines 52 - 53, Replace
the insecure substring check with a proper realpath-based containment check:
resolve realpath() for both the requested $absFilePath and the allowed directory
(Storage::disk('recordings')->path($allowedDir)), verify neither realpath call
returned false (reject if they did), normalize directory path with a trailing
DIRECTORY_SEPARATOR, and then ensure the file path begins with the canonical
allowed-dir path (e.g. check that strpos($fileRealPath, $allowedDirReal) === 0
or use Str::startsWith on the resolved values) to prevent prefix-similar
bypasses; update the condition around $absFilePath/$allowedDir in
RecordingController to implement this.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
CHANGELOG.md (1)

25-25: Clarify the failure trigger in changelog wording

At Line 25, consider explicitly mentioning the async access-link flow as the trigger (not just generic new-window restrictions), so the fix context is more precise for readers.

Suggested wording
-- Recording access blocked in some browsers due to restrictions on opening new windows without user interaction ([`#2851`], [`#2901`])
+- Recording access could fail in some browsers when the access link was generated asynchronously before opening a new tab, triggering user-interaction restrictions ([`#2851`], [`#2901`])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CHANGELOG.md` at line 25, Update the changelog entry that currently reads
"Recording access blocked in some browsers due to restrictions on opening new
windows without user interaction ([`#2851`], [`#2901`])" to explicitly state that
the failure was triggered by the async access-link flow (not just generic
new-window restrictions); edit the sentence to mention "async access-link flow"
as the trigger and keep the issue references ([`#2851`], [`#2901`]) intact so
readers understand the precise context of the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@CHANGELOG.md`:
- Line 25: Update the changelog entry that currently reads "Recording access
blocked in some browsers due to restrictions on opening new windows without user
interaction ([`#2851`], [`#2901`])" to explicitly state that the failure was
triggered by the async access-link flow (not just generic new-window
restrictions); edit the sentence to mention "async access-link flow" as the
trigger and keep the issue references ([`#2851`], [`#2901`]) intact so readers
understand the precise context of the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 80dc2ae1-a358-4f7b-8029-daf44e7c5f87

📥 Commits

Reviewing files that changed from the base of the PR and between 92e172f and bc68c9f.

📒 Files selected for processing (1)
  • CHANGELOG.md

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (2)
tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js (2)

28-71: Extract repeated link/message assertions into test helpers

The same assertion and postMessage logic is duplicated across multiple scenarios. This makes future route or message contract changes expensive and error-prone.

♻️ Proposed refactor
+const assertRecordingFormatLink = ({ button, formatTypeKey, formatId, auth }) => {
+  cy.get(`[data-test="${button}"]`)
+    .should("be.visible")
+    .and("include.text", formatTypeKey)
+    .then(($a) => {
+      const url = new URL($a.attr("href"));
+      expect(url.pathname).to.eq(
+        `/rooms/abc-def-123/recordings/e0cfa18c5fd75a42bd7947d8549321b03abf1daf-1660728035/formats/${formatId}`,
+      );
+      if (auth) {
+        expect(url.searchParams.get("room_auth_token")).to.eq(auth.token);
+        expect(url.searchParams.get("room_auth_token_type")).to.eq(String(auth.type));
+      }
+    })
+    .and("have.attr", "rel", "opener")
+    .and("have.attr", "target", "_blank");
+};
+
+const postRecordingMessage = (type) => {
+  cy.window().then(($window) => {
+    $window.postMessage(type ? { type } : null, Cypress.config("baseUrl"));
+  });
+};

Also applies to: 136-179, 360-403, 247-252, 291-296, 457-462, 488-493, 522-527, 563-568, 628-633, 654-656, 666-670, 679-685, 716-721

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

In `@tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js` around lines 28
- 71, The repeated assertions for recording format links (examples: the
cy.get('[data-test="notes-button"]') / podcast-button / presentation-button /
screenshare-button blocks) should be extracted into a reusable Cypress helper or
custom command (e.g., assertRecordingFormatButton or
Cypress.Commands.add('assertRecordingFormatButton')). Implement the helper to
accept the data-test selector (or button name), the expected i18n text key
(e.g., "rooms.recordings.format_types.notes") and the format id (1–4) and then
run the chain of .should checks including href composition with
Cypress.config("baseUrl"), rel and target assertions; replace each duplicated
block with calls to that helper across all listed occurrences (lines referencing
notes-button, podcast-button, presentation-button, screenshare-button and the
other similar selectors).

31-36: Prefer semantic URL assertions over full-string equality

Current href checks are very strict on serialization details. Asserting pathname + required query params is less flaky while still validating behavior.

Also applies to: 42-47, 53-58, 65-69, 140-144, 151-155, 162-166, 173-177, 364-368, 375-379, 386-390, 397-401

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

In `@tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js` around lines 31
- 36, Replace the brittle full-string href equality checks in the RoomsView
recordings tests with semantic URL assertions: for each occurrence of the chain
that calls .and("have.attr","href", Cypress.config("baseUrl") + "...") (e.g., in
RoomsViewRecordingsRecordingActions.cy.js where the href is built via
Cypress.config("baseUrl") + "/rooms/abc-def-123/recordings/.../formats/1"),
parse the anchor's href using the URL API inside a .should callback and assert
url.pathname equals the expected path
("/rooms/abc-def-123/recordings/.../formats/1") and that required query params
exist/have expected values via url.searchParams.has/.get; apply this change to
all listed occurrences (lines around the given diffs and the other ranges in the
comment) so tests validate pathname and necessary query params instead of exact
serialized baseUrl + href string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@resources/js/components/RoomTabRecordingsViewButton.vue`:
- Around line 114-132: The viewFormatUrl function currently concatenates strings
using settingsStore.getSetting("general.base_url") and router.resolve(...).href
and appends raw room_auth_token fields, which can produce malformed URLs and
unescaped params; change viewFormatUrl to use the URL constructor with a safe
fallback base (e.g. "http://localhost/") when general.base_url is falsy, build
the path using router.resolve(...).href appended to that URL object, and add
token values via URL.searchParams.set to ensure proper encoding (use
props.roomAuthToken.id and props.roomAuthToken.type only when present). Ensure
you update only the viewFormatUrl helper so it returns the properly encoded
href.

---

Nitpick comments:
In `@tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js`:
- Around line 28-71: The repeated assertions for recording format links
(examples: the cy.get('[data-test="notes-button"]') / podcast-button /
presentation-button / screenshare-button blocks) should be extracted into a
reusable Cypress helper or custom command (e.g., assertRecordingFormatButton or
Cypress.Commands.add('assertRecordingFormatButton')). Implement the helper to
accept the data-test selector (or button name), the expected i18n text key
(e.g., "rooms.recordings.format_types.notes") and the format id (1–4) and then
run the chain of .should checks including href composition with
Cypress.config("baseUrl"), rel and target assertions; replace each duplicated
block with calls to that helper across all listed occurrences (lines referencing
notes-button, podcast-button, presentation-button, screenshare-button and the
other similar selectors).
- Around line 31-36: Replace the brittle full-string href equality checks in the
RoomsView recordings tests with semantic URL assertions: for each occurrence of
the chain that calls .and("have.attr","href", Cypress.config("baseUrl") + "...")
(e.g., in RoomsViewRecordingsRecordingActions.cy.js where the href is built via
Cypress.config("baseUrl") + "/rooms/abc-def-123/recordings/.../formats/1"),
parse the anchor's href using the URL API inside a .should callback and assert
url.pathname equals the expected path
("/rooms/abc-def-123/recordings/.../formats/1") and that required query params
exist/have expected values via url.searchParams.has/.get; apply this change to
all listed occurrences (lines around the given diffs and the other ranges in the
comment) so tests validate pathname and necessary query params instead of exact
serialized baseUrl + href string.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 018b9660-2eb8-4912-bab4-1b807fe3219a

📥 Commits

Reviewing files that changed from the base of the PR and between bc68c9f and 7c09edc.

📒 Files selected for processing (2)
  • resources/js/components/RoomTabRecordingsViewButton.vue
  • tests/Frontend/e2e/RoomsViewRecordingsRecordingActions.cy.js

@Sabr1n4W Sabr1n4W requested a review from samuelwei March 4, 2026 16:12
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.

Recording access not working in restrictive browsers

1 participant