Skip to content

Commit

Permalink
two-factor authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
alaingilbert committed Jun 11, 2020
1 parent d3d067f commit 5b20404
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 11 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func main() {
```go
IsV7() bool
GetExtractor() Extractor
SetOGameCredentials(username, password string)
SetOGameCredentials(username, password, otpSecret string)
SetProxy(proxyAddress, username, password, proxyType string, loginOnly bool) error
SetLoginWrapper(func(func() error) error)
GetClient() *OGameClient
Expand Down
6 changes: 6 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ var ErrNotLogged = errors.New("not logged")
// ErrBadCredentials returned when the provided credentials are invalid
var ErrBadCredentials = errors.New("bad credentials")

// ErrOTPRequired returned when the otp is required
var ErrOTPRequired = errors.New("otp required")

// ErrOTPInvalid returned when the otp is invalid
var ErrOTPInvalid = errors.New("otp invalid")

// ErrAccountNotFound returned when the account is not found
var ErrAccountNotFound = errors.New("account not found")

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ require (
github.com/olekukonko/tablewriter v0.0.4
github.com/orirawlings/persistent-cookiejar v0.3.0
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.2.0
github.com/stretchr/testify v1.4.0
github.com/valyala/fasttemplate v1.1.0 // indirect
github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb
Expand Down
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4r
github.com/alaingilbert/clockwork v0.1.1-0.20200117075841-891256a24209 h1:X2PLaXYK9kPDScClx4IdBOZvJsRHx6LnA++VCa0lY58=
github.com/alaingilbert/clockwork v0.1.1-0.20200117075841-891256a24209/go.mod h1:YeWgj4cWbHxtx84adUefC05mTiKGZfjKNwGGVnA1W6g=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
Expand Down Expand Up @@ -100,12 +102,15 @@ github.com/orirawlings/persistent-cookiejar v0.3.0 h1:8vNJZlc9EIk5+zZHkB/+EJupd4
github.com/orirawlings/persistent-cookiejar v0.3.0/go.mod h1:qKIjX5I6+Z515AiEoh731TyjytxeMIoWDiwSR1Z1W1A=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.2.0 h1:/A3+Jn+cagqayeR3iHs/L62m5ue7710D35zl1zJ1kok=
github.com/pquerna/otp v1.2.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a h1:3QH7VyOaaiUHNrA9Se4YQIRkDTCw1EJls9xTUCaCeRM=
github.com/rogpeppe/clock v0.0.0-20190514195947-2896927a307a/go.mod h1:4r5QyqhjIWCcK8DO4KMclc5Iknq5qVBAlbYYzAbUScQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
Expand Down
2 changes: 1 addition & 1 deletion interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ type Wrapper interface {
ServerURL() string
ServerVersion() string
SetLoginWrapper(func(func() (bool, error)) error)
SetOGameCredentials(username, password string)
SetOGameCredentials(username, password, otpSecret string)
SetProxy(proxyAddress, username, password, proxyType string, loginOnly bool) error
SetUserAgent(newUserAgent string)
WithPriority(priority int) Prioritizable
Expand Down
44 changes: 36 additions & 8 deletions ogame.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import (
version "github.com/hashicorp/go-version"
cookiejar "github.com/orirawlings/persistent-cookiejar"
"github.com/pkg/errors"
"github.com/pquerna/otp"
"github.com/pquerna/otp/totp"
lua "github.com/yuin/gopher-lua"
"golang.org/x/net/proxy"
"golang.org/x/net/websocket"
Expand Down Expand Up @@ -56,6 +58,7 @@ type OGame struct {
Universe string
Username string
password string
otpSecret string
language string
playerID int64
lobby string
Expand Down Expand Up @@ -162,6 +165,7 @@ type CelestialID int64
type Params struct {
Username string
Password string
OTPSecret string
Universe string
Lang string
PlayerID int64
Expand Down Expand Up @@ -289,7 +293,7 @@ func AddAccount(username, password, universe, lang string, proxyAddr, proxyUsern

// New creates a new instance of OGame wrapper.
func New(universe, username, password, lang string) (*OGame, error) {
b, err := NewNoLogin(username, password, universe, lang, "", 0)
b, err := NewNoLogin(username, password, "", universe, lang, "", 0)
if err != nil {
return nil, err
}
Expand All @@ -301,7 +305,7 @@ func New(universe, username, password, lang string) (*OGame, error) {

// NewWithParams create a new OGame instance with full control over the possible parameters
func NewWithParams(params Params) (*OGame, error) {
b, err := NewNoLogin(params.Username, params.Password, params.Universe, params.Lang, params.CookiesFilename, params.PlayerID)
b, err := NewNoLogin(params.Username, params.Password, params.OTPSecret, params.Universe, params.Lang, params.CookiesFilename, params.PlayerID)
if err != nil {
return nil, err
}
Expand All @@ -321,15 +325,15 @@ func NewWithParams(params Params) (*OGame, error) {
}

// NewNoLogin does not auto login.
func NewNoLogin(username, password, universe, lang, cookiesFilename string, playerID int64) (*OGame, error) {
func NewNoLogin(username, password, otpSecret, universe, lang, cookiesFilename string, playerID int64) (*OGame, error) {
b := new(OGame)
b.loginWrapper = DefaultLoginWrapper
b.Enable()
b.quiet = false
b.logger = log.New(os.Stdout, "", 0)

b.Universe = universe
b.SetOGameCredentials(username, password)
b.SetOGameCredentials(username, password, otpSecret)
b.setOGameLobby("lobby")
b.language = lang
b.playerID = playerID
Expand Down Expand Up @@ -789,7 +793,7 @@ type postSessionsResponse struct {
HasUnmigratedGameAccounts bool `json:"hasUnmigratedGameAccounts"`
}

func postSessions(b *OGame, gameEnvironmentID, platformGameID, username, password string) (postSessionsResponse, error) {
func postSessions(b *OGame, gameEnvironmentID, platformGameID, username, password, otpSecret string) (postSessionsResponse, error) {
var out postSessionsResponse
payload := url.Values{
"autoGameAccountCreation": {"false"},
Expand All @@ -805,6 +809,20 @@ func postSessions(b *OGame, gameEnvironmentID, platformGameID, username, passwor
return out, err
}

if otpSecret != "" {
passcode, err := totp.GenerateCodeCustom(otpSecret, time.Now(), totp.ValidateOpts{
Period: 30,
Skew: 1,
Digits: otp.DigitsSix,
Algorithm: otp.AlgorithmSHA1,
})
if err != nil {
return out, err
}
req.Header.Add("tnt-2fa-code", passcode)
req.Header.Add("tnt-installation-id", "")
}

req.Header.Add("Content-Type", "application/x-www-form-urlencoded")

resp, err := b.doReqWithLoginProxyTransport(req)
Expand All @@ -819,6 +837,12 @@ func postSessions(b *OGame, gameEnvironmentID, platformGameID, username, passwor

by, err := ioutil.ReadAll(resp.Body)
if resp.StatusCode != 201 {
if string(by) == `{"reason":"OTP_REQUIRED"}` {
return out, ErrOTPRequired
}
if string(by) == `{"reason":"OTP_INVALID"}` {
return out, ErrOTPInvalid
}
b.error(resp.StatusCode, string(by), err)
return out, ErrBadCredentials
}
Expand Down Expand Up @@ -890,7 +914,7 @@ func (b *OGame) login() error {
}

b.debug("post sessions")
postSessionsRes, err := postSessions(b, gameEnvironmentID, platformGameID, b.Username, b.password)
postSessionsRes, err := postSessions(b, gameEnvironmentID, platformGameID, b.Username, b.password, b.otpSecret)
if err != nil {
return err
}
Expand Down Expand Up @@ -1071,9 +1095,10 @@ func (b *OGame) GetExtractor() Extractor {
}

// SetOGameCredentials sets ogame credentials for the bot
func (b *OGame) SetOGameCredentials(username, password string) {
func (b *OGame) SetOGameCredentials(username, password, otpSecret string) {
b.Username = username
b.password = password
b.otpSecret = otpSecret
}

func (b *OGame) setOGameLobby(lobby string) {
Expand Down Expand Up @@ -1651,7 +1676,10 @@ func (b *OGame) withRetry(fn func() error) error {
if loginErr := b.wrapLogin(); loginErr != nil {
b.error(loginErr.Error()) // log error
if loginErr == ErrAccountNotFound ||
loginErr == ErrAccountBlocked {
loginErr == ErrAccountBlocked ||
loginErr == ErrBadCredentials ||
loginErr == ErrOTPRequired ||
loginErr == ErrOTPInvalid {
return loginErr
}
}
Expand Down
2 changes: 1 addition & 1 deletion ogame_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func BenchmarkUserInfoGoquery(b *testing.B) {

func TestWrapper(t *testing.T) {
var bot Wrapper
bot, _ = NewNoLogin("", "", "", "", "", 0)
bot, _ = NewNoLogin("", "", "", "", "", "", 0)
assert.NotNil(t, bot)
}

Expand Down

3 comments on commit 5b20404

@0xE232FE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much!!!

Something like this was on my mind when it comes to 2FA. Some people talking on OGame Board about, we can prevent Bots with 2FA. As long as the 2FA Algorithm is Public, nothing is prevented.

Again thanks for the very fast implementation! :-)

@jc01rho
Copy link

@jc01rho jc01rho commented on 5b20404 Jun 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any knowledge about 2FA, is this code supports google OneTimePassword ( 6-digits ) input support with ogamed?

@alaingilbert
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is compatible, it's the same algorithm.

Please sign in to comment.