You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
155 lines
4.3 KiB
155 lines
4.3 KiB
3 years ago
|
package auth
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
|
||
|
"github.com/go-faster/errors"
|
||
|
|
||
|
"github.com/gotd/td/tg"
|
||
|
"github.com/gotd/td/tgerr"
|
||
|
)
|
||
|
|
||
|
// ErrPasswordInvalid means that password provided to Password is invalid.
|
||
|
//
|
||
|
// Note that telegram does not trim whitespace characters by default, check
|
||
|
// that provided password is expected and clean whitespaces if needed.
|
||
|
// You can use strings.TrimSpace(password) for this.
|
||
|
var ErrPasswordInvalid = errors.New("invalid password")
|
||
|
|
||
|
// Password performs login via secure remote password (aka 2FA).
|
||
|
//
|
||
|
// Method can be called after SignIn to provide password if requested.
|
||
|
func (c *Client) Password(ctx context.Context, password string) (*tg.AuthAuthorization, error) {
|
||
|
p, err := c.api.AccountGetPassword(ctx)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "get SRP parameters")
|
||
|
}
|
||
|
|
||
|
a, err := PasswordHash([]byte(password), p.SRPID, p.SRPB, p.SecureRandom, p.CurrentAlgo)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "compute password hash")
|
||
|
}
|
||
|
|
||
|
auth, err := c.api.AuthCheckPassword(ctx, &tg.InputCheckPasswordSRP{
|
||
|
SRPID: p.SRPID,
|
||
|
A: a.A,
|
||
|
M1: a.M1,
|
||
|
})
|
||
|
if tg.IsPasswordHashInvalid(err) {
|
||
|
return nil, ErrPasswordInvalid
|
||
|
}
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "check password")
|
||
|
}
|
||
|
result, err := checkResult(auth)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "check")
|
||
|
}
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
// SendCodeOptions defines how to send auth code to user.
|
||
|
type SendCodeOptions struct {
|
||
|
// AllowFlashCall allows phone verification via phone calls.
|
||
|
AllowFlashCall bool
|
||
|
// Pass true if the phone number is used on the current device.
|
||
|
// Ignored if AllowFlashCall is not set.
|
||
|
CurrentNumber bool
|
||
|
// If a token that will be included in eventually sent SMSs is required:
|
||
|
// required in newer versions of android, to use the android SMS receiver APIs.
|
||
|
AllowAppHash bool
|
||
|
}
|
||
|
|
||
|
// SendCode requests code for provided phone number, returning code hash
|
||
|
// and error if any. Use AuthFlow to reduce boilerplate.
|
||
|
//
|
||
|
// This method should be called first in user authentication flow.
|
||
|
func (c *Client) SendCode(ctx context.Context, phone string, options SendCodeOptions) (*tg.AuthSentCode, error) {
|
||
|
var settings tg.CodeSettings
|
||
|
if options.AllowAppHash {
|
||
|
settings.SetAllowAppHash(true)
|
||
|
}
|
||
|
if options.AllowFlashCall {
|
||
|
settings.SetAllowFlashcall(true)
|
||
|
}
|
||
|
if options.CurrentNumber {
|
||
|
settings.SetCurrentNumber(true)
|
||
|
}
|
||
|
|
||
|
sentCode, err := c.api.AuthSendCode(ctx, &tg.AuthSendCodeRequest{
|
||
|
PhoneNumber: phone,
|
||
|
APIID: c.appID,
|
||
|
APIHash: c.appHash,
|
||
|
Settings: settings,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "send code")
|
||
|
}
|
||
|
return sentCode, nil
|
||
|
}
|
||
|
|
||
|
// ErrPasswordAuthNeeded means that 2FA auth is required.
|
||
|
//
|
||
|
// Call Client.Password to provide 2FA password.
|
||
|
var ErrPasswordAuthNeeded = errors.New("2FA required")
|
||
|
|
||
|
// SignIn performs sign in with provided user phone, code and code hash.
|
||
|
//
|
||
|
// If ErrPasswordAuthNeeded is returned, call Password to provide 2FA
|
||
|
// password.
|
||
|
//
|
||
|
// To obtain codeHash, use SendCode.
|
||
|
func (c *Client) SignIn(ctx context.Context, phone, code, codeHash string) (*tg.AuthAuthorization, error) {
|
||
|
auth, err := c.api.AuthSignIn(ctx, &tg.AuthSignInRequest{
|
||
|
PhoneNumber: phone,
|
||
|
PhoneCodeHash: codeHash,
|
||
|
PhoneCode: code,
|
||
|
})
|
||
|
if tgerr.Is(err, "SESSION_PASSWORD_NEEDED") {
|
||
|
return nil, ErrPasswordAuthNeeded
|
||
|
}
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "sign in")
|
||
|
}
|
||
|
result, err := checkResult(auth)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "check")
|
||
|
}
|
||
|
return result, nil
|
||
|
}
|
||
|
|
||
|
// AcceptTOS accepts version of Terms Of Service.
|
||
|
func (c *Client) AcceptTOS(ctx context.Context, id tg.DataJSON) error {
|
||
|
_, err := c.api.HelpAcceptTermsOfService(ctx, id)
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
// SignUp wraps parameters for SignUp.
|
||
|
type SignUp struct {
|
||
|
PhoneNumber string
|
||
|
PhoneCodeHash string
|
||
|
FirstName string
|
||
|
LastName string
|
||
|
}
|
||
|
|
||
|
// SignUp registers a validated phone number in the system.
|
||
|
//
|
||
|
// To obtain codeHash, use SendCode.
|
||
|
// Use AuthFlow helper to handle authentication flow.
|
||
|
func (c *Client) SignUp(ctx context.Context, s SignUp) (*tg.AuthAuthorization, error) {
|
||
|
auth, err := c.api.AuthSignUp(ctx, &tg.AuthSignUpRequest{
|
||
|
LastName: s.LastName,
|
||
|
PhoneCodeHash: s.PhoneCodeHash,
|
||
|
PhoneNumber: s.PhoneNumber,
|
||
|
FirstName: s.FirstName,
|
||
|
})
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "request")
|
||
|
}
|
||
|
result, err := checkResult(auth)
|
||
|
if err != nil {
|
||
|
return nil, errors.Wrap(err, "check")
|
||
|
}
|
||
|
return result, nil
|
||
|
}
|