Skip to content

Commit

Permalink
Merge pull request #1129 from xPaw/authentication
Browse files Browse the repository at this point in the history
Add `SteamAuthentication` to handle new Steam login flow sessions
  • Loading branch information
xPaw authored Mar 23, 2023
2 parents 0d321f2 + 1d41f5f commit 1d50962
Show file tree
Hide file tree
Showing 17 changed files with 1,146 additions and 16 deletions.
134 changes: 134 additions & 0 deletions Samples/1a.Authentication/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using System;
using System.Text.Json;
using SteamKit2;
using SteamKit2.Authentication;

if ( args.Length < 2 )
{
Console.Error.WriteLine( "Sample1a: No username and password specified!" );
return;
}

// save our logon details
var user = args[ 0 ];
var pass = args[ 1 ];

// create our steamclient instance
var steamClient = new SteamClient();
// create the callback manager which will route callbacks to function calls
var manager = new CallbackManager( steamClient );

// get the steamuser handler, which is used for logging on after successfully connecting
var steamUser = steamClient.GetHandler<SteamUser>();

// 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<SteamClient.ConnectedCallback>( OnConnected );
manager.Subscribe<SteamClient.DisconnectedCallback>( OnDisconnected );

manager.Subscribe<SteamUser.LoggedOnCallback>( OnLoggedOn );
manager.Subscribe<SteamUser.LoggedOffCallback>( OnLoggedOff );

var isRunning = true;

Console.WriteLine( "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( TimeSpan.FromSeconds( 1 ) );
}

async void OnConnected( SteamClient.ConnectedCallback callback )
{
Console.WriteLine( "Connected to Steam! Logging in '{0}'...", user );

// Begin authenticating via credentials
var authSession = await steamClient.Authentication.BeginAuthSessionViaCredentialsAsync( new AuthSessionDetails
{
Username = user,
Password = pass,
IsPersistentSession = false,
Authenticator = new UserConsoleAuthenticator(),
} );

// Starting polling Steam for authentication response
var pollResponse = await authSession.PollingWaitForResultAsync();

// Logon to Steam with the access token we have received
// Note that we are using RefreshToken for logging on here
steamUser.LogOn( new SteamUser.LogOnDetails
{
Username = pollResponse.AccountName,
AccessToken = pollResponse.RefreshToken,
} );

// This is not required, but it is possible to parse the JWT access token to see the scope and expiration date.
ParseJsonWebToken( pollResponse.AccessToken, nameof( pollResponse.AccessToken ) );
ParseJsonWebToken( pollResponse.RefreshToken, nameof( pollResponse.RefreshToken ) );
}

void OnDisconnected( SteamClient.DisconnectedCallback callback )
{
Console.WriteLine( "Disconnected from Steam" );

isRunning = false;
}

