SCREECHVR AUTH REVIEW V15: PLAYFAB IDENTITY + PHOTON AUTH Date: 2026-10-30 Scope: Read-only source and documentation review. No code, Unity files, revisions, dashboards, or production services changed. V08 remains an unverified draft. SUMMARY Current code turns the Meta ID returned to the client into a PlayFab Custom ID, without proving that ID to PlayFab. Photon normally uses a PlayFab-issued Photon token, but PhotonVRManager falls back to a connection with no AuthValues after waiting 20 seconds. These are source-confirmed trust weaknesses. Whether the Photon fallback is admitted by production Photon settings is unverified. 1. PLAYFAB IDENTITY Source and flow - `! Screech VR/Screech VR/Assets/Scripts/Oculus_Login.cs` - `Start()` initializes Meta Platform and requests `Users.GetLoggedInUser()`. `OnUserLogin()` assigns `msg.Data.ID.ToString()` to `OculusID` and invokes `Login()` (source lines approximately 100-181; local source checked). - `Login()` calls PlayFab `LoginWithCustomID` with `CustomId = OculusID` and `CreateAccount = true`. The client sends no Meta proof to PlayFab/backend. - `OnLoginSuccess()` stores `PlayFabId` and `SessionTicket`, then requests a Photon token. Session ticket is used by authenticated PlayFab client calls; source inspection found no Meta proof validation or server-side identity binding in this flow. Protect tickets as credentials; do not log them. - `AppEntitlementCheck.cs` is a separate client entitlement check and does not independently authenticate the Meta ID to PlayFab. Its failure behavior is not a substitute for account identity verification. Confirmed vulnerability vs uncertainty - Confirmed: `LoginWithCustomID` accepts the client-provided Meta ID without a server-verified proof in this path. PlayFab documents CustomId as a title- generated unique ID and warns that predictable/reused values risk account impersonation. A modified client can submit another known ID. This is an authentication design flaw; practical exploitability and any additional live restrictions are not established here. - Not verified: PlayFab title API policies, deployed server-side login controls, account linking/recovery policy, or production enforcement settings. Meta `getUserProof` - Official Meta User Verification documentation describes `Users.GetUserProof()` as a client-provided, one-use nonce; a trusted backend submits nonce plus user ID to Meta `https://graph.oculus.com/user_nonce_validate` using app credentials. A successful verification proves the Meta account identity for that request. - It is not safe to call the client API and trust its result. Backend must call Meta, ensure the nonce is one-use/fresh, bind the verified identity to the account, and keep Meta app secret exclusively server-side. Meta says User Verification does not replace Entitlement Check. User ID Data Use Checkup permission is required; app eligibility/DUC and SDK compatibility must be confirmed for this project. - Recommended flow: Unity obtains Meta user ID + proof nonce; sends both via TLS to backend; backend verifies with Meta S2S using secret, obtains/uses a stable verified account key (Meta documents org-scoped ID retrieval after verification), and maps it to a PlayFab account. Backend then issues/authorizes a short-lived game credential. Avoid retaining an unverified client ID as account authority. Migration must preserve existing PlayFab IDs, inventory, purchases, and recovery. - If direct PlayFab login is retained, a backend-mediated identity exchange must securely bind verified Meta identity to the intended PlayFab account; do not put PlayFab title secret or Meta app secret in Unity. Do not assume a custom ID derived in client code becomes secret or unforgeable. 2. PHOTON AUTHENTICATION Source and flow - `! Screech VR/Screech VR/Assets/Scripts/Oculus_Login.cs`: `OnLoginSuccess()` calls `PlayFabClientAPI.GetPhotonAuthenticationToken` with the configured Photon Realtime App ID; success stores `PhotonCustomAuthToken` and marks Photon readiness. The PlayFab session ticket authorizes that request; it is not itself the Photon token. - `! Screech VR/Screech VR/Assets/Scripts/PhotonVRManager.cs`: `Start()` starts `WaitForAuthThenConnect()` when `ConnectOnAwake` is set. The coroutine waits up to 20 seconds for `Oculus_Login.IsPhotonReady`; on success it calls `ConnectAuthenticated(PlayFab_Id, PhotonCustomAuthToken)`. That method sets `AuthenticationValues` to Custom and adds `username` (PlayFab ID) and `token` (PlayFab Photon token), then connects. - On timeout/not-ready it calls `Connect()`, which clears `PhotonNetwork.AuthValues` and connects without those credentials. This is a confirmed unauthenticated fallback in source. Photon documents anonymous clients as assigned a random ID when no authentication values are provided. - Token request failure does not make `IsPhotonReady` true; thus the 20-second wait reaches the same unauthenticated fallback. No credential-refresh/retry recovery is implemented in this manager's `OnDisconnected()` path; it updates state only. `OnConnectedToMaster()` may automatically join a room based on `JoinRoomOnConnect`. - `ConnectRandom()` sets auth values but only calls `JoinRandomRoom` in the inspected method; it is not evidence of a fresh authenticated connect. Audit its callers and connection lifecycle before changing it. Confirmed vs dashboard-dependent - Confirmed: fallback clears auth and attempts connection without PlayFab custom credentials; timeout and token-fetch failure can reach that code path. - Dashboard-dependent: Photon application’s custom auth provider URL, anonymous client option, fail-closed behavior when the provider is unavailable, app-ID association, and whether the live app accepts the fallback. No dashboard settings were accessed or verified. - Reconnect: no explicit application-level credential reacquisition is present in this manager. Photon may handle its own server-switch/reconnect token flow after an authenticated connection, but that does not make the initial unauthenticated fallback safe. Verify the exact PUN version and reconnect behavior in staging; do not assume reconnect bypasses or enforces auth. Required Photon dashboard configuration (do not change in this review) - Configure the Photon Realtime application’s Custom Server authentication provider URL as PlayFab documents: `https://.playfabapi.com/photon/authenticate` - Disable “Allow anonymous clients to connect, independently of configured providers” for the relevant app. Photon’s PlayFab integration marks this recommended; verify its exact setting in the dashboard. - Confirm the PlayFab Photon add-on is associated with the same Realtime App ID used by `GetPhotonAuthenticationToken`; configure/test Voice app authentication separately if its app ID differs. - Ensure Photon rejects failed/unavailable custom authentication rather than accepting fallback; test provider outage behavior and app IDs in a non-prod app. 3. RECOMMENDED IMPLEMENTATION ARCHITECTURE Unity changes (future work only) - Gate Photon connect on successful PlayFab login AND successful Photon token retrieval. On timeout/error, show retry/error state and do not call anonymous `Connect()`. Keep offline/dev paths explicit and unavailable in production. - Centralize auth state transitions: authenticated, retryable network failure, permanent credential/configuration failure, signed-out. Bound retries with backoff; do not start multiple concurrent login/token requests. - Reconnect by obtaining a fresh PlayFab Photon token when needed, setting Custom AuthValues, then connecting. Confirm PUN-managed server switches remain compatible. Never silently downgrade on reconnect. - For Meta identity, add `GetUserProof` only with backend S2S validation and an account migration/linking design. Keep all server credentials backend-only. Backend changes (future work only) - Add authenticated Meta-proof verification endpoint: validate Meta nonce and user ID server-to-server; enforce freshness/one-use, rate limit, strict input validation, TLS, secret storage, and audit without logging proof/credentials. - Bind verified stable Meta identity to PlayFab account. Define account recovery, duplicate-link behavior, migration of existing accounts, and rollback first. - For Photon custom auth, use PlayFab’s official Photon integration endpoint unless a concrete requirement demands a custom provider. Do not confuse Worker `/photonwh/*` webhooks with Photon player authentication: they handle webhook/ CCU events, not player credential validation. No general game-auth endpoint or Meta proof validator was found in `discord-bot/worker.js`. - Backend must never trust a PlayFab ID sent alone by client; validate the PlayFab session ticket server-side when custom backend authorization requires it. Keep Meta and PlayFab server secrets out of client builds. 4. FAILURE, RECONNECT, AND COMPATIBILITY POLICY - Meta proof service unavailable: do not create a new unverified account or silently bind a different identity. Offer retry; existing account access may follow a documented, verified recovery policy. - PlayFab login/token request timeout: bounded retry/backoff; no Photon connect. - Photon auth rejection/provider outage: surface retryable vs configuration failure; do not fall back to anonymous. Preserve room matchmaking after authentication succeeds. - Expired Photon credential/reconnect: obtain fresh token via valid PlayFab session, then reconnect authenticated. If PlayFab session expired, reauthenticate through the identity flow. Avoid duplicate Photon managers/connections. - Roll out with a staging Photon app and test accounts first. Existing users must retain PlayFab ID, data, currency, owned cosmetics, and IAP entitlements. 5. SMALL REGRESSION TEST PLAN 1. Existing legitimate Meta account logs in; same PlayFab ID and inventory remain. 2. Alter client-supplied Meta ID while proof belongs to another user: backend rejects mismatch; no account is created/linked to the victim. 3. Reuse an already-consumed Meta proof nonce: rejected; no session issued. 4. Meta unavailable/DUC or permission failure: clear recoverable error; no unverified identity fallback. 5. PlayFab login succeeds, Photon token succeeds: custom auth connects and room join/voice/matchmaking behavior is unchanged. 6. Delay/fail Photon-token request beyond 20 seconds: no unauthenticated connect; retry works after service recovery. 7. Reject token or disable custom-auth provider in staging: Photon refuses connection; no anonymous session is admitted. 8. Disconnect/reconnect and server switch: fresh/managed auth succeeds; expired credentials do not trigger an anonymous downgrade or duplicate connection. 9. Test Realtime and Voice app IDs/config independently; verify correct identity and normal room/voice behavior. 6. REMAINING UNKNOWNS - Live PlayFab title/API policies, deployed CloudScript/Functions, and any additional identity binding not present in inspected source. - Photon dashboard configuration, anonymous-client setting, provider outage policy, Realtime/Voice app IDs, and production connection path. - Meta DUC/User ID permission, app eligibility, Unity Platform SDK version/API availability in Unity 2021.3.32f1, and review requirements. - Whether Oculus ID vs org-scoped ID is the correct durable account key for this title; confirm with Meta documentation and migration constraints before design. - Exact PUN package version and reconnect/server-switch behavior in this project. - Source review was targeted, not a complete audit of all login callers or every scene’s PhotonVRManager configuration. OFFICIAL DOCUMENTATION CONSULTED - Meta Unity User Verification: https://developers.meta.com/vr/documentation/unity/ps-ownership/ - Meta S2S API basics/secrets: https://developers.meta.com/vr/documentation/unity/ps-s2s-basics/ - PlayFab LoginWithCustomID: https://learn.microsoft.com/en-us/rest/api/playfab/client/authentication/login-with-custom-id - PlayFab login best practices: https://learn.microsoft.com/en-us/gaming/playfab/identity/player-identity/login/login-basics-best-practices - PlayFab GetPhotonAuthenticationToken: https://learn.microsoft.com/en-us/rest/api/playfab/client/authentication/get-photon-authentication-token - Photon PUN PlayFab integration/dashboard setup: https://doc.photonengine.com/pun/current/reference/playfab - Photon PUN auth overview: https://doc.photonengine.com/pun/current/connection-and-authentication/authentication/overview-auth No code or production changes. This report is a focused supplement, not a replacement for V14. END OF REPORT