package steam import ( "crypto/sha1" . "github.com/Philipp15b/go-steam/protocol" . "github.com/Philipp15b/go-steam/protocol/protobuf" . "github.com/Philipp15b/go-steam/protocol/steamlang" . "github.com/Philipp15b/go-steam/steamid" "github.com/golang/protobuf/proto" "sync/atomic" "time" ) type Auth struct { client *Client details *LogOnDetails } type SentryHash []byte type LogOnDetails struct { Username string Password string AuthCode string TwoFactorCode string SentryFileHash SentryHash } // Log on with the given details. You must always specify username and // password. For the first login, don't set an authcode or a hash and you'll receive an error // and Steam will send you an authcode. Then you have to login again, this time with the authcode. // Shortly after logging in, you'll receive a MachineAuthUpdateEvent with a hash which allows // you to login without using an authcode in the future. // // If you don't use Steam Guard, username and password are enough. func (a *Auth) LogOn(details *LogOnDetails) { if len(details.Username) == 0 || len(details.Password) == 0 { panic("Username and password must be set!") } logon := new(CMsgClientLogon) logon.AccountName = &details.Username logon.Password = &details.Password if details.AuthCode != "" { logon.AuthCode = proto.String(details.AuthCode) } if details.TwoFactorCode != "" { logon.TwoFactorCode = proto.String(details.TwoFactorCode) } logon.ClientLanguage = proto.String("english") logon.ProtocolVersion = proto.Uint32(MsgClientLogon_CurrentProtocol) logon.ShaSentryfile = details.SentryFileHash atomic.StoreUint64(&a.client.steamId, uint64(NewIdAdv(0, 1, int32(EUniverse_Public), int32(EAccountType_Individual)))) a.client.Write(NewClientMsgProtobuf(EMsg_ClientLogon, logon)) } func (a *Auth) HandlePacket(packet *Packet) { switch packet.EMsg { case EMsg_ClientLogOnResponse: a.handleLogOnResponse(packet) case EMsg_ClientNewLoginKey: a.handleLoginKey(packet) case EMsg_ClientSessionToken: case EMsg_ClientLoggedOff: a.handleLoggedOff(packet) case EMsg_ClientUpdateMachineAuth: a.handleUpdateMachineAuth(packet) case EMsg_ClientAccountInfo: a.handleAccountInfo(packet) case EMsg_ClientWalletInfoUpdate: case EMsg_ClientRequestWebAPIAuthenticateUserNonceResponse: case EMsg_ClientMarketingMessageUpdate: } } func (a *Auth) handleLogOnResponse(packet *Packet) { if !packet.IsProto { a.client.Fatalf("Got non-proto logon response!") return } body := new(CMsgClientLogonResponse) msg := packet.ReadProtoMsg(body) result := EResult(body.GetEresult()) if result == EResult_OK { atomic.StoreInt32(&a.client.sessionId, msg.Header.Proto.GetClientSessionid()) atomic.StoreUint64(&a.client.steamId, msg.Header.Proto.GetSteamid()) a.client.Web.webLoginKey = *body.WebapiAuthenticateUserNonce go a.client.heartbeatLoop(time.Duration(body.GetOutOfGameHeartbeatSeconds())) a.client.Emit(&LoggedOnEvent{ Result: EResult(body.GetEresult()), ExtendedResult: EResult(body.GetEresultExtended()), OutOfGameSecsPerHeartbeat: body.GetOutOfGameHeartbeatSeconds(), InGameSecsPerHeartbeat: body.GetInGameHeartbeatSeconds(), PublicIp: body.GetPublicIp(), ServerTime: body.GetRtime32ServerTime(), AccountFlags: EAccountFlags(body.GetAccountFlags()), ClientSteamId: SteamId(body.GetClientSuppliedSteamid()), EmailDomain: body.GetEmailDomain(), CellId: body.GetCellId(), CellIdPingThreshold: body.GetCellIdPingThreshold(), Steam2Ticket: body.GetSteam2Ticket(), UsePics: body.GetUsePics(), WebApiUserNonce: body.GetWebapiAuthenticateUserNonce(), IpCountryCode: body.GetIpCountryCode(), VanityUrl: body.GetVanityUrl(), NumLoginFailuresToMigrate: body.GetCountLoginfailuresToMigrate(), NumDisconnectsToMigrate: body.GetCountDisconnectsToMigrate(), }) } else if result == EResult_Fail || result == EResult_ServiceUnavailable || result == EResult_TryAnotherCM { // some error on Steam's side, we'll get an EOF later } else { a.client.Emit(&LogOnFailedEvent{ Result: EResult(body.GetEresult()), }) a.client.Disconnect() } } func (a *Auth) handleLoginKey(packet *Packet) { body := new(CMsgClientNewLoginKey) packet.ReadProtoMsg(body) a.client.Write(NewClientMsgProtobuf(EMsg_ClientNewLoginKeyAccepted, &CMsgClientNewLoginKeyAccepted{ UniqueId: proto.Uint32(body.GetUniqueId()), })) a.client.Emit(&LoginKeyEvent{ UniqueId: body.GetUniqueId(), LoginKey: body.GetLoginKey(), }) } func (a *Auth) handleLoggedOff(packet *Packet) { result := EResult_Invalid if packet.IsProto { body := new(CMsgClientLoggedOff) packet.ReadProtoMsg(body) result = EResult(body.GetEresult()) } else { body := new(MsgClientLoggedOff) packet.ReadClientMsg(body) result = body.Result } a.client.Emit(&LoggedOffEvent{Result: result}) } func (a *Auth) handleUpdateMachineAuth(packet *Packet) { body := new(CMsgClientUpdateMachineAuth) packet.ReadProtoMsg(body) hash := sha1.New() hash.Write(packet.Data) sha := hash.Sum(nil) msg := NewClientMsgProtobuf(EMsg_ClientUpdateMachineAuthResponse, &CMsgClientUpdateMachineAuthResponse{ ShaFile: sha, }) msg.SetTargetJobId(packet.SourceJobId) a.client.Write(msg) a.client.Emit(&MachineAuthUpdateEvent{sha}) } func (a *Auth) handleAccountInfo(packet *Packet) { body := new(CMsgClientAccountInfo) packet.ReadProtoMsg(body) a.client.Emit(&AccountInfoEvent{ PersonaName: body.GetPersonaName(), Country: body.GetIpCountry(), CountAuthedComputers: body.GetCountAuthedComputers(), AccountFlags: EAccountFlags(body.GetAccountFlags()), FacebookId: body.GetFacebookId(), FacebookName: body.GetFacebookName(), }) }