using System; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json.Linq; using RudderSdk.Core.Models.Auth; namespace RudderSdk.Core; /// /// Authentication: device login, explicit token refresh and logout. /// Token refresh also happens automatically — see . /// public sealed class AuthService { private readonly RudderClient _client; internal AuthService(RudderClient client) => _client = client; /// /// Raised when the session state flips: after a successful login /// () and after logout or a failed /// token refresh (). /// public event Action? AuthStateChanged; /// /// Signs the player in with the device identifier. The returned token pair /// is stored in the configured token store. /// public async Task LoginWithDeviceAsync( string region, string language, string? nickname = null, CancellationToken cancellationToken = default) { var response = await _client.SendAsync( "POST", "/sdk/v1/authorization/device", new LoginViaDeviceRequest { Key = _client.ProjectKey, DeviceId = _client.DeviceIdProvider.DeviceId, Region = region, Language = language, Nickname = nickname }, cancellationToken).ConfigureAwait(false); if (response == null || string.IsNullOrEmpty(response.AccessToken) || string.IsNullOrEmpty(response.RefreshToken)) throw new InvalidOperationException("The server returned an incomplete session."); _client.TokenStore.SaveTokens(response.AccessToken, response.RefreshToken); NotifySignedIn(); return response; } /// /// Signs the player in through the project's custom authorization webhook. /// The returned token pair is stored in the configured token store. /// public async Task LoginWithCustomAsync( JToken customData, string region, string language, string? nickname = null, CancellationToken cancellationToken = default) { var response = await _client.SendAsync( "POST", "/sdk/v1/authorization/custom", new LoginViaCustomRequest { Key = _client.ProjectKey, CustomData = customData, Region = region, Language = language, Nickname = nickname }, cancellationToken).ConfigureAwait(false); if (response == null || string.IsNullOrEmpty(response.AccessToken) || string.IsNullOrEmpty(response.RefreshToken)) throw new InvalidOperationException("The server returned an incomplete session."); _client.TokenStore.SaveTokens(response.AccessToken, response.RefreshToken); NotifySignedIn(); return response; } /// /// Exchanges the stored refresh token for a new token pair. Concurrent /// callers share one refresh request. Returns false when the session could /// not be renewed; the stored tokens are cleared in that case. /// public Task RefreshAsync() => _client.RefreshTokensAsync(); /// Drops the stored session. public void Logout() { _client.TokenStore.Clear(); NotifySignedOut(); } internal void NotifySignedIn() => AuthStateChanged?.Invoke(RudderAuthState.SignedIn); internal void NotifySignedOut() => AuthStateChanged?.Invoke(RudderAuthState.SignedOut); }