Conversation
Telegram Login speaks OpenID Connect (https://core.telegram.org/bots/telegram-login) but differs from the generic defaults in three ways: - Its discovery document has no userinfo_endpoint, and Telegram never returns an email, so the userinfo call made for an email-less id_token failed on the missing key. Every claim is already in the id_token. - The user id that bots and Mini Apps know is the numeric `id` claim of the `profile` scope; `sub` is a different, opaque value. getId() returns `id`, `profile` is always requested, and a token without `id` is refused. - There is no `email` scope, so the scopes default to `openid profile`. The provider is available as the `telegram` shorthand. Checked against Telegram with a real bot: the nonce round-trips through the redirect flow, client_secret_basic is accepted, `id` arrives as a numeric string, and the id_token is valid for 30 seconds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
atymic
left a comment
There was a problem hiding this comment.
Thanks for the PR!
Should we deprecate the old provider?
There was a problem hiding this comment.
Copilot review overview
🔵 Needs a closer look
Filter unsupported email scopes before adding Telegram’s required profile scope.
Review effort: Lite
Findings: None
What changed in this PR
Adds a built-in Telegram OpenID Connect provider with Telegram-specific claims, scopes, and userinfo handling.
Changes:
- Adds and registers the
telegramprovider. - Maps Telegram IDs, usernames, and avatars.
- Adds documentation and unit tests.
| File | Summary |
|---|---|
tests/OpenIDConnect/Unit/TelegramProviderTest.php |
Tests Telegram OIDC behavior. |
src/OpenIDConnect/README.md |
Documents configuration and provider behavior. |
src/OpenIDConnect/Providers/TelegramProvider.php |
Implements Telegram-specific OIDC handling. |
src/OpenIDConnect/OpenIDConnectServiceProvider.php |
Registers the provider shorthand. |
src/OpenIDConnect/docs/extending.md |
Documents the new shorthand. |
src/OpenIDConnect/config/oidc.php |
Updates configuration documentation. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
I wouldn't deprecate it just yet. Telegram does label the Login Widget as legacy, but it doesn't look like it's been switched off. I checked it myself back in April and it still worked. People who already use it would get a deprecation warning with no real reason to migrate. What I'd suggest instead is adding a note to the Telegram provider's docs page pointing to the OpenID Connect flow and the new telegram shorthand, so new users don't pick the legacy one by mistake. Also, a small heads-up: yesterday I sent an email to the address in the security policy about two issues, with a patch attached. Just making sure it didn't end up in spam. No rush, of course. |
The other built-in providers are named there, and it is what people search the docs page for.
Telegram Login speaks OpenID Connect now (docs), and the generic provider already does almost all the work. This adds a small
TelegramProviderfor the few places where Telegram differs, available as thetelegramshorthand.Some background: I've been running Telegram login and account linking in production since April through my own package, smotim/socialiteproviders-telegram-oidc. When the OpenID Connect provider landed here, I realised Telegram doesn't need a package of its own, just its quirks written down on top of this one. So here they are.
To avoid confusion with what's already here:
Telegramis the legacy Login Widget (hash-signed payload), andTelegramWebAppvalidates Mini AppinitData. Neither of them does OIDC.Where Telegram differs
userinfo_endpointkey. Every claim is already in the id_token, sogetUserByToken()returnsnull.subis an opaque value, not the user id people actually need. The id that bots, Mini Apps and the Bot API know is theidclaim, and it only comes with theprofilescope.getId()returnsid,substays ingetRaw(), andprofileis always requested. A token withoutidis refused rather than falling back tosub, because I learned the hard way that a silent fallback splits one person into two accounts.emailscope, so the defaults areopenid profile.preferred_usernameandpicture.Checked against the real thing
On top of the unit tests, I ran this branch against Telegram with a real bot:
client_secret_basicwithoutclient_idin the body is accepted;idarrives as a numeric string, not a number as in the docs example (the provider accepts both);clock_skewin the README.Happy to reshape
getId()=subfor consistency with the base class, I can switch it. For Telegram, though,idis what everyone ends up needing.getUserByToken(), the base provider could skip userinfo whenever discovery has nouserinfo_endpoint(it's RECOMMENDED, not REQUIRED, in OIDC Discovery 1.0). I can send that as a separate PR if you prefer the general fix.Tests are in
tests/OpenIDConnect/Unit/TelegramProviderTest.php, built on the existingInteractsWithOidcharness with a discovery document shaped like Telegram's. All 206 OpenID Connect tests pass on PHP 8.5, and Pint is clean.I paired with Claude Code on this; I reviewed every line and ran the flow against a real bot myself.