22using Snowcloak . API . Dto . Account ;
33using Snowcloak . API . Routes ;
44using Snowcloak . Utils ;
5- using System . Net ;
65using System . Net . Http . Json ;
7- using System . Security . Cryptography ;
8- using System . Text . Json ;
96
107namespace Snowcloak . WebAPI ;
118
@@ -15,55 +12,60 @@ public async Task<RegisterReplyDto> XIVAuth(CancellationToken token)
1512 {
1613 var secretKey = GenerateSecretKey ( ) ;
1714 var hashedSecretKey = secretKey . GetHash256 ( ) ;
18- var playerName = _dalamudUtilService . GetPlayerNameAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
19- var worldId = ( ushort ) _dalamudUtilService . GetHomeWorldIdAsync ( ) . GetAwaiter ( ) . GetResult ( ) ;
15+ var playerName = await _dalamudUtilService . GetPlayerNameAsync ( ) . ConfigureAwait ( false ) ;
16+ var worldId = ( ushort ) await _dalamudUtilService . GetHomeWorldIdAsync ( ) . ConfigureAwait ( false ) ;
2017 var worldName = _dalamudUtilService . WorldData [ worldId ] ;
2118
22- var sessionID = Convert . ToBase64String ( RandomNumberGenerator . GetBytes ( 32 ) ) . Replace ( '+' , '-' ) . Replace ( '/' , '_' ) . TrimEnd ( '=' ) ;
23- var handshakeUri = new Uri ( "https://account.snowcloak-sync.com/register" ) ;
24- var handshakePayload = new { session_id = sessionID , hashed_secret = hashedSecretKey , character_name = playerName , home_world = worldName } ;
25- var handshakeResponse = await _httpClient . PostAsJsonAsync ( handshakeUri , handshakePayload , token ) . ConfigureAwait ( false ) ;
26- handshakeResponse . EnsureSuccessStatusCode ( ) ;
27- using var registerStream = await handshakeResponse . Content . ReadAsStreamAsync ( token ) . ConfigureAwait ( false ) ;
28- using var register = await JsonDocument . ParseAsync ( registerStream , cancellationToken : token ) . ConfigureAwait ( false ) ;
29- var linkUrl = register . RootElement . TryGetProperty ( "link_url" , out var linkUrlProperty ) ? linkUrlProperty . GetString ( ) : null ;
30- var pollUrl = register . RootElement . TryGetProperty ( "poll_url" , out var pollUrlProperty ) ? pollUrlProperty . GetString ( ) : null ;
31- if ( string . IsNullOrWhiteSpace ( linkUrl ) || string . IsNullOrWhiteSpace ( pollUrl ) )
19+ var startUri = new Uri ( GetApiBaseUri ( ) , XivAuthRegisterStartRoute ) ;
20+ var startPayload = new XivAuthRegisterStartRequestDto
21+ {
22+ HashedSecretKey = hashedSecretKey ,
23+ CharacterName = playerName ,
24+ HomeWorld = worldName
25+ } ;
26+
27+ using var startResponse = await _httpClient . PostAsJsonAsync ( startUri , startPayload , token ) . ConfigureAwait ( false ) ;
28+ if ( ! startResponse . IsSuccessStatusCode )
29+ {
30+ return new RegisterReplyDto { Success = false , ErrorMessage = await ReadErrorAsync ( startResponse , token ) . ConfigureAwait ( false ) } ;
31+ }
32+
33+ var start = await startResponse . Content . ReadFromJsonAsync < XivAuthRegisterStartReplyDto > ( token ) . ConfigureAwait ( false ) ;
34+ if ( start == null || string . IsNullOrWhiteSpace ( start . SessionId ) || string . IsNullOrWhiteSpace ( start . AuthorizationUrl ) )
3235 {
3336 return new RegisterReplyDto { Success = false , ErrorMessage = "Malformed registration response." } ;
3437 }
3538
36- Util . OpenLink ( linkUrl ) ;
37- const int maxAttempts = 600 / 5 ;
38- var pollUri = new Uri ( pollUrl ) ;
39- for ( var i = 0 ; i < maxAttempts ; i ++ )
39+ Util . OpenLink ( start . AuthorizationUrl ) ;
40+ var expiry = start . ExpiresAtUtc > DateTimeOffset . UtcNow ? start . ExpiresAtUtc : DateTimeOffset . UtcNow . AddMinutes ( 10 ) ;
41+ var pollUri = new Uri ( GetApiBaseUri ( ) , XivAuthRegisterPollRoutePrefix + start . SessionId ) ;
42+
43+ while ( DateTimeOffset . UtcNow < expiry )
4044 {
4145 token . ThrowIfCancellationRequested ( ) ;
42- using var resp = await _httpClient . GetAsync ( pollUri , token ) . ConfigureAwait ( false ) ;
43- if ( resp . StatusCode == HttpStatusCode . Gone )
46+ using var pollResponse = await _httpClient . GetAsync ( pollUri , token ) . ConfigureAwait ( false ) ;
47+ if ( ! pollResponse . IsSuccessStatusCode )
4448 {
45- return new RegisterReplyDto
46- {
47- Success = false , ErrorMessage = "Registration session expired. Please try again."
48- } ;
49+ return new RegisterReplyDto { Success = false , ErrorMessage = $ "Registration polling failed ({ ( int ) pollResponse . StatusCode } )." } ;
4950 }
5051
51- if ( resp . StatusCode == HttpStatusCode . OK )
52+ var poll = await pollResponse . Content . ReadFromJsonAsync < XivAuthRegisterPollReplyDto > ( token ) . ConfigureAwait ( false ) ?? new XivAuthRegisterPollReplyDto ( ) ;
53+ if ( poll . Status . Equals ( "pending" , StringComparison . OrdinalIgnoreCase ) )
5254 {
53- using var pollStream = await resp . Content . ReadAsStreamAsync ( token ) . ConfigureAwait ( false ) ;
54- using var pollPayload = await JsonDocument . ParseAsync ( pollStream , cancellationToken : token ) . ConfigureAwait ( false ) ;
55- var status = pollPayload . RootElement . TryGetProperty ( "status" , out var statusProperty ) ? statusProperty . GetString ( ) : null ;
56- if ( status ? . Equals ( "bound" , StringComparison . OrdinalIgnoreCase ) == true )
57- {
58- var uid = pollPayload . RootElement . TryGetProperty ( "uid" , out var uidProperty ) ? uidProperty . GetString ( ) : null ;
59- return new RegisterReplyDto
60- {
61- Success = true , ErrorMessage = string . Empty , UID = uid ?? string . Empty , SecretKey = secretKey
62- } ;
63- }
55+ await Task . Delay ( TimeSpan . FromSeconds ( 5 ) , token ) . ConfigureAwait ( false ) ;
56+ continue ;
6457 }
6558
66- await Task . Delay ( TimeSpan . FromSeconds ( 5 ) , token ) . ConfigureAwait ( false ) ;
59+ if ( poll . Status . Equals ( "completed" , StringComparison . OrdinalIgnoreCase ) )
60+ {
61+ return new RegisterReplyDto { Success = true , ErrorMessage = string . Empty , UID = poll . Uid , SecretKey = secretKey } ;
62+ }
63+
64+ return new RegisterReplyDto
65+ {
66+ Success = false ,
67+ ErrorMessage = string . IsNullOrWhiteSpace ( poll . ErrorMessage ) ? "XIVAuth registration failed. Please try again." : poll . ErrorMessage
68+ } ;
6769 }
6870
6971 return new RegisterReplyDto
0 commit comments