Repository navigation
Technical Social Login
Reviewed on 2026-11-12 against the local Streamer changes for GHSA-9rvm-cfc4-q9g9 and GHSA-286v-rxqx-v42r. The latter changes are uncommitted; verify deployed source independently. LoginTwitter and LoginFacebook report plugin version 1.0; the binding implementation belongs to Streamer core. Provider configuration plugins and MobileManager must be inspected independently of a deployed core version.
objects/login.json.php and plugin/MobileManager/oauth2.php obtain a Hybridauth profile and call User::getSocialLoginUserId(). The web call passes emailVerified = true; the mobile call retains the helper's default false. The web flag also updates existing no-email accounts, including accounts first created through mobile and reattached legacy accounts, preserving the pre-binding behavior under unverifiedEmailsCanNOTLogin. Failure to persist that update refuses selection. Profiles with an email retain the existing createUserIfNotExists(email, ...) mapping: the account whose username is that email. The emailVerified argument is only the flag written to the account, not the provider's verification.
The provider's own verification is Hybridauth's $userProfile->emailVerified. Since GHSA-286v-rxqx-v42r, when it is set and differs from the email (Google and Yahoo leave it empty for email_verified = false), the email is ignored and the profile follows the no-email binding below: the email itself no longer authorizes account selection. Existing bindings and the legacy photo-based reattachment rules still apply. Adapters that leave it null (Apple, and Facebook's OIDC profile path) keep the email mapping. Facebook's access-token path, Twitter and LinkedIn copy the email into it. AVideo ships no GitHub login plugin. A user whose earlier login used an unverified Google email normally gets a new Google_<sub> account instead of the email-named one.
Without an email, a provider identifier is required. Display names do not authorize selection of an arbitrary local account. A binding is the socialLogin<Provider> key in users.externalOptions, whose value is the complete identifier as a string. Normal usernames are <Provider>_<identifier>. Names exceeding the users.user column's 45-character limit use up to 12 provider characters, an underscore and 32 hexadecimal SHA-256 characters derived from the provider and full identifier. The full stored binding is still checked; a username alone does not establish ownership.
The bundled Hybridauth adapters populate identifiers from Twitter id_str, Facebook/LinkedIn id and Yahoo/Google/Apple sub. These are source mappings, not verification of a provider's current live API behavior.
LoginWordPress::login() for a custom WordPress site still calls createUserIfNotExists() directly and is unaffected. The plugin's optional WordPress.com OAuth button uses the shared web/mobile endpoints and is affected: its adapter maps ID to the identifier and reports the exact email when verified, or an empty emailVerified otherwise. Unverified WordPress.com profiles therefore use the same provider-ID fallback.
New accounts receive their binding in the same INSERT as the account. createUserIfNotExists() accepts optional initial external options, applied only to new accounts; existing options are never replaced by that argument. Default group assignment and existing account creation rules remain in this helper.
The canonical username provides the fast lookup. For legacy or renamed accounts, a prepared LOCATE(?, FROM_BASE64(externalOptions)) query locates serialized key/value candidates. Every candidate's decoded top-level option is checked before use, so nested or unrelated values do not authenticate an account. This fallback scans stored options; it is not indexed and needs performance evaluation for very large user tables.
An unbound legacy display-name account can be reattached only if its email is empty, it is not an administrator, and its stored photo URL exactly matches the nonempty provider photo URL, with no default substring. An already bound account requires the same identifier. A failed legacy binding save refuses authentication and permits a later retry. Shared/custom photo URLs without that substring have not been validated across all providers; this remains a limit of the legacy migration heuristic.
For an object loaded with a user ID, User::login(true) now reloads that exact ID and requires active status instead of resolving its username through find() and its optional email alias. This prevents a validated social binding from logging into another account whose email matches the selected account's username. Objects constructed without an ID retain the existing username lookup. Password-based login is unchanged.
Email verification restrictions, session setup, remember-me credentials, last-login writes, AVideoPlugin::onUserSignIn() and session regeneration remain after account selection. LoginControl's 2FA/device behavior still runs through the existing hook; it is not bypassed by the binding helper.
Other trusted ID callers include Encoder callback authentication and recording workers. The change introduces no global Encoder/Streamer binding and changes no provider callback addresses, web popup handling or mobile success/error routes.
tests/Security/SocialLoginAccountBindingTest.php uses token-extracted production construction, load/save, external option, account creation, selection and login methods with in-memory SQL and framework infrastructure. The fixture loads no installation configuration and writes no repository files; tests restore their globals. Endpoint assertions inspect source rather than execute OAuth.
Real provider consent/callback flows, browser cookie delivery, LoginControl challenges and native mobile handoffs still require integration testing with configured provider applications. Automated tests do not establish those flows work end to end.
Start
Install and update
- Install on Ubuntu 24.04 or 26.04
- Installation Setup Page
- Scheduler cron job (required)
- Set up email
- Use HTTPS
- Own Encoder
- Docker
- Update AVideo
- Backup
- Secure file permissions
- Server command-line tools
- Hidden configuration options
Videos
- Upload a video
- Upload methods — visual guide
- Local Encoder (on your computer)
- Bulk upload from a server folder
- Import a channel
- Publish on YouTube and social media
- Access and payments — visual guide
- Privacy options
- Only for logged-in users
- Resolutions by user group
- Mandatory playback access rules
- Storage options
- Move and protect video files
- Video does not play
Users
Live
- Make a live stream
- Stream with OBS
- Own live server
- One live server, many sites
- Live server install errors
- Live does not play
Plugins
Problems
Other
- Translate your site
- Write a plugin (developers)
- Technical reference (developers and advanced administrators)
Old and retired guides