-
Notifications
You must be signed in to change notification settings - Fork 494
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add SteamAuthentication to handle new Steam login flow sessions #1129
Merged
Merged
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
0084f8e
Create SteamAuthentication
xPaw 3c8472d
Add some polling support
xPaw cf5635a
Add AccessToken to LogOnDetails
xPaw a127e68
Return tokens when poll returns data
xPaw b617056
Put send/poll methods on the session object
xPaw d373630
Add authenticator object and implement 2FA auth
xPaw a8add89
Sort by preferred auth confirmation
xPaw 7603aa8
Add AuthenticationException
xPaw 6322eb3
Add guard_data; add some comments
xPaw 8c7b10f
Add authentication sample
xPaw f92bac3
Change enum to IsPersistentSession bool
xPaw b7d393f
Add cancellation token to polling
xPaw ec96a73
Make `AuthenticationException` public
JustArchi 3687218
Allow consumers to decide whether they want to poll for device confir…
xPaw 5767a15
Add a sample for authenticating using qr codes
xPaw 1c2cc7a
Add more comments
xPaw 8d10d5a
Cleanup, add qr challenge url change callback, add os type
xPaw 43b634a
Use default WebsiteID in sample
xPaw f8ec8a9
Add a sample parsing jwt payload
xPaw 44cf6b4
Deprecate login keys
xPaw b006cd2
Document more website ids
xPaw 2fc013e
Handle incorrect 2FA codes and call the authenticator again
xPaw 20c0b84
Update guard data comment
xPaw 7b266f3
Review
xPaw 2cb4a75
Move authentication stuff to its own namespace
xPaw 2496b06
Suppress LoginKey obsolete warnings in SK codebase
xPaw 9aa61d0
Review
xPaw 79931d7
Change visibility
xPaw 1d41f5f
Added `SteamClient.Authentication`
xPaw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
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 ); | ||
|
||
// get the authentication handler, which used for authenticating with Steam | ||
var auth = new SteamAuthentication( steamClient ); | ||
|
||
// Begin authenticating via credentials | ||
var authSession = await auth.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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
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 ) | ||
{ | ||
// get the authentication handler, which used for authenticating with Steam | ||
var auth = new SteamAuthentication( steamClient ); | ||
|
||
// Start an authentication session by requesting a link | ||
var authSession = await auth.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 ); | ||
} |
22 changes: 22 additions & 0 deletions
22
Samples/1b.QrCodeAuthentication/Sample1b_QrCodeAuthentication.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be tempted to pull in System.IdentityModel.Tokens.Jwt instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried it for a minute, and getting info out of it is more effort than I'm willing to spend.