Go library for Telegram Login (manual implementation): Authorization Code Flow with PKCE and id_token validation via Telegram JWKS.
Documentation: Telegram Login manual implementation
go get github.com/statebyte/go-telegram-loginimport "github.com/statebyte/go-telegram-login/teleauth"- Create a
TeleAuthinstance. - Call
StartAuthto get an authorization URL +state+code_verifier(+ optionalnonce). - Redirect the user to the returned URL.
- In your callback handler, read
codeandstatefrom query params, then exchangecodefor tokens and validateid_token.
Minimal example (server-side):
package main
import (
"context"
"net/http"
"github.com/statebyte/go-telegram-login/teleauth"
)
func main() {
ta, _ := teleauth.New(teleauth.Config{
ClientID: 123456789,
ClientSecret: "BOT_CLIENT_SECRET",
RedirectURI: "https://example.com/auth/callback",
// Audience defaults to ClientID when omitted.
})
// Example: start authorization
http.HandleFunc("/auth/start", func(w http.ResponseWriter, r *http.Request) {
res, err := ta.StartAuth(r.Context(), teleauth.StartAuthOptions{
GenerateNonce: true,
// Scopes defaults to: ["openid", "profile", "phone"]
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// IMPORTANT:
// Store res.State and res.CodeVerifier (and res.Nonce if used) in a user session/CSRF-safe storage.
// Do not rely on client-provided values without checking them.
http.Redirect(w, r, res.URL, http.StatusFound)
})
// Example: callback handler
http.HandleFunc("/auth/callback", func(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
q := r.URL.Query()
code := q.Get("code")
state := q.Get("state")
_ = state // Verify it against the stored value from your session.
// IMPORTANT:
// Load codeVerifier (and expectedNonce if you enabled it) from your session.
codeVerifier := "CODE_VERIFIER_FROM_SESSION"
expectedNonce := "NONCE_FROM_SESSION" // or "" if you didn't include nonce
user, err := ta.ExchangeAndValidate(ctx, code, codeVerifier, expectedNonce)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}
_ = user
w.WriteHeader(http.StatusOK)
})
_ = http.ListenAndServe(":8080", nil)
}- Always validate
state(CSRF protection) on the callback. - Always validate the
id_tokensignature and claims server-side (this library does it via JWKS + RS256). - If you set
GenerateNonce: true, pass the returnednonceasexpectedNonceintoExchangeAndValidate.