1package steam 2 3import ( 4 "crypto/sha1" 5 "sync/atomic" 6 "time" 7 8 . "github.com/Philipp15b/go-steam/protocol" 9 . "github.com/Philipp15b/go-steam/protocol/protobuf" 10 . "github.com/Philipp15b/go-steam/protocol/steamlang" 11 . "github.com/Philipp15b/go-steam/steamid" 12 "github.com/golang/protobuf/proto" 13) 14 15type Auth struct { 16 client *Client 17 details *LogOnDetails 18} 19 20type SentryHash []byte 21 22type LogOnDetails struct { 23 Username string 24 25 // If logging into an account without a login key, the account's password. 26 Password string 27 28 // If you have a Steam Guard email code, you can provide it here. 29 AuthCode string 30 31 // If you have a Steam Guard mobile two-factor authentication code, you can provide it here. 32 TwoFactorCode string 33 SentryFileHash SentryHash 34 LoginKey string 35 36 // true if you want to get a login key which can be used in lieu of 37 // a password for subsequent logins. false or omitted otherwise. 38 ShouldRememberPassword bool 39} 40 41// Log on with the given details. You must always specify username and 42// password OR username and loginkey. For the first login, don't set an authcode or a hash and you'll 43// receive an error (EResult_AccountLogonDenied) 44// and Steam will send you an authcode. Then you have to login again, this time with the authcode. 45// Shortly after logging in, you'll receive a MachineAuthUpdateEvent with a hash which allows 46// you to login without using an authcode in the future. 47// 48// If you don't use Steam Guard, username and password are enough. 49// 50// After the event EMsg_ClientNewLoginKey is received you can use the LoginKey 51// to login instead of using the password. 52func (a *Auth) LogOn(details *LogOnDetails) { 53 if details.Username == "" { 54 panic("Username must be set!") 55 } 56 if details.Password == "" && details.LoginKey == "" { 57 panic("Password or LoginKey must be set!") 58 } 59 60 logon := new(CMsgClientLogon) 61 logon.AccountName = &details.Username 62 logon.Password = &details.Password 63 if details.AuthCode != "" { 64 logon.AuthCode = proto.String(details.AuthCode) 65 } 66 if details.TwoFactorCode != "" { 67 logon.TwoFactorCode = proto.String(details.TwoFactorCode) 68 } 69 logon.ClientLanguage = proto.String("english") 70 logon.ProtocolVersion = proto.Uint32(MsgClientLogon_CurrentProtocol) 71 logon.ShaSentryfile = details.SentryFileHash 72 if details.LoginKey != "" { 73 logon.LoginKey = proto.String(details.LoginKey) 74 } 75 if details.ShouldRememberPassword { 76 logon.ShouldRememberPassword = proto.Bool(details.ShouldRememberPassword) 77 } 78 79 atomic.StoreUint64(&a.client.steamId, uint64(NewIdAdv(0, 1, int32(EUniverse_Public), int32(EAccountType_Individual)))) 80 81 a.client.Write(NewClientMsgProtobuf(EMsg_ClientLogon, logon)) 82} 83 84func (a *Auth) HandlePacket(packet *Packet) { 85 switch packet.EMsg { 86 case EMsg_ClientLogOnResponse: 87 a.handleLogOnResponse(packet) 88 case EMsg_ClientNewLoginKey: 89 a.handleLoginKey(packet) 90 case EMsg_ClientSessionToken: 91 case EMsg_ClientLoggedOff: 92 a.handleLoggedOff(packet) 93 case EMsg_ClientUpdateMachineAuth: 94 a.handleUpdateMachineAuth(packet) 95 case EMsg_ClientAccountInfo: 96 a.handleAccountInfo(packet) 97 } 98} 99 100func (a *Auth) handleLogOnResponse(packet *Packet) { 101 if !packet.IsProto { 102 a.client.Fatalf("Got non-proto logon response!") 103 return 104 } 105 106 body := new(CMsgClientLogonResponse) 107 msg := packet.ReadProtoMsg(body) 108 109 result := EResult(body.GetEresult()) 110 if result == EResult_OK { 111 atomic.StoreInt32(&a.client.sessionId, msg.Header.Proto.GetClientSessionid()) 112 atomic.StoreUint64(&a.client.steamId, msg.Header.Proto.GetSteamid()) 113 a.client.Web.webLoginKey = *body.WebapiAuthenticateUserNonce 114 115 go a.client.heartbeatLoop(time.Duration(body.GetOutOfGameHeartbeatSeconds())) 116 117 a.client.Emit(&LoggedOnEvent{ 118 Result: EResult(body.GetEresult()), 119 ExtendedResult: EResult(body.GetEresultExtended()), 120 OutOfGameSecsPerHeartbeat: body.GetOutOfGameHeartbeatSeconds(), 121 InGameSecsPerHeartbeat: body.GetInGameHeartbeatSeconds(), 122 PublicIp: body.GetDeprecatedPublicIp(), 123 ServerTime: body.GetRtime32ServerTime(), 124 AccountFlags: EAccountFlags(body.GetAccountFlags()), 125 ClientSteamId: SteamId(body.GetClientSuppliedSteamid()), 126 EmailDomain: body.GetEmailDomain(), 127 CellId: body.GetCellId(), 128 CellIdPingThreshold: body.GetCellIdPingThreshold(), 129 Steam2Ticket: body.GetSteam2Ticket(), 130 UsePics: body.GetDeprecatedUsePics(), 131 WebApiUserNonce: body.GetWebapiAuthenticateUserNonce(), 132 IpCountryCode: body.GetIpCountryCode(), 133 VanityUrl: body.GetVanityUrl(), 134 NumLoginFailuresToMigrate: body.GetCountLoginfailuresToMigrate(), 135 NumDisconnectsToMigrate: body.GetCountDisconnectsToMigrate(), 136 }) 137 } else if result == EResult_Fail || result == EResult_ServiceUnavailable || result == EResult_TryAnotherCM { 138 // some error on Steam's side, we'll get an EOF later 139 } else { 140 a.client.Emit(&LogOnFailedEvent{ 141 Result: EResult(body.GetEresult()), 142 }) 143 a.client.Disconnect() 144 } 145} 146 147func (a *Auth) handleLoginKey(packet *Packet) { 148 body := new(CMsgClientNewLoginKey) 149 packet.ReadProtoMsg(body) 150 a.client.Write(NewClientMsgProtobuf(EMsg_ClientNewLoginKeyAccepted, &CMsgClientNewLoginKeyAccepted{ 151 UniqueId: proto.Uint32(body.GetUniqueId()), 152 })) 153 a.client.Emit(&LoginKeyEvent{ 154 UniqueId: body.GetUniqueId(), 155 LoginKey: body.GetLoginKey(), 156 }) 157} 158 159func (a *Auth) handleLoggedOff(packet *Packet) { 160 result := EResult_Invalid 161 if packet.IsProto { 162 body := new(CMsgClientLoggedOff) 163 packet.ReadProtoMsg(body) 164 result = EResult(body.GetEresult()) 165 } else { 166 body := new(MsgClientLoggedOff) 167 packet.ReadClientMsg(body) 168 result = body.Result 169 } 170 a.client.Emit(&LoggedOffEvent{Result: result}) 171} 172 173func (a *Auth) handleUpdateMachineAuth(packet *Packet) { 174 body := new(CMsgClientUpdateMachineAuth) 175 packet.ReadProtoMsg(body) 176 hash := sha1.New() 177 hash.Write(packet.Data) 178 sha := hash.Sum(nil) 179 180 msg := NewClientMsgProtobuf(EMsg_ClientUpdateMachineAuthResponse, &CMsgClientUpdateMachineAuthResponse{ 181 ShaFile: sha, 182 }) 183 msg.SetTargetJobId(packet.SourceJobId) 184 a.client.Write(msg) 185 186 a.client.Emit(&MachineAuthUpdateEvent{sha}) 187} 188 189func (a *Auth) handleAccountInfo(packet *Packet) { 190 body := new(CMsgClientAccountInfo) 191 packet.ReadProtoMsg(body) 192 a.client.Emit(&AccountInfoEvent{ 193 PersonaName: body.GetPersonaName(), 194 Country: body.GetIpCountry(), 195 CountAuthedComputers: body.GetCountAuthedComputers(), 196 AccountFlags: EAccountFlags(body.GetAccountFlags()), 197 FacebookId: body.GetFacebookId(), 198 FacebookName: body.GetFacebookName(), 199 }) 200} 201