Skip to content

Commit

Permalink
Merge pull request #248 from LossyDragon/webcookiesample
Browse files Browse the repository at this point in the history
Add web cookie samples. Add allowRenewal parameter for generateAccessTokenForApp.
  • Loading branch information
LossyDragon authored Nov 13, 2023
2 parents ad735d5 + 6e5bf4f commit bade894
Show file tree
Hide file tree
Showing 3 changed files with 383 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package in.dragonbra.javasteamsamples._1logon;

import in.dragonbra.javasteam.enums.EResult;
import in.dragonbra.javasteam.steam.authentication.*;
import in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.SteamUnifiedMessages;
import in.dragonbra.javasteam.steam.handlers.steamuser.LogOnDetails;
import in.dragonbra.javasteam.steam.handlers.steamuser.SteamUser;
import in.dragonbra.javasteam.steam.handlers.steamuser.callback.LoggedOffCallback;
import in.dragonbra.javasteam.steam.handlers.steamuser.callback.LoggedOnCallback;
import in.dragonbra.javasteam.steam.steamclient.SteamClient;
import in.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackManager;
import in.dragonbra.javasteam.steam.steamclient.callbacks.ConnectedCallback;
import in.dragonbra.javasteam.steam.steamclient.callbacks.DisconnectedCallback;
import in.dragonbra.javasteam.util.Strings;
import in.dragonbra.javasteam.util.log.DefaultLogListener;
import in.dragonbra.javasteam.util.log.LogManager;

import java.util.concurrent.CancellationException;

