Skip to content

Steam App Auth

Alex Corn edited this page Feb 27, 2022 · 3 revisions

As of v3.25.0, a SteamUser can retrieve Steam app tickets.
This is currently experimental. The API is not finalized and may change at any time.

Ticket Overview

A typical app ticket consists of a GC token, a session header, and an app ownership ticket. All three parts are sent to game servers or peers (in P2P games) for validation.

GC Token

GC stands for "game connect" here. This is a token assigned by Steam when you connect which is assigned to your login session. These aren't assigned to any app until you "activate" a ticket. They're used to make tickets unique.

Session Header

This part isn't particularly interesting. It contains your external IP and some uninteresting stuff like how many tickets you've retrieved this session.

App Ownership Ticket

This part of the ticket is signed by Steam and is valid for a longer period of time, usually a couple weeks. It proves to your peer that you own the game you're trying to authenticate for. It can be reused many times with different GC tokens.

It contains things like your SteamID, the ID of the app it was assigned for, your external and internal IP addresses, the times when the ticket was generated and when it expires, the licenses you own which grant you this game, any DLC you own, and a signature.

Since this part of the ticket is signed, has an expiration date, and can be reused, there's no need to send it to Steam for validation, so it's validated locally.

WebAPI Validation

In addition to validating session tickets using a Steam client, you can also validate session tickets using the Steam WebAPI. To do so, you should hex-encode the entire session ticket (not just the authTicket portion) and send it to the ISteamUserAuth/AuthenticateUserTicket WebAPI method.

Example:

let {sessionTicket} = await steamUser.createAuthSessionTicket(480);
let apiCheckUrl = 'https://api.steampowered.com/ISteamUserAuth/AuthenticateUserTicket/v0001/?key=STEAM_API_KEY&appid=480&ticket=' + sessionTicket.toString('hex');

Static Methods

parseAppTicket(ticket)

  • ticket - A Buffer containing the app ticket you want to parse
  • allowInvalidSignature - Optional. Pass true to get back data even if the ticket has no valid signature. Defaults to false.

Also available as its own module.

This is a static method, so you should use it like this:

const SteamUser = require('steam-user');
let parsed = SteamUser.parseAppTicket(ticket);

Parses an app ticket. You can either parse a full appticket (GC token + session header + ownership ticket), or just an ownership ticket.

On success, returns an object containing these properties:

  • authTicket - A Buffer containing the part of the ticket that's sent to Steam for validation
  • gcToken - A string containing a 64-bit number which is the ticket's "GC token" (GC stands for "game connect")
  • tokenGenerated - A Date object containing the time when this ticket's GC token was generated
  • sessionExternalIP - A string containing the ticket owner's external IP address (as reported by Steam) at time of connection
  • clientConnectionTime - Time in milliseconds the ticket owner was connected to Steam when they generated this ticket (?)
  • clientConnectionCount - Number of tickets generated by the ticket owner for this Steam connection (?)
  • version - The version of the app ownership ticket
  • steamID - The ticket owner's SteamID, as a SteamID object
  • appID - The ID of the app this ticket authenticates
  • ownershipTicketExternalIP - A string containing the external IP address of the ticket owner as reported by Steam at the time when the ownership ticket was assigned
  • ownershipTicketInternalIP - Same as above but for their internal IP. If the ticket was generated by steam-user then this may be random
  • ownershipFlags - A number containing some (probably uninteresting) flags
  • ownershipTicketGenerated - A Date object containing the time when this ticket's ownership ticket was assigned
  • ownershipTicketExpires - Same as above but for when the ownership ticket expires
  • licenses - An array of integers containing the package IDs for all the licenses the ticket owner owns which grant them this app
  • dlc - An array of objects, each of which contains:
    • appID - The AppID of the piece of DLC
    • licenses - An array of integers containing the package IDs for all the licenses the ticket owner owns which grant them this DLC. Seems to not work right now.
  • signature - A Buffer containing the signature for the app ownership ticket (uninteresting to you)
  • isExpired - A boolean indicating whether the app ownership ticket is expired
  • hasValidSignature - A boolean indicating whether the app ownership ticket signature is valid
  • isValid - A boolean indicating whether the app ownership ticket is valid
    • If allowInvalidSignature is true and the signature is missing, this will be true if the ticket is not expired!

Returns null if unable to decode any part of the ticket.

