|
| 1 | +--- |
| 2 | +title: Steam Session Ticket Authentication |
| 3 | +--- |
| 4 | + |
| 5 | +SpacetimeAuth supports authentication using Steam's Session Ticket system. This allows |
| 6 | +users to authenticate with your application using their Steam account. |
| 7 | +This method is particularly useful for gaming applications that want to distribute |
| 8 | +their applications on Steam. |
| 9 | + |
| 10 | +Once a user authenticates with Steam, SpacetimeAuth will create an account for them |
| 11 | +in your SpacetimeAuth project and issue an ID token that your application can use |
| 12 | +to authenticate with SpacetimeDB. The ID token will contain claims with information |
| 13 | +about the user and game ownership, which you can use for authorization in your application. |
| 14 | + |
| 15 | +The ID token will notably contain the following claims: |
| 16 | + |
| 17 | +- `sub`: The unique identifier for the user in SpacetimeAuth |
| 18 | +- `provider_id`: The unique identifier for the user in Steam |
| 19 | +- `login_method`: The authentication provider (in this case, "steam") |
| 20 | +- `preferred_username`: The user's Steam display name |
| 21 | +- `picture`: The URL to the user's Steam avatar (full size) |
| 22 | +- `steam_owned_games`: An array of all the applications you published owned by the |
| 23 | + user on Steam |
| 24 | + |
| 25 | +## Creating a Steam Publisher Key |
| 26 | + |
| 27 | +To use Steam Session Ticket authentication, you need to create a |
| 28 | +[Steam Publisher Key](https://partner.steamgames.com/doc/webapi_overview/auth) |
| 29 | +in the Steamworks dashboard. This key will be used to verify the authenticity of |
| 30 | +the session tickets provided by Steam during the authentication process and to |
| 31 | +retrieve information about the user's owned games. |
| 32 | +To create a Steam Publisher Key, follow [Valve's official documentation](https://partner.steamgames.com/doc/webapi_overview/auth#create_publisher_key) |
| 33 | + |
| 34 | +## Adding your Steam Publisher Key and allowed app IDs to SpacetimeAuth |
| 35 | + |
| 36 | +Once you have your Steam Publisher Key, you need to add it to your SpacetimeAuth |
| 37 | +project along with the list of allowed app IDs that you want to check for ownership. |
| 38 | +You can do this in the SpacetimeAuth dashboard under the "Settings" section. |
| 39 | + |
| 40 | + |
| 41 | + |
| 42 | +Any app IDs that will authenticate users through Steam Session Tickets must be added |
| 43 | +to the allowed app IDs list in SpacetimeAuth. This ensures that only session tickets |
| 44 | +issued for those specific app IDs will be accepted during authentication. This is |
| 45 | +an important security measure to prevent unauthorized access using session tickets |
| 46 | +from other applications. Make sure to add all the app IDs for the games or DLCs you |
| 47 | +want to check ownership for in your application. |
| 48 | + |
| 49 | +## Getting a Steam Session Ticket |
| 50 | + |
| 51 | +Now that you've set up your Steam Publisher Key and allowed app IDs, you can implement |
| 52 | +the client-side logic to get a Steam Session Ticket and authenticate with SpacetimeAuth. |
| 53 | +You can use the Steamworks SDK to retrieve a session ticket for the user and then |
| 54 | +send it to your backend server for authentication. |
| 55 | + |
| 56 | +Depending on the platform you're developing for, the process of obtaining a Steam |
| 57 | +Session Ticket may vary. |
| 58 | + |
| 59 | +Here are some resources to help you get started with retrieving a Steam Session Ticket: |
| 60 | + |
| 61 | +- [Steamworks SDK documentation (C++/Unreal Engine)](https://partner.steamgames.com/doc/api/ISteamUser#GetAuthTicketForWebApi) |
| 62 | +- [Steamworks .Net (C#/Unity)](https://steamworks.github.io/gettingstarted/) |
| 63 | +- [GodotSteam (Godot Engine)](https://godotsteam.com/classes/user/#getauthticketforwebapi) |
| 64 | +- [Steamworks.js (JavaScript/Node.js)](https://github.com/ceifa/steamworks.js) |
| 65 | + |
| 66 | +:::warning |
| 67 | +Make sure to request a session ticket with `spacetimeauth` as the identity parameter. |
| 68 | +::: |
| 69 | + |
| 70 | +## Exchanging the Steam Session Ticket for a SpacetimeAuth ID Token |
| 71 | + |
| 72 | +Once you have the Steam Session Ticket, you can send it to the token endpoint of |
| 73 | +SpacetimeAuth to exchange it for an ID token that you can use to authenticate with |
| 74 | +SpacetimeDB. |
| 75 | + |
| 76 | +Here is an example of how to exchange the Steam Session Ticket using `curl`: |
| 77 | + |
| 78 | +```bash |
| 79 | +curl -X POST https://auth.spacetimedb.com/oidc/token \ |
| 80 | + -H "content-type: application/x-www-form-urlencoded" \ |
| 81 | + -d "client_id=client_032xAh3p8o3zGzDghXsO5x" \ |
| 82 | + -d "grant_type=urn:spacetimeauth:steam-ticket" \ |
| 83 | + -d "steam_ticket=<YOUR STEAM TICKET>" \ |
| 84 | + -d "steam_app_id=<APP ID FROM WHICH YOU REQUESTED THE TICKET>" |
| 85 | +``` |
| 86 | + |
| 87 | +## Checking App or DLC Ownership |
| 88 | + |
| 89 | +You can check if a user owns a specific app or DLC by inspecting the `steam_owned_games` |
| 90 | +claim in the ID token. This claim contains an array of all the applications you published |
| 91 | +that the user owns on Steam. Each entry in the array includes the `appid` and a boolean |
| 92 | +`ownsapp` indicating whether the user owns the app or not. You can use this information |
| 93 | +to implement authorization logic in your application, such as granting access to |
| 94 | +certain features or content based on game ownership. |
| 95 | + |
| 96 | +```rust |
| 97 | +#[derive(serde::Deserialize)] |
| 98 | +struct SteamGame { |
| 99 | + appid: u64, |
| 100 | + ownsapp: bool, |
| 101 | +} |
| 102 | + |
| 103 | +#[derive(serde::Deserialize)] |
| 104 | +struct CustomClaims { |
| 105 | + steam_owned_games: Vec<SteamGame>, |
| 106 | +} |
| 107 | + |
| 108 | +#[reducer(client_connected)] |
| 109 | +pub fn connect(ctx: &ReducerContext) -> Result<(), String> { |
| 110 | + let jwt = ctx.sender_auth().jwt().ok_or("JWT required")?; |
| 111 | + let claims: CustomClaims = serde_json::from_slice(jwt.raw_payload().as_bytes()) |
| 112 | + .map_err(|e| format!("Invalid JWT: {}", e))?; |
| 113 | + |
| 114 | + const APP_ID: u64 = 2717550; |
| 115 | + let owns = claims.steam_owned_games.iter().any(|g| g.appid == APP_ID && g.ownsapp); |
| 116 | + |
| 117 | + if !owns { |
| 118 | + return Err(format!("Unauthorized: does not own app {}", APP_ID)); |
| 119 | + } |
| 120 | + Ok(()) |
| 121 | +} |
| 122 | +``` |
0 commit comments