/**
* @author lossy
* @since 2023-11-06
*/
@SuppressWarnings("FieldCanBeLocal")
public class SampleWebCookie implements Runnable {

private SteamClient steamClient;

private SteamUnifiedMessages unifiedMessages;

private SteamAuthentication auth;

private CallbackManager manager;

private SteamUser steamUser;

private boolean isRunning;

private final String user;

private final String pass;

private String accessToken;

private String refreshToken;

public SampleWebCookie(String user, String pass) {
this.user = user;
this.pass = pass;
}

public static void main(String[] args) {
if (args.length < 2) {
System.out.println("SampleWebCookie: No username and password specified!");
return;
}

LogManager.addListener(new DefaultLogListener());

new SampleWebCookie(args[0], args[1]).run();
}

@Override
public void run() {

// create our steamclient instance
steamClient = new SteamClient();

// create the callback manager which will route callbacks to function calls
manager = new CallbackManager(steamClient);

// get the steam unified messages handler, which is used for sending and receiving responses from the unified service api
unifiedMessages = steamClient.getHandler(SteamUnifiedMessages.class);

// get the steamuser handler, which is used for logging on after successfully connecting
steamUser = steamClient.getHandler(SteamUser.class);

// register a few callbacks we're interested in
// these are registered upon creation to a callback manager, which will then route the callbacks
// to the functions specified
manager.subscribe(ConnectedCallback.class, this::onConnected);
manager.subscribe(DisconnectedCallback.class, this::onDisconnected);

manager.subscribe(LoggedOnCallback.class, this::onLoggedOn);
manager.subscribe(LoggedOffCallback.class, this::onLoggedOff);

isRunning = true;

System.out.println("Connecting to steam...");

// initiate the connection
steamClient.connect();

// create our callback handling loop
while (isRunning) {
// in order for the callbacks to get routed, they need to be handled by the manager
manager.runWaitCallbacks(1000L);
}
}

private void onConnected(ConnectedCallback callback) {
System.out.println("Connected to Steam! Logging in " + user + "...");

AuthSessionDetails authSessionDetails = new AuthSessionDetails();
authSessionDetails.username = user;
authSessionDetails.password = pass;
authSessionDetails.persistentSession = false;
authSessionDetails.authenticator = new UserConsoleAuthenticator();

// get the authentication handler, which used for authenticating with Steam
auth = new SteamAuthentication(steamClient, unifiedMessages);

try {
CredentialsAuthSession authSession = auth.beginAuthSessionViaCredentials(authSessionDetails);

// AuthPollResult pollResponse = authSession.pollingWaitForResult(); // This method is for Kotlin (coroutines)
AuthPollResult pollResponse = authSession.pollingWaitForResultCompat();

LogOnDetails logOnDetails = new LogOnDetails();
logOnDetails.setUsername(pollResponse.getAccountName());
logOnDetails.setAccessToken(pollResponse.getRefreshToken());

// Set LoginID to a non-zero value if you have another client connected using the same account,
// the same private ip, and same public ip.
logOnDetails.setLoginID(149);

steamUser.logOn(logOnDetails);

// AccessToken can be used as the steamLoginSecure cookie
// RefreshToken is required to generate new access tokens
accessToken = pollResponse.getAccessToken();
refreshToken = pollResponse.getRefreshToken();
} catch (Exception e) {
e.printStackTrace();

// List a couple of exceptions that could be important to handle.
if (e instanceof AuthenticationException) {
System.out.println("An Authentication error has occurred.");
}

if (e instanceof CancellationException) {
System.out.println("An Cancellation exception was raised. Usually means a timeout occurred");
}
}
}

private void onDisconnected(DisconnectedCallback callback) {
System.out.println("Disconnected from Steam");

isRunning = false;
}

private void onLoggedOn(LoggedOnCallback callback) {
if (callback.getResult() != EResult.OK) {
System.out.println("Unable to login to steam: " + callback.getResult() + " / " + callback.getExtendedResult());

isRunning = false;

return;
}

System.out.println("Successfully logged on!");

// This is how you concatenate the cookie, you can set it on the Steam domains, and it should work
// but actual usage of this will be left as an exercise for the reader
@SuppressWarnings("unused")
String steamLoginSecure = callback.getClientSteamID().convertToUInt64() + "||" + accessToken;

// The access token expires in 24 hours (at the time of writing) so you will have to renew it.
// Parse this token with a JWT library to get the expiration date and set up a timer to renew it.
// To renew you will have to call this:
// When allowRenewal is set to true, Steam may return new RefreshToken
AccessTokenGenerateResult newTokens = auth.generateAccessTokenForApp(callback.getClientSteamID(), refreshToken, false);

accessToken = newTokens.getAccessToken();

if (!Strings.isNullOrEmpty(newTokens.getRefreshToken())) {
refreshToken = newTokens.getRefreshToken();
}

// Do not forget to update steamLoginSecure with the new accessToken!

// for this sample we'll just log off
steamUser.logOff();
}

private void onLoggedOff(LoggedOffCallback callback) {
System.out.println("Logged off of Steam: " + callback.getResult());

isRunning = false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
package `in`.dragonbra.javasteamsamples._1logon

import `in`.dragonbra.javasteam.enums.EResult
import `in`.dragonbra.javasteam.steam.authentication.*
import `in`.dragonbra.javasteam.steam.handlers.steamunifiedmessages.SteamUnifiedMessages
import `in`.dragonbra.javasteam.steam.handlers.steamuser.LogOnDetails
import `in`.dragonbra.javasteam.steam.handlers.steamuser.SteamUser
import `in`.dragonbra.javasteam.steam.handlers.steamuser.callback.LoggedOffCallback
import `in`.dragonbra.javasteam.steam.handlers.steamuser.callback.LoggedOnCallback
import `in`.dragonbra.javasteam.steam.steamclient.SteamClient
import `in`.dragonbra.javasteam.steam.steamclient.callbackmgr.CallbackManager
import `in`.dragonbra.javasteam.steam.steamclient.callbacks.ConnectedCallback
import `in`.dragonbra.javasteam.steam.steamclient.callbacks.DisconnectedCallback
import `in`.dragonbra.javasteam.util.Strings
import `in`.dragonbra.javasteam.util.log.DefaultLogListener
import `in`.dragonbra.javasteam.util.log.LogManager
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.supervisorScope

/**
* @author lossy
* @since 2023-11-06
*/
class SampleWebCookieKt(private val user: String, private val pass: String) : Runnable {

private lateinit var steamClient: SteamClient

private lateinit var unifiedMessages: SteamUnifiedMessages

private lateinit var auth: SteamAuthentication

private lateinit var manager: CallbackManager

private lateinit var steamUser: SteamUser

private var isRunning = false

private var accessToken: String = ""

private var refreshToken: String = ""

override fun run() {

// create our steamclient instance
steamClient = SteamClient()

// create the callback manager which will route callbacks to function calls
manager = CallbackManager(steamClient)

// get the steam unified messages handler, which is used for sending and receiving responses from the unified service api
unifiedMessages = steamClient.getHandler(SteamUnifiedMessages::class.java)

// get the steamuser handler, which is used for logging on after successfully connecting
steamUser = steamClient.getHandler(SteamUser::class.java)

// register a few callbacks we're interested in
// these are registered upon creation to a callback manager, which will then route the callbacks
// to the functions specified
manager.subscribe(ConnectedCallback::class.java, ::onConnected)
manager.subscribe(DisconnectedCallback::class.java, ::onDisconnected)
manager.subscribe(LoggedOnCallback::class.java, ::onLoggedOn)
manager.subscribe(LoggedOffCallback::class.java, ::onLoggedOff)

isRunning = true

println("Connecting to steam...")

// initiate the connection
steamClient.connect()

// create our callback handling loop
while (isRunning) {
// in order for the callbacks to get routed, they need to be handled by the manager
manager.runWaitCallbacks(1000L)
}
}

@Suppress("UNUSED_PARAMETER")
private fun onConnected(callback: ConnectedCallback) {
println("Connected to Steam! Logging in $user...")

val authSessionDetails = AuthSessionDetails().apply {
username = user
password = pass
persistentSession = false
authenticator = UserConsoleAuthenticator()
}

// get the authentication handler, which used for authenticating with Steam
auth = SteamAuthentication(steamClient, unifiedMessages)

runBlocking {
supervisorScope {
val authSession: CredentialsAuthSession = auth.beginAuthSessionViaCredentials(authSessionDetails)

val deferredPolling = async {
authSession.pollingWaitForResult(this)
}

val pollResponse: AuthPollResult = deferredPolling.await()

LogOnDetails().apply {
username = pollResponse.accountName
accessToken = pollResponse.refreshToken

// Set LoginID to a non-zero value if you have another client connected using the same account,
// the same private ip, and same public ip.
loginID = 149
}.also(steamUser::logOn)

// AccessToken can be used as the steamLoginSecure cookie
// RefreshToken is required to generate new access tokens
accessToken = pollResponse.accessToken
refreshToken = pollResponse.refreshToken
}
}
}

@Suppress("UNUSED_PARAMETER")
private fun onDisconnected(callback: DisconnectedCallback) {
println("Disconnected from Steam")

isRunning = false
}

private fun onLoggedOn(callback: LoggedOnCallback) {
if (callback.result != EResult.OK) {
println("Unable to login to steam: ${callback.result} / ${callback.extendedResult}")

isRunning = false

return
}

println("Successfully logged on!")

// This is how you concatenate the cookie, you can set it on the Steam domains, and it should work
// but actual usage of this will be left as an exercise for the reader
@Suppress("UNUSED_VARIABLE")
val steamLoginSecure = "${callback.clientSteamID.convertToUInt64()}||$accessToken"

// The access token expires in 24 hours (at the time of writing) so you will have to renew it.
// Parse this token with a JWT library to get the expiration date and set up a timer to renew it.
// To renew you will have to call this:
// When allowRenewal is set to true, Steam may return new RefreshToken
val newTokens = auth.generateAccessTokenForApp(callback.clientSteamID, refreshToken, false)

accessToken = newTokens.accessToken

if (!Strings.isNullOrEmpty(newTokens.refreshToken)) {
refreshToken = newTokens.refreshToken
}

// Do not forget to update steamLoginSecure with the new accessToken!

// for this sample we'll just log off
steamUser.logOff()
}

private fun onLoggedOff(callback: LoggedOffCallback) {
println("Logged off of Steam: " + callback.result)

isRunning = false
}

companion object {
@JvmStatic
fun main(args: Array<String>) {
if (args.size < 2) {
println("SampleWebCookie: No username and password specified!")
return
}

LogManager.addListener(DefaultLogListener())

SampleWebCookieKt(args[0], args[1]).run()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,22 @@ class SteamAuthentication(private val steamClient: SteamClient, unifiedMessages:
*
* @param steamID the SteamID this token belongs to.
* @param refreshToken the refresh token.
* @param allowRenewal If true, allow renewing the token.
* @return A [AccessTokenGenerateResult] containing the new token
*/
fun generateAccessTokenForApp(steamID: SteamID, refreshToken: String): AccessTokenGenerateResult {
@JvmOverloads
fun generateAccessTokenForApp(
steamID: SteamID,
refreshToken: String,
allowRenewal: Boolean = false
): AccessTokenGenerateResult {
val request = CAuthentication_AccessToken_GenerateForApp_Request.newBuilder().apply {
this.refreshToken = refreshToken
this.steamid = steamID.convertToUInt64()
this.renewalType = ETokenRenewalType.k_ETokenRenewalType_Allow

if (allowRenewal) {
this.renewalType = ETokenRenewalType.k_ETokenRenewalType_Allow
}
}

val message = authenticationService.GenerateAccessTokenForApp(request.build()).runBlock()
Expand Down

0 comments on commit bade894

Please sign in to comment.