parseEncryptedAppTicket(ticket, encryptionKey)

  • ticket - A Buffer containing the encrypted app ticket you want to parse
  • encryptionKey - A Buffer or a hex string containing the encryption key for the app to which this ticket belongs

Also available as its own module.

v3.26.0 or later is required to use this function

If you happen to be the developer of an app, you could use this to decrypt one of your encrypted app tickets. You'll need the encrypted app ticket key for the app to which the ticket you supply belongs. This happens 100% locally, so there's no need to be able to reach Steam to do this.

  • version - The version of the app ownership ticket
  • steamID - The ticket owner's SteamID, as a SteamID object
  • appID - The ID of the app this ticket authenticates
  • ownershipTicketExternalIP - A string containing the external IP address of the ticket owner as reported by Steam at the time when the ownership ticket was assigned
  • ownershipTicketInternalIP - Same as above but for their internal IP. If the ticket was generated by steam-user then this may be random
  • ownershipFlags - A number containing some (probably uninteresting) flags
  • ownershipTicketGenerated - A Date object containing the time when this ticket's ownership ticket was assigned
  • licenses - An array of integers containing the package IDs for all the licenses the ticket owner owns which grant them this app
  • dlc - An array of objects, each of which contains:
    • appID - The AppID of the piece of DLC
    • licenses - An array of integers containing the package IDs for all the licenses the ticket owner owns which grant them this DLC. Seems to not work right now.
  • userData - Whatever data was sent by the user to Steam when they requested the ticket

Returns null if the provided ticket could not be parsed or could not be verified for authenticity. If you get data returned, it is guaranteed that it has not been tampered with, provided your encryption key has not been compromised.

To determine if a ticket is valid, you should do the following:

  1. Check that the AppID matches the AppID you expect
  2. If the user has already supplied their SteamID, make sure it matches the one in the ticket
  3. Make sure it hasn't been generated too far in the past for your liking
  4. If you built a nonce into the ticket, make sure the userData matches what you expect

If you want to have a relatively long grace period in which an encrypted app ticket can be used, but you also want to make sure that it wasn't reused, you can send a nonce to the client and have them build that into their encrypted app ticket's userData.

Methods

createAuthSessionTicket(appid, callback)

  • appid - The ID of the app you want a ticket for
  • callback - A function to be called when the ticket is available
    • err - An Error object on failure, or null on success
    • sessionTicket - A Buffer containing the requested session ticket

Creates and activates a new session ticket. The returned ticket contains everything you need and should be sent verbatim to your partner (i.e. a game server or P2P client). Note that the ticket itself may not be usable if you aren't marked as in-game for the specified app on Steam.

Each ticket may only be used once. If you're authenticating in a P2P scheme, you'll need to create a new ticket for every peer.

Once you're done with the ticket, you should cancel it using cancelAuthSessionTickets. If you want to cancel a specific ticket and not all outstanding tickets, you can parse this ticket to get the GC Token out of it.

getAppOwnershipTicket(appid, callback)

  • appid - The ID of the app you want an ownership ticket for
  • callback - A function to be called when the ticket is available
    • err - An Error object on failure, or null on success
    • ticket - A Buffer containing the requested ownership ticket

Gets an app ownership ticket. You can't use this to authenticate with game servers, but you could use this to prove to someone that you own an app. Chances are, you don't want this. This is used internally by createAuthSessionTicket.

If you have local file storage enabled, this will save your ownership tickets to disk (unless you set the saveAppTickets steam-user option to false). If present and not expired, they will be supplied from disk whenever possible.

If you acquire some DLC between when a ticket is saved to disk and when you call this method, the ticket you get back might not contain information about that DLC.

Ownership tickets can be parsed with parseAppTicket but the result will be missing some properties that are contained in other ticket sections, since ownership tickets aren't full app tickets.

activateAuthSessionTickets(appid, tickets[, callback])

  • appid - The ID of the app you want to activate tickets for
  • tickets - An array of Buffers containing tickets as returned by createAuthSessionTicket. All tickets must match the specified app ID.
  • callback - Optional. Called when Steam acknowledges the activation, although at this point the ticket hasn't been validated.
    • err - An Error object on failure, or null on success

Activates one or more session tickets that we received from another user. Prior to sending the ticket to Steam for validation, this first checks the signature and expiration date in the ownership ticket, and validates to make sure the AppID in the ticket matches the appid you provided.

