Skip to content

PIN pairing fails with "Missing CSRF token" - web UI never fetches/attaches the token #5467

Description

@this-lincoln

Is there an existing issue for this?

  • I have searched the existing issues

Is your issue described in the documentation?

  • I have read the documentation

Is your issue present in the latest beta/pre-release?

I'm too lazy to test

Describe the Bug

Pairing a Moonlight client via the web UI's PIN Pairing page (/pin) always fails with a "CSRF Protection Error" toast, even though csrf_allowed_origins in sunshine.conf already lists the exact origin being used to access the UI.

I confirmed this is not an origin allow-list problem. Using the browser's dev tools, I instrumented window.fetch on the /pin page and captured the exact request the "Send" button issues:

POST ./api/pin
Headers: { "Content-Type": "application/json" }
Body: {"pin":"1234","name":"test-device"}

There is no X-CSRF-Token header, and the page never calls GET /api/csrf-token beforehand to obtain one. The server correctly rejects this with {"error":"Missing CSRF token","status":false,"status_code":400}.

I confirmed the CSRF mechanism itself works fine server-side: manually fetching a token and attaching it as X-CSRF-Token on the same POST /api/pin request succeeds (200, {"status":true}). So the bug is specifically that the PIN Pairing page's own frontend code never fetches or attaches the CSRF token before submitting.

Expected Behavior

Entering a valid PIN and Device Name and clicking "Send" should successfully pair the Moonlight client, the same way manually attaching a fetched X-CSRF-Token to the same request does.

Additional Context

