/// <summary> /// Authenticates with the server and creates all the necessary components. /// All of them should be initialized inside the synchronized block and MUST NOT call any method on <see cref="SpotifySession">this</see> object. /// </summary> /// <param name="credentials"><see cref="Spotify.LoginCredentials"/></param> /// <exception cref="MercuryException"></exception> private async Task Authenticate( [NotNull] Spotify.LoginCredentials credentials, [NotNull] WebsocketHandler handler) { AuthenticatePartial(credentials, false); using (await authLock.LockAsync()) { //Initialize Services mercuryClient = new MercuryClient(this); eventService = new EventService(this); tokenProvider = new TokenProvider(this); apiClient = await ApiClient.BuildApiClient(this); dealer = new DealerClient(this, handler); //_audioKeyManager = new AudioKeyManager(this); // _channelManager = new ChannelManager(this); // _cdn = new CdnManager(this); // _cache = new CacheManager(_inner.Conf); // _contentFeeder = new PlayableContentFeeder(this); authLockEventWaitHandle.Set(); } receiver = new Receiver(this); eventService.Language(inner.PreferredLocale); TimeProvider.Init(this); Debug.WriteLine($"Authenticated as {apWelcome.CanonicalUsername}!"); Mercury().InterestedIn("spotify:user:attributes:update", this); Dealer().AddMessageListener(this, "hm://connect-state/v1/connect/logout"); }
/// <summary> /// Authenticates with the server. Does not create all the components unlike <see cref="Authenticate"/>. /// </summary> /// <param name="credentials"><see cref="Spotify.LoginCredentials"/></param> /// <param name="removeLock">Whether <see cref="authLockEventWaitHandle"/> should be released or not. /// <code>false</code> for <see cref="Authenticate"/> /// <code>true</code> for <see cref="Reconnect"/></param> private void AuthenticatePartial( [NotNull] Spotify.LoginCredentials credentials, bool removeLock) { GuardAgainst.ArgumentBeingNull(recvCipher); GuardAgainst.ArgumentBeingNull(sendCipher); var clientResponseEncrypted = new ClientResponseEncrypted { LoginCredentials = credentials, SystemInfo = new SystemInfo { Os = Os.Windows, CpuFamily = CpuFamily.CpuX86, SystemInformationString = "1", DeviceId = inner.DeviceId }, VersionString = "1.0" }; SendUnchecked(MercuryPacket.Type.Login, clientResponseEncrypted.ToByteArray(), closedToken); var packet = Receive(conn.Result.NetworkStream, closedToken); switch (packet.Cmd) { case MercuryPacket.Type.APWelcome: { apWelcome = APWelcome.Parser.ParseFrom(packet.Payload); var bytes0X0F = new byte[20]; (new Random()).NextBytes(bytes0X0F); SendUnchecked(MercuryPacket.Type.Unknown_0x0f, bytes0X0F, closedToken); using var preferredLocale = new MemoryStream(18 + 5); preferredLocale.WriteByte((byte)0x0); preferredLocale.WriteByte((byte)0x0); preferredLocale.WriteByte((byte)0x10); preferredLocale.WriteByte((byte)0x0); preferredLocale.WriteByte((byte)0x02); preferredLocale.Write("preferred-locale"); preferredLocale.Write(inner.PreferredLocale); SendUnchecked(MercuryPacket.Type.PreferredLocale, preferredLocale.ToArray(), closedToken); if (removeLock) { using (authLock.Lock()) { authLockEventWaitHandle.Set(); } } try { if (inner.Conf.StoreCredentials) { var jsonObj = new StoredCredentials { AuthenticationType = apWelcome.ReusableAuthCredentialsType, Base64Credentials = apWelcome.ReusableAuthCredentials.ToBase64(), Username = apWelcome.CanonicalUsername }; inner.Conf.StoreCredentialsFunction(jsonObj); // ApplicationData.Current.LocalSettings.Values["auth_data"] = // JsonConvert.SerializeObject(jsonObj); } } catch (Exception x) { Debug.WriteLine(x.ToString()); throw; } break; } case MercuryPacket.Type.AuthFailure: throw new SpotifyAuthenticatedException(APLoginFailed.Parser.ParseFrom(packet.Payload)); default: throw new Exception("Unknown CMD 0x" + packet.Cmd); } }