Provided local checks pass, this then sends the ticket to Steam for validation. You'll receive the validation result in the authTicketValidation event.

If you try to activate a ticket that you've already activated and haven't yet canceled, then the ticket is silently ignored. If you try to activate a ticket that matches an already-existing AppID/SteamID auth session, then the previous session is automatically ended.

cancelAuthSessionTickets(appid[, gcTokens][, callback])

  • appid - The ID of the app you want to cancel your ticket(s) for
  • gcTokens - An array of GC Tokens for the tickets you want to cancel. Omit or null to cancel all outstanding tickets.
  • callback - Optional. Called when Steam acknowledges the canceling.
    • err - An Error object on failure, or null on success
    • response - A response object
      • canceledTicketCount - The count of how many session tickets were canceled in this request

Cancels our issued session tickets for the specified app. If you provide an array of gcTokens, then this only cancels the matching tickets and not all tickets. You should call this when you're done with your auth session. When a ticket is canceled, any party that activated and validated your ticket will be notified that it is now canceled.

This doesn't affect the app ownership ticket that was used in this app ticket. Ownership tickets cannot be canceled and only become invalidated when they expire.

endAuthSessions(appid[, steamIDs][, callback])

  • appid - The ID of the app you want to end auth sessions for
  • steamIDs - Optionally pass some SteamID objects (or strings that can parse into them) to restrict which sessions you want to end. Omit or pass null to end all active auth sessions for the app.
  • callback - Optional. Called when Steam acknowledges the request.
    • err - An Error object on failure, or null on success
    • response - A response object
      • canceledTicketCount - The count of how many sessions were ended in this request

Ends our auth sessions with other users. Once ended, we will no longer receive notifications when users' auth session tickets are canceled or otherwise invalidated. If we've already been notified that a ticket was canceled or invalidated, the session was already automatically ended.

This does not cancel your own session tickets. If you're leaving a game, you should call both endAuthSessions and cancelAuthSessionTickets.

getActiveAuthSessionTickets()

Synchronously returns an array of objects which represent active session tickets. Each object in the array has these properties:

  • appID - The ID of the app to which this ticket belongs
  • steamID - A SteamID object representing the user to whom this ticket belongs. Might be your own SteamID if this is your own ticket.
  • ticketCrc - A number containing the CRC of the auth ticket from this session ticket (the auth ticket is the only part that's sent to Steam).
  • gcToken - The GC Token contained in this ticket. Can be used to uniquely identify tickets.
  • validated - A boolean indicating whether this ticket has been validated by Steam yet or not. If false, treat the data contained in this ticket as untrusted and possibly spoofed.

Events

authTicketValidation

  • status - An object containing these properties:
    • steamID - A SteamID object containing the ticket owner's SteamID
    • appOwnerSteamID - A SteamID object containing the app owner's SteamID (usually the ticket owner, but might not be if they're using family sharing)
    • appID - The AppID of the app for which the ticket is being validated
    • ticketCrc - The CRC32 of the ticket in question
    • ticketGcToken - The GC Token contained in the ticket in question
    • state - A value from some enum I'm not sure about right now
    • authSessionResponse - A value from the EAuthSessionResponse enum

Emitted when Steam sends back a validation result for an activated ticket belonging to someone else.

If the authSessionResponse is OK, then the ticket was successfully validated. If it's any other value, then the auth session has been implicitly canceled and there's no need to call endAuthSession for this session.

This event might be emitted multiple times with the same value, so your code should be robust enough to handle such cases. This event could possibly be emitted before the callback fires or promise resolves in activateAuthSessionTickets.

This might be emitted with a different authSessionResponse value even after you get back OK. For example, you might get UserNotConnectedToSteam if the user disconnects from Steam, or AuthTicketCanceled if they cancel their ticket. In such cases, the auth session is implicitly canceled and you should treat the user as though they're now unauthenticated (usually by kicking them from the session).

authTicketStatus

Emitted when a ticket we created is validated by someone else. This event has the same format as authTicketValidation, except the steamID property will be the SteamID of the user who validated our ticket. This might be an individual Steam account, a gameserver account, or an invalid SteamID ({universe: 1, type: 0, instance: 1, accountid: 1}) if the ticket was validated by the WebAPI.