void OnLoggedOn( SteamUser.LoggedOnCallback callback )
{
if ( callback.Result != EResult.OK )
{
Console.WriteLine( "Unable to logon to Steam: {0} / {1}", callback.Result, callback.ExtendedResult );

isRunning = false;
return;
}

Console.WriteLine( "Successfully logged on!" );

// at this point, we'd be able to perform actions on Steam

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

void OnLoggedOff( SteamUser.LoggedOffCallback callback )
{
Console.WriteLine( "Logged off of Steam: {0}", callback.Result );
}



// This is simply showing how to parse JWT, this is not required to login to Steam
void ParseJsonWebToken( string token, string name )
{
// You can use a JWT library to do the parsing for you
var tokenComponents = token.Split( '.' );

// Fix up base64url to normal base64
var base64 = tokenComponents[ 1 ].Replace( '-', '+' ).Replace( '_', '/' );

if ( base64.Length % 4 != 0 )
{
base64 += new string( '=', 4 - base64.Length % 4 );
}

var payloadBytes = Convert.FromBase64String( base64 );

// Payload can be parsed as JSON, and then fields such expiration date, scope, etc can be accessed
var payload = JsonDocument.Parse( payloadBytes );

// For brevity we will simply output formatted json to console
var formatted = JsonSerializer.Serialize( payload, new JsonSerializerOptions
{
WriteIndented = true,
} );
Console.WriteLine( $"{name}: {formatted}" );
Console.WriteLine();
}
18 changes: 18 additions & 0 deletions Samples/1a.Authentication/Sample1a_Authentication.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Company>SteamRE</Company>
<Product>Sample1a_Authentication</Product>
<Authors />
<Description>SteamKit Sample 1a: Authentication</Description>
<Copyright>Copyright © Pavel Djundik 2023</Copyright>
<PackageId>Sample1a_Authentication</PackageId>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\SteamKit2\SteamKit2\SteamKit2.csproj" />
</ItemGroup>

</Project>
111 changes: 111 additions & 0 deletions Samples/1b.QrCodeAuthentication/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using System;
using QRCoder;
using SteamKit2;
using SteamKit2.Authentication;

// create our steamclient instance
var steamClient = new SteamClient();
// create the callback manager which will route callbacks to function calls
var manager = new CallbackManager( steamClient );

// get the steamuser handler, which is used for logging on after successfully connecting
var steamUser = steamClient.GetHandler<SteamUser>();

// 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<SteamClient.ConnectedCallback>( OnConnected );
manager.Subscribe<SteamClient.DisconnectedCallback>( OnDisconnected );

manager.Subscribe<SteamUser.LoggedOnCallback>( OnLoggedOn );
manager.Subscribe<SteamUser.LoggedOffCallback>( OnLoggedOff );

var isRunning = true;

Console.WriteLine( "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( TimeSpan.FromSeconds( 1 ) );
}

async void OnConnected( SteamClient.ConnectedCallback callback )
{
// Start an authentication session by requesting a link
var authSession = await steamClient.Authentication.BeginAuthSessionViaQRAsync( new AuthSessionDetails() );

// Steam will periodically refresh the challenge url, this callback allows you to draw a new qr code
authSession.ChallengeURLChanged = () =>
{
Console.WriteLine();
Console.WriteLine( "Steam has refreshed the challenge url" );
DrawQRCode( authSession );
};

// Draw current qr right away
DrawQRCode( authSession );

// Starting polling Steam for authentication response
// This response is later used to logon to Steam after connecting
var pollResponse = await authSession.PollingWaitForResultAsync();

Console.WriteLine( $"Logging in as '{pollResponse.AccountName}'..." );

// Logon to Steam with the access token we have received
steamUser.LogOn( new SteamUser.LogOnDetails
{
Username = pollResponse.AccountName,
AccessToken = pollResponse.RefreshToken,
} );
}

void OnDisconnected( SteamClient.DisconnectedCallback callback )
{
Console.WriteLine( "Disconnected from Steam" );

isRunning = false;
}

void OnLoggedOn( SteamUser.LoggedOnCallback callback )
{
if ( callback.Result != EResult.OK )
{
Console.WriteLine( "Unable to logon to Steam: {0} / {1}", callback.Result, callback.ExtendedResult );

isRunning = false;
return;
}

Console.WriteLine( "Successfully logged on!" );

// at this point, we'd be able to perform actions on Steam

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

void OnLoggedOff( SteamUser.LoggedOffCallback callback )
{
Console.WriteLine( "Logged off of Steam: {0}", callback.Result );
}

void DrawQRCode( QrAuthSession authSession )
{
Console.WriteLine( $"Challenge URL: {authSession.ChallengeURL}" );
Console.WriteLine();

// Encode the link as a QR code
var qrGenerator = new QRCodeGenerator();
var qrCodeData = qrGenerator.CreateQrCode( authSession.ChallengeURL, QRCodeGenerator.ECCLevel.L );
var qrCode = new AsciiQRCode( qrCodeData );
var qrCodeAsAsciiArt = qrCode.GetGraphic( 1, drawQuietZones: false );

Console.WriteLine( "Use the Steam Mobile App to sign in via QR code:" );
Console.WriteLine( qrCodeAsAsciiArt );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<Company>SteamRE</Company>
<Product>Sample1b_QrCodeAuthentication</Product>
<Authors />
<Description>SteamKit Sample 1b: Authentication using QR codes</Description>
<Copyright>Copyright © Pavel Djundik 2023</Copyright>
<PackageId>Sample1b_QrCodeAuthentication</PackageId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="QRCoder" Version="1.4.3" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\SteamKit2\SteamKit2\SteamKit2.csproj" />
</ItemGroup>

</Project>
58 changes: 43 additions & 15 deletions Samples/Samples.sln
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27130.2003
# Visual Studio Version 17
VisualStudioVersion = 17.5.33424.131
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample1_Logon", "1.Logon\Sample1_Logon.csproj", "{CEF39496-576D-4A70-9A06-16112B84B79F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample1a_Authentication", "1a.Authentication\Sample1a_Authentication.csproj", "{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample1b_QrCodeAuthentication", "1b.QrCodeAuthentication\Sample1b_QrCodeAuthentication.csproj", "{EFC8F224-9441-48D0-8FEE-2FC9F948837C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample2_Extending", "2.Extending\Sample2_Extending.csproj", "{B8D7F87B-DBAA-4FBE-8254-E1FE07D6C7DC}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample3_DebugLog", "3.DebugLog\Sample3_DebugLog.csproj", "{808EAE9B-B9F6-4692-8F5A-9E2A703BF8CE}"
Expand All @@ -23,7 +27,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample9_AsyncJobs", "9.Asyn
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample10_DotaMatchRequest", "10.DotaMatchRequest\Sample10_DotaMatchRequest.csproj", "{734863D3-4EED-4758-B1E9-7B324C2D8D72}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamKit2", "..\SteamKit2\SteamKit2\SteamKit2.csproj", "{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamKit2", "..\SteamKit2\SteamKit2\SteamKit2.csproj", "{4B2B0365-DE37-4B65-B614-3E4E7C05147D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -155,18 +159,42 @@ Global
{734863D3-4EED-4758-B1E9-7B324C2D8D72}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{734863D3-4EED-4758-B1E9-7B324C2D8D72}.Release|x86.ActiveCfg = Release|Any CPU
{734863D3-4EED-4758-B1E9-7B324C2D8D72}.Release|x86.Build.0 = Release|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Debug|x86.ActiveCfg = Debug|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Debug|x86.Build.0 = Debug|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Release|Any CPU.Build.0 = Release|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Release|x86.ActiveCfg = Release|Any CPU
{D3E2F2D3-A663-4232-B9C5-A4F81DB665A2}.Release|x86.Build.0 = Release|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Debug|x86.ActiveCfg = Debug|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Debug|x86.Build.0 = Debug|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Release|Any CPU.Build.0 = Release|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Release|x86.ActiveCfg = Release|Any CPU
{4B2B0365-DE37-4B65-B614-3E4E7C05147D}.Release|x86.Build.0 = Release|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Debug|x86.ActiveCfg = Debug|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Debug|x86.Build.0 = Debug|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Release|Any CPU.Build.0 = Release|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Release|x86.ActiveCfg = Release|Any CPU
{C6FC3BF7-BF99-46FE-98F3-56C7C123BDC5}.Release|x86.Build.0 = Release|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Debug|x86.ActiveCfg = Debug|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Debug|x86.Build.0 = Debug|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Release|Any CPU.Build.0 = Release|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Release|x86.ActiveCfg = Release|Any CPU
{EFC8F224-9441-48D0-8FEE-2FC9F948837C}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading

0 comments on commit 1d50962

Please sign in to comment.