Jump to content
McKay Development

Generating App Ticket


y0landi

Recommended Posts

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 Tickets

User Authentication
The 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 ticket
Call SteamEncryptedAppTicket_BIsTicketForApp() to verify that the ticket is for the expected application
Call SteamEncryptedAppTicket_GetTicketIssueTime() to verify that the ticket has not expired (expiration time defined by the application)
Call SteamEncryptedAppTicket_GetTicketSteamID to retrieve the user's SteamID
An example implementation can be found in the SpaceWarExample project in the SDK. Specifically CSpaceWarClient::RetrieveEncryptedAppTicket and CSpaceWarClient::OnRequestEncryptedAppTicket.

Ownership Verification
Steam 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.

 

Link to comment
Share on other sites

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 Unauthorized
Date: Sat, 17 Dec 2016 00:02:06 GMT
WWW-Authenticate: Bearer realm="realm"
Content-Type: text/plain
Content-Length: 49

Credentials are required to access this resource.POST /auth/refresh/v1 HTTP/1.1
Content-Type: application/json; charset=UTF-8
Client-Version: Bloodgate=92,Gameplay=91
Content-Length: 65
Host: api.battlerite.net

{"refreshToken":"C811BCFF74CC3A207B8B7D8DCD2B5708","userId":352}HTTP/1.1 400 Bad Request
Date: Sat, 17 Dec 2016 00:02:06 GMT
Content-Type: application/json
Content-Length: 82

{"status":400,"code":2,"message":"Invalid refreshToken","property":"refreshToken"}POST /auth/steam-async/v1 HTTP/1.1
Content-Type: application/json; charset=UTF-8
Client-Version: Bloodgate=92,Gameplay=91
Content-Length: 178
Host: api.battlerite.net

{"key":"CAEQv53/jAgYACA6KnDgM25IlI9jnNEmBDlyPkmnT44yEfcYYq0NZj85IpZYi2nZrJBISlXZnOo3J2PqgZe30PMEs5G+0DdBHzOpGfNR2ypy6diAw2iokVCGUgqWDVxscB+gJsQlqMtc0Qx+IvUqMwuXw4DHqogIdrd1Vha7"}HTTP/1.1 200 OK
Date: Sat, 17 Dec 2016 00:02:13 GMT
Content-Type: application/json
Connection: 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 Tickets
Encrypted 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 by y0landi
Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
×
×
  • Create New...