Authentication

QR login

QR login is what the Telegram desktop app uses on its log-in screen. Show a QR code, the user scans it from their phone, your client is authenticated.

Why QR

Compared to phone+code, QR login has two advantages: no code to type and no SMS or service message that could be intercepted. The user authorises a new session from inside an already-authenticated client, which makes it the recommended flow for second devices and web sign-ins.

Generating the token

client.Connect()

qr, err := client.QRLogin()
if err != nil {
	log.Fatal(err)
}

fmt.Println("scan this URL with the Telegram app:")
fmt.Println(qr.URL())

if err := qr.Wait(); err != nil {
	log.Fatal(err)
}

log.Println("logged in")
client.Connect()

qr, err := client.QRLogin()
if err != nil {
	log.Fatal(err)
}

fmt.Println("scan this URL with the Telegram app:")
fmt.Println(qr.URL())

if err := qr.Wait(); err != nil {
	log.Fatal(err)
}

log.Println("logged in")

QRLogin calls auth.exportLoginToken and returns a *QrToken with three things: URL() returns the tg://login?token=... URL that encodes the token, Token() returns the raw token bytes (handy if you render the QR yourself), and Wait() blocks until either the token is accepted, the user denies the request, or the token expires.

Rendering the QR

Encode qr.URL() with any QR-code library. In the terminal: qrterminal draws coloured cells. In a web app: pass the URL to your front-end and render with qrcodejs or an SVG generator. In a TUI: go-qrcode.

Waiting for the scan

qr.Wait() polls the server. Telegram pushes updateLoginToken when the user approves or denies the request, and gogram translates that into the return value of Wait.

The token has a server-side TTL of about 30 seconds; gogram refreshes it automatically until Timeout elapses:

qr, _ := client.QRLogin(telegram.QrOptions{
	Timeout: 5 * time.Minute,
})
qr, _ := client.QRLogin(telegram.QrOptions{
	Timeout: 5 * time.Minute,
})

Two-factor on top

If the account has a cloud password, scanning the QR is only half the auth — the user still has to enter the 2FA password somewhere. Provide it ahead of time and gogram passes it through the SRP check:

qr, _ := client.QRLogin(telegram.QrOptions{
	Password: os.Getenv("TG_2FA_PASSWORD"),
})
qr, _ := client.QRLogin(telegram.QrOptions{
	Password: os.Getenv("TG_2FA_PASSWORD"),
})

Without Password, Wait returns a SESSION_PASSWORD_NEEDED error and you can prompt the user before calling CheckPassword.