Steps to reproduce:

  1. Configure csrf_allowed_origins in sunshine.conf to include the exact origin used to access the web UI (ruling out an origin-allowlist problem).
    1. Navigate to /pin, enter any PIN and device name, click Send.
    1. Observe the "CSRF Protection Error" toast (added in CSRF failures are not apparent to users #4855 / v2026.516.143833) and a 400 response from POST /api/pin with body {"error":"Missing CSRF token",...}.
      Workaround that confirms the CSRF mechanism itself is fine: call GET /api/csrf-token (Basic Auth) to obtain a token, then POST /api/pin with that token in an X-CSRF-Token header. That request succeeds normally. It's specifically the /pin page's own frontend code that never performs step 1 of that flow.

Related but distinct issues I found while searching before filing this: #4855 (about the error being invisible to users, fixed by making the toast appear - which is how I could see this error at all) and #5059 (a feature request to disable CSRF entirely for LAN/dynamic-IP use, not this bug). Neither covers the frontend not sending the token in the first place.

Happy to test a fix or provide more info - this is on a fresh Flatpak install with only sunshine.conf hand-edited for csrf_allowed_origins, nothing else customized.Pairing a Moonlight client via the web UI's PIN Pairing page (/pin) always fails with a "CSRF Protection Error" toast, even though csrf_allowed_origins in sunshine.conf already lists the exact origin being used to access the UI.

I confirmed this is not an origin allow-list problem. Using the browser's dev tools, I instrumented window.fetch on the /pin page and captured the exact request the "Send" button issues:

POST ./api/pin
Headers: { "Content-Type": "application/json" }
Body: {"pin":"1234","name":"test-device"}

There is no X-CSRF-Token header, and the page never calls GET /api/csrf-token beforehand to obtain one. The server correctly rejects this with {"error":"Missing CSRF token","status":false,"status_code":400}.

I confirmed the CSRF mechanism itself works fine server-side: manually fetching a token and attaching it as X-CSRF-Token on the same POST /api/pin request succeeds (200, {"status":true}). So the bug is specifically that the PIN Pairing page's own frontend code never fetches or attaches the CSRF token before submitting.

Host Operating System

Linux

Operating System Version

SteamOS 3.8.16 (build 20260716.1) on ROG Ally X

Architecture

amd64/x86_64

Package

Linux - flathub/flatpak

GPU Type

AMD

GPU Model

AMD Radeon 780M (integrated, Ryzen Z1 Extreme APU, Phoenix1)

GPU Driver/Mesa Version

Mesa 25.3.0.213835.radeonsi_25.3.0 (radeonsi)

Capture Method

None

Apps

Log output

[2026-08-01 11:01:13.275]: Info: Sunshine version: 2026.516.143833 commit: 14ffa6fdaa53f7b51512be2b3d24f3939695403c
[2026-08-01 11:01:13.275]: Info: Package Publisher: LizardByte
[2026-08-01 11:01:13.275]: Info: Publisher Website: https://app.lizardbyte.dev
[2026-08-01 11:01:13.275]: Info: Get support: https://app.lizardbyte.dev/support
[2026-08-01 11:01:13.275]: Info: config: 'csrf_allowed_origins' = [redacted]
[2026-08-01 11:01:13.275]: Error: Couldn't load cuda: -1
[2026-08-01 11:01:13.275]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:13.276]: Info: [wayland] Found interface: zxdg_output_manager_v1(32) version 3
[2026-08-01 11:01:13.276]: Info: [wayland] Found interface: zwp_linux_dmabuf_v1(57) version 5
[2026-08-01 11:01:13.276]: Info: [wayland] Found interface: wl_output(64) version 4
[2026-08-01 11:01:13.276]: Warning: [wlgrab] Missing Wayland wire for wlr-export-dmabuf
[2026-08-01 11:01:13.276]: Error: Failed to gain CAP_SYS_ADMIN
[2026-08-01 11:01:13.276]: Info: /dev/dri/card0 -> amdgpu
[2026-08-01 11:01:13.278]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:13.278]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:13.278]: Info: [wayland] Found interface: zxdg_output_manager_v1(32) version 3
[2026-08-01 11:01:13.278]: Info: [wayland] Found interface: zwp_linux_dmabuf_v1(57) version 5
[2026-08-01 11:01:13.278]: Info: [wayland] Found interface: wl_output(64) version 4
[2026-08-01 11:01:13.278]: Info: [wayland] Resolution: 1920x1080
[2026-08-01 11:01:13.278]: Info: [wayland] Offset: 0x0
[2026-08-01 11:01:13.279]: Info: [wayland] Logical size: 1920x1080
[2026-08-01 11:01:13.279]: Info: [wayland] Name: eDP-1
[2026-08-01 11:01:13.279]: Info: [wayland] Found monitor: Thermotrex Corporation eDP-1-TL070FVXS01-0
[2026-08-01 11:01:13.279]: Info: -------- Start of KMS monitor list --------
[2026-08-01 11:01:13.279]: Warning: Mismatch on expected Resolution compared to actual resolution: 0x0 vs 1920x1080
[2026-08-01 11:01:13.279]: Info: Monitor 0 is eDP-1: Thermotrex Corporation eDP-1-TL070FVXS01-0
[2026-08-01 11:01:13.279]: Info: --------- End of KMS monitor list ---------
[2026-08-01 11:01:13.279]: Info: [portalgrab] Loaded portal restore token from disk
[2026-08-01 11:01:13.295]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:13.295]: Info: [wayland] Found interface: zxdg_output_manager_v1(32) version 3
[2026-08-01 11:01:13.295]: Info: [wayland] Found interface: zwp_linux_dmabuf_v1(57) version 5
[2026-08-01 11:01:13.295]: Info: [wayland] Found interface: wl_output(64) version 4
[2026-08-01 11:01:13.295]: Info: [wayland] Resolution: 1920x1080
[2026-08-01 11:01:13.295]: Info: [wayland] Offset: 0x0
[2026-08-01 11:01:13.295]: Info: [wayland] Logical size: 1920x1080
[2026-08-01 11:01:13.295]: Info: [wayland] Name: eDP-1
[2026-08-01 11:01:13.295]: Info: [wayland] Found monitor: Thermotrex Corporation eDP-1-TL070FVXS01-0
[2026-08-01 11:01:13.295]: Warning: [portalgrab] Falling back to position 0x0 for stream with resolution 1920x1080on pipewire node 108
[2026-08-01 11:01:13.297]: Info: [portalgrab] Found stream for display id/name: 'eDP-1' position: 0x0 resolution: 1920x1080
[2026-08-01 11:01:13.314]: Warning: Gamepad ds5 is disabled due to Permission denied
[2026-08-01 11:01:13.314]: Info: // Testing for available encoders, this may generate errors. You can safely ignore those errors. //
[2026-08-01 11:01:13.314]: Info: Trying encoder [nvenc]
[2026-08-01 11:01:13.314]: Info: Screencasting with XDG portal
[2026-08-01 11:01:13.314]: Info: [pipewire] Requested frame rate [60/1, approx. 60 fps]
[2026-08-01 11:01:13.314]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:13.330]: Info: [portalgrab] Loaded portal restore token from disk
[2026-08-01 11:01:13.343]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:13.343]: Info: [wayland] Found interface: zxdg_output_manager_v1(32) version 3
[2026-08-01 11:01:13.343]: Info: [wayland] Found interface: zwp_linux_dmabuf_v1(57) version 5
[2026-08-01 11:01:13.343]: Info: [wayland] Found interface: wl_output(64) version 4
[2026-08-01 11:01:13.343]: Info: [wayland] Resolution: 1920x1080
[2026-08-01 11:01:13.343]: Info: [wayland] Offset: 0x0
[2026-08-01 11:01:13.343]: Info: [wayland] Logical size: 1920x1080
[2026-08-01 11:01:13.343]: Info: [wayland] Name: eDP-1
[2026-08-01 11:01:13.343]: Info: [wayland] Found monitor: Thermotrex Corporation eDP-1-TL070FVXS01-0
[2026-08-01 11:01:13.343]: Warning: [portalgrab] Falling back to position 0x0 for stream with resolution 1920x1080on pipewire node 108
[2026-08-01 11:01:13.345]: Info: [[2026-08-01 11:08:57.882]: Info: Web UI: [192.168.0.80] -- not authorized
[2026-08-01 11:10:08.420]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://192.168.0.40:47990
[2026-08-01 11:10:08.420]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:10:18.764]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://192.168.0.40:47990
[2026-08-01 11:10:18.764]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:11:23.828]: Info: Web UI: [192.168.0.80] -- not authorized
[2026-08-01 11:11:50.561]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://sunshine.home.arpa:47990
[2026-08-01 11:11:50.561]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:13:32.696]: Info: Web UI: [192.168.0.80] -- not authorized
[2026-08-01 11:13:32.722]: Info: Web UI: [192.168.0.80] -- not authorized
[2026-08-01 11:13:32.745]: Info: Web UI: [192.168.0.80] -- not authorized
[2026-08-01 11:14:23.846]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://sunshine.home.arpa:47990
[2026-08-01 11:14:23.846]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:14:43.730]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://sunshine.home.arpa:47990
[2026-08-01 11:14:43.730]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:14:48.982]: Info: Web UI: [192.168.0.80] -- not authorized
[2026-08-01 11:14:58.773]: Info: Web UI: [192.168.0.80] -- not authorized
[2026-08-01 11:15:54.450]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://sunshine.home.arpa:47990
[2026-08-01 11:15:54.450]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:16:08.213]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://sunshine.home.arpa:47990
[2026-08-01 11:16:08.213]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:18:57.052]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://192.168.0.40:47990
[2026-08-01 11:18:57.053]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:19:11.575]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://192.168.0.40:47990
[2026-08-01 11:19:11.575]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:19:26.695]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://192.168.0.40:47990
[2026-08-01 11:19:26.695]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
[2026-08-01 11:21:35.346]: Error: Web UI: [192.168.0.80] -- CSRF protection blocked request from origin: https://192.168.0.40:47990
[2026-08-01 11:21:35.346]: Error: Web UI: To allow this origin, add it to the 'csrf_allowed_origins' option in your Sunshine configuration
et: 0x0
[2026-08-01 11:01:14.898]: Info: [wayland] Logical size: 1920x1080
[2026-08-01 11:01:14.898]: Info: [wayland] Name: eDP-1
[2026-08-01 11:01:14.898]: Info: [wayland] Found monitor: Thermotrex Corporation eDP-1-TL070FVXS01-0
[2026-08-01 11:01:14.900]: Info: [pipewire] Connected to pipewire version 1.6.4
[2026-08-01 11:01:14.917]: Info: [pipewire] Pipewire Error, id:2 seq:13 message: no more input formats
[2026-08-01 11:01:16.414]: Info: Creating encoder [h264_vulkan]
[2026-08-01 11:01:16.414]: Info: Color coding: SDR (Rec. 601)
[2026-08-01 11:01:16.414]: Info: Color depth: 8-bit
[2026-08-01 11:01:16.414]: Info: Color range: JPEG
[2026-08-01 11:01:16.462]: Info: Streaming bitrate is 1000000
[2026-08-01 11:01:16.462]: Info: Vulkan encode using GPU: AMD Ryzen Z1 Extreme (RADV PHOENIX)
[2026-08-01 11:01:16.474]: Info: Creating encoder [hevc_vulkan]
[2026-08-01 11:01:16.474]: Info: Color coding: SDR (Rec. 601)
[2026-08-01 11:01:16.474]: Info: Color depth: 8-bit
[2026-08-01 11:01:16.474]: Info: Color range: JPEG
[2026-08-01 11:01:16.514]: Info: Streaming bitrate is 1000000
[2026-08-01 11:01:16.515]: Info: Vulkan encode using GPU: AMD Ryzen Z1 Extreme (RADV PHOENIX)
[2026-08-01 11:01:16.526]: Info: Creating encoder [av1_vulkan]
[2026-08-01 11:01:16.526]: Info: Color coding: SDR (Rec. 601)
[2026-08-01 11:01:16.526]: Info: Color depth: 8-bit
[2026-08-01 11:01:16.526]: Info: Color range: JPEG
[2026-08-01 11:01:16.565]: Info: Streaming bitrate is 1000000
[2026-08-01 11:01:16.566]: Info: Vulkan encode using GPU: AMD Ryzen Z1 Extreme (RADV PHOENIX)
[2026-08-01 11:01:16.575]: Info: Screencasting with XDG portal
[2026-08-01 11:01:16.575]: Info: [pipewire] Requested frame rate [60/1, approx. 60 fps]
[2026-08-01 11:01:16.575]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:16.583]: Info: [portalgrab] Loaded portal restore token from disk
[2026-08-01 11:01:16.598]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:16.599]: Info: [wayland] Found interface: zxdg_output_manager_v1(32) version 3
[2026-08-01 11:01:16.599]: Info: [wayland] Found interface: zwp_linux_dmabuf_v1(57) version 5
[2026-08-01 11:01:16.599]: Info: [wayland] Found interface: wl_output(64) version 4
[2026-08-01 11:01:16.599]: Info: [wayland] Resolution: 1920x1080
[2026-08-01 11:01:16.599]: Info: [wayland] Offset: 0x0
[2026-08-01 11:01:16.599]: Info: [wayland] Logical size: 1920x1080
[2026-08-01 11:01:16.599]: Info: [wayland] Name: eDP-1
[2026-08-01 11:01:16.599]: Info: [wayland] Found monitor: Thermotrex Corporation eDP-1-TL070FVXS01-0
[2026-08-01 11:01:16.599]: Warning: [portalgrab] Falling back to position 0x0 for stream with resolution 1920x1080on pipewire node 108
[2026-08-01 11:01:16.601]: Info: [portalgrab] Using first available stream as no matching stream was found for: ''
[2026-08-01 11:01:16.601]: Info: [pipewire] Streaming display '' offset: 0x0 resolution: 1920x1080
[2026-08-01 11:01:16.601]: Info: [wayland] Found display [wayland-0]
[2026-08-01 11:01:16.601]: Info: [wayland] Found interface: zxdg_output_manager_v1(32) version 3
[2026-08-01 11:01:16.601]: Info: [wayland] Found interface: zwp_linux_dmabuf_v1(57) version 5
[2026-08-01 11:01:16.601]: Info: [wayland] Found interface: wl_output(64) version 4
[2026-08-01 11:01:16.601]: Info: [wayland] Resolution: 1920x1080
[2026-08-01 11:01:16.601]: Info: [wayland] Offset: 0x0
[2026-08-01 11:01:16.601]: Info: [wayland] Logical size: 1920x1080
[2026-08-01 11:01:16.601]: Info: [wayland] Name: eDP-1
[2026-08-01 11:01:16.601]: Info: [wayland] Found monitor: Thermotrex Corporation eDP-1-TL070FVXS01-0
[2026-08-01 11:01:16.603]: Info: [pipewire] Connected to pipewire version 1.6.4
[2026-08-01 11:01:16.621]: Info: [pipewire] Pipewire Error, id:2 seq:13 message: no more input formats
[2026-08-01 11:01:18.115]: Info: Creating encoder [hevc_vulkan]
[2026-08-01 11:01:18.115]: Info: Color coding: SDR (Rec. 709)
[2026-08-01 11:01:18.115]: Info: Color depth: 10-bit
[2026-08-01 11:01:18.115]: Info: Color range: JPEG
[2026-08-01 11:01:18.164]: Info: Streaming bitrate is 1000000
[2026-08-01 11:01:18.166]: Info: Vulkan encode using GPU: AMD Ryzen Z1 Extreme (RADV PHOENIX)
[2026-08-01 11:01:18.181]: Info: Creating encoder [av1_vulkan]
[2026-08-01 11:01:18.181]: Info: Color coding: SDR (Rec. 709)
[2026-08-01 11:01:18.181]: Info: Color depth: 10-bit
[2026-08-01 11:01:18.181]: Info: Color range: JPEG
[2026-08-01 11:01:18.218]: Info: Streaming bitrate is 1000000
[2026-08-01 11:01:18.219]: Info: Vulkan encode using GPU: AMD Ryzen Z1 Extreme (RADV PHOENIX)
[2026-08-01 11:01:18.230]: Info: 
[2026-08-01 11:01:18.230]: Info: // Ignore any errors mentioned above, they are not relevant. //
[2026-08-01 11:01:18.230]: Info: 
[2026-08-01 11:01:18.230]: Info: Found H.264 encoder: h264_vulkan [vulkan]
[2026-08-01 11:01:18.230]: Info: Found HEVC encoder: hevc_vulkan [vulkan]
[2026-08-01 11:01:18.230]: Info: Found AV1 encoder: av1_vulkan [vulkan]
[2026-08-01 11:01:18.231]: Info: Starting system tray
[2026-08-01 11:01:18.231]: Fatal: Couldn't bind RTSP server to port [48010], Endereço já em uso
[2026-08-01 11:01:18.232]: Error: Failed to create client: Daemon not running
[2026-08-01 11:01:18.258]: Info: System tray created
[2026-08-01 11:01:18.258]: Info: Starting main loop

Online logs

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions