public async Task AuthenticateUserAsync(string clientId, string clientSecret, string localIp, SemaphoreSlim semaphore, CancellationToken cancellationToken) { await semaphore.WaitAsync(); cancellationToken.ThrowIfCancellationRequested(); logger.LogInformation("Starting authentication process."); // If we're authenticating for the first time, we want to clear out our tokens if they exist. using (var context = contextProvider.GenerateContext <ISpotifyContext>(scopeFactory)) { foreach (var token in context.Tokens) { context.Tokens.Remove(token); } await context.SaveChangesAsync(cancellationToken); } Scope[] scopes = new Scope[] { Scope.UserLibraryModify, Scope.UserReadPrivate, Scope.UserReadEmail, Scope.UserLibraryRead, Scope.PlaylistModifyPublic }; // Wrapper class for AuthorizationCodeAuth - Works better with inability to open a browser on pi. Auth = new SpotifyAuthorizationCodeAuth( clientId, clientSecret, $"http://{localIp}:4002", $"http://{localIp}:4002", scopes ); logger.LogInformation($"id - {clientId}\n secret - {clientSecret}\n localIp - {localIp}\n scopes - {scopes}"); // Delegate for when the endpoint has been accessed/approved by user. Auth.AuthReceived += async(sender, payload) => { logger.LogInformation("Authentication received. Creating api object."); Auth.Stop(); Token = await Auth.ExchangeCode(payload.Code); RefreshToken = Token.RefreshToken; // We got a new token, we save it. using (var scope = scopeFactory.CreateScope()) { var context = scope.ServiceProvider.GetService <ISpotifyContext>(); context.Tokens.Add((SpotifyToken)Token); await context.SaveChangesAsync(cancellationToken); } // Denotes that we can refresh the token in the future. IsAuthenticated = true; semaphore.Release(); }; // Starts to listen using the auth endpoint. Auth.Start(); var authString = Auth.CreateUri(); // This will do for now, but I'd like to have a better way of doing this. // Logging it as an error makes it email the code to the provided user. logger.LogError($"Spotify user is not authenticated. Please visit this link to authenticate:\n {authString}"); }