y0landi Posted December 17, 2016 Report Posted December 17, 2016 Hi, I'd like to know if it's possible to retrieve an application ticket for a specific app. I believe the publisher is using the application ticket directly to generate a sessionID for the client. This token is used to authenticate requests to the game's API, which I would like access to for the purpose of developing a community web app. The token expires after 15 minutes and the application ticket after an hour, so it's necessary to be able to generate my own token for things like user registration to pull player data as needed. The only alternative to this is creating a database of all players' data and updating regularly. The application ticket to which I'm referring is referenced in the Steamworks documentation as shown below: Encrypted Application TicketsUser AuthenticationThe following steps detail how to use Encrypted Application Tickets to verify a user's identity between the user's game client and a secure server:The client must call SteamUser()->RequestEncryptedAppTicket() and wait for the EncryptedAppTicketResponse_t call result.The client must then call SteamUser()->GetEncryptedAppTicket() to retrieve the user's encrypted ticket and send that ticket to the secured server.Using the Encrypted Application Ticket library, the secure server must then:Call SteamEncryptedAppTicket_BDecryptTicket() to decrypt the user's ticketCall SteamEncryptedAppTicket_BIsTicketForApp() to verify that the ticket is for the expected applicationCall SteamEncryptedAppTicket_GetTicketIssueTime() to verify that the ticket has not expired (expiration time defined by the application)Call SteamEncryptedAppTicket_GetTicketSteamID to retrieve the user's SteamIDAn example implementation can be found in the SpaceWarExample project in the SDK. Specifically CSpaceWarClient::RetrieveEncryptedAppTicket and CSpaceWarClient::OnRequestEncryptedAppTicket.Ownership VerificationSteam will only create Encrypted Application Tickets for users who own the AppID for which the ticket was created. After decrypting an Encrypted Application Ticket, the secure server can use SteamEncryptedAppTicket_BIsTicketForApp() to verify the AppID of the ticket matches the title's AppID. The server can also use SteamEncryptedAppTicket_BUserOwnsAppInTicket() to determine if the user owns a specific piece of downloadable content. Quote
Dr. McKay Posted December 17, 2016 Report Posted December 17, 2016 That's not currently possible but I had worked on it previously. Do you know if this application is using an encrypted appticket, or just a regular one? Quote
y0landi Posted December 17, 2016 Author Report Posted December 17, 2016 (edited) I'm not entirely certain. Here is an example of the ticket, though:CAEQour61Q0YACA6KnCZisCVMU6wo+cpZm77xisYoMSkgADk8ftfkCvP5Cwvx0kCccFs+ZebjgbSwsX3+XAFTRcsuL7mXMu3suFvwhAFFq/shUiop9wfUVl7TMNXFsimeMqVecgmLtswkdmBUFr9ZQjAqXg8PGKvpiXUWhLc The first 4 characters are always "CAEQ". The rest is random. I'm not 100% sure this is the application ticket itself, but some of the server responses for bad or expired keys seem to indicate it's an application ticket. Here's an example of it in a curl request:curl -XPOST "http://api.battlerite.net/auth/steam-async/v1"-H "Client-Version: Bloodgate=92,Gameplay=91" -H "Content-type: application/json" -d "{\"key\":\"CAEQour61Q0YACA6KnCZisCVMU6wo+cpZm77xisYoMSkgADk8ftfkCvP5Cwvx0kCccFs+ZebjgbSwsX3+XAFTRcsuL7mXMu3suFvwhAFFq/shUiop9wfUVl7TMNXFsimeMqVecgmLtswkdmBUFr9ZQjAqXg8PGKvpiXUWhLc\"}" An expired "key", for example, will get this response:{"status":400,"code":7,"message":"App ticket is too old","property":"key"} If the key is correct and hasn't expired, the server responds with something like:{"sessionID":"BDC0291F338E9FBAD4DBCA13AB603F23","refreshToken":"A8B756CB57FAEAB9A860A9910A45F5A0","timeUntilExpire":900,"userId":352} From there the sessionID is used in the header of any requests to authenticate them:curl -H "authorization: Bearer BDC0291F338E9FBAD4DBCA13AB603F23" -H "Content-type: application/json" "http://api.battlerite.net/account/public/v1" The server would respond to the request above with some data. Not important for this example... Every 15 minutes the refresh token generates a new sessionID with this request:curl -H "Content-type: application/json" -H "Client-Version: Bloodgate=92,Gameplay=91" -d "{\"refreshToken\":\"A8B756CB57FAEAB9A860A9910A45F5A0\",\"userId\":352}" "http://api.battlerite.net/auth/refresh/v1" Response:{"refreshToken":"526E3777D8173139283A691179710759","timeUntilExpire":900,"userId":352} If, for some reason, the refresh token is incorrect then a new application ticket is generated and it generates a new sessionID and refresh token:HTTP/1.1 401 UnauthorizedDate: Sat, 17 Dec 2016 00:02:06 GMTWWW-Authenticate: Bearer realm="realm"Content-Type: text/plainContent-Length: 49Credentials are required to access this resource.POST /auth/refresh/v1 HTTP/1.1Content-Type: application/json; charset=UTF-8Client-Version: Bloodgate=92,Gameplay=91Content-Length: 65Host: api.battlerite.net{"refreshToken":"C811BCFF74CC3A207B8B7D8DCD2B5708","userId":352}HTTP/1.1 400 Bad RequestDate: Sat, 17 Dec 2016 00:02:06 GMTContent-Type: application/jsonContent-Length: 82{"status":400,"code":2,"message":"Invalid refreshToken","property":"refreshToken"}POST /auth/steam-async/v1 HTTP/1.1Content-Type: application/json; charset=UTF-8Client-Version: Bloodgate=92,Gameplay=91Content-Length: 178Host: api.battlerite.net{"key":"CAEQv53/jAgYACA6KnDgM25IlI9jnNEmBDlyPkmnT44yEfcYYq0NZj85IpZYi2nZrJBISlXZnOo3J2PqgZe30PMEs5G+0DdBHzOpGfNR2ypy6diAw2iokVCGUgqWDVxscB+gJsQlqMtc0Qx+IvUqMwuXw4DHqogIdrd1Vha7"}HTTP/1.1 200 OKDate: Sat, 17 Dec 2016 00:02:13 GMTContent-Type: application/jsonConnection: close {"position":1}{"sessionID":"710F7C64FFCE115D15DE08F02A7212F0","refreshToken":"AC5DF2AF04048485B04217809759C990","timeUntilExpire":900,"userId":352} As stated in the op, I'm interested in retrieving some of the data each time a user registers for my web app or chooses to update their data in my database, and I'd like a way to generate my own sessionID's. Given what you see here, is that possible? In addition to the excerpt from the documentation posted in the op, there is this information on application tickets:Encrypted Application TicketsEncrypted Application Tickets can be used to verify a user's identity between a game client and a secure, backend server. Unlike Session Tickets, verifying Encrypted Application Tickets does not require that the secure server can make HTTPS requests. Instead, a C++ library and a private, symmetric key are used by the secure server to verify the ticket. The Steamworks SDK includes 32-bit and 64-bit versions of this library for Windows and Linux under the public/steam/lib directory.Before using Encrypted Application Tickets, you must generate a private key for each title. You can do this by navigating to Edit Steamworks Settings for your application and selecting 'SDK Auth' from the 'Security' drop-down. This key will be associated with your title's AppID and any downloadable content for that title. The keys must be stored securely, and must not be distributed with a game client Am I to understand that this only means that the private key is needed for the server in order to verify the ticket and that the user can request and view the application ticket without the private key? Edited December 17, 2016 by y0landi Quote
Dr. McKay Posted December 17, 2016 Report Posted December 17, 2016 That doesn't look like an unencrypted appticket, so I'd assume that it's an encrypted one. But it doesn't look like that either, which is curious. I think I'd need some more information to know what it is. Have you ever used NetHook? If you were interested in gifting me a copy of Battlerite, I could probably figure out what it is definitively. Quote
y0landi Posted December 17, 2016 Author Report Posted December 17, 2016 (edited) I haven't used NetHook. I'd happily gift you a copy of the game for this. What is your steam info? Edited December 17, 2016 by y0landi Quote
Dr. McKay Posted December 18, 2016 Report Posted December 18, 2016 It would probably be easiest to just email it to me: [email protected] Quote
y0landi Posted December 18, 2016 Author Report Posted December 18, 2016 Sent. Looking forward to the results! Quote
Dr. McKay Posted December 18, 2016 Report Posted December 18, 2016 That is indeed an encrypted appticket. I'll add ticket generation to steam-user shortly, probably tonight. Quote
y0landi Posted December 18, 2016 Author Report Posted December 18, 2016 Excellent! Thank you for your help. Would you be willing to share the process you used to confirm this? Quote
Dr. McKay Posted December 18, 2016 Report Posted December 18, 2016 I used NetHook (a kind of Wireshark-esque application specifically for Steam) to watch the Steam traffic as I launched the game, and saw a RequestEncryptedAppTicket call. I grabbed the data from the ticket and encoded it via protobuf and then via base64 and compared it to the value in the HTTP request (captured via Wireshark) and it matched. If you update to v3.14.0, you can use requestEncryptedAppTicket (see the readme for specific usage) to get an encrypted appticket. You don't need to be "running" the game to get a ticket. Then you can call ticket.toString('base64') to get the base64 representation expected by the HTTP API. I confirmed on my end that the API will accept the result of that process. Quote
y0landi Posted December 18, 2016 Author Report Posted December 18, 2016 Thanks for your work. And for your instructive response. Quote
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.