Skip to content
Permalink
Browse files
API login and authorization
logging in to the spotify API, as well as authorizing use on mobile webview browsers
  • Loading branch information
Chronos authored and Chronos committed Mar 19, 2023
1 parent b622a26 commit 5242d29cff10effcf3ce9a4390258dc9bab8df19
Show file tree
Hide file tree
Showing 21 changed files with 129 additions and 8 deletions.
@@ -10,7 +10,7 @@ global using CommunityToolkit.Mvvm.Input;
global using CommunityToolkit.Mvvm.ComponentModel;

global using Microsoft.Maui.Controls;

global using app.Services;

namespace app;

@@ -29,6 +29,9 @@ public static class MauiProgram {
#endif
builder.Services.AddTransient<LoginView>();
builder.Services.AddTransient<LoginViewModel>();

builder.Services.AddSingleton<ISpotifyService, SpotifyService>();

return builder.Build();
}
}
@@ -0,0 +1,13 @@
using System;
using System.Text.Json.Serialization;

namespace app.Models;

public record AuthResult {

[JsonPropertyName("access_token")]
public string AccessToken { get; set; }
[JsonPropertyName("refresh_token")]
public string RefreshToken { get; set; }

}
@@ -0,0 +1,9 @@
using System;
namespace app.Services;

public interface ISpotifyService {

Task<bool> Initialize(string authCode);

}

@@ -0,0 +1,38 @@
using System.Text;
using System.Text.Json;
using app.Models;

namespace app.Services;

public class SpotifyService : ISpotifyService {
private string accessToken;

public SpotifyService() {
}

public async Task<bool> Initialize(string authCode) {

var bytes = Encoding.UTF8.GetBytes($"{Constants.SpotifyClientId}:{Constants.SpotifyClientSecret}");
var authHeader = Convert.ToBase64String(bytes);

var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authHeader);

var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>() {
new("code", authCode),
new("redirect_uri", Constants.RedirectUrl),
new("grant_type", "authorization_code")
});

var response = await client.PostAsync("https://accounts.spotify.com/api/token", content);

var json = await response.Content.ReadAsStringAsync();

//deserializing token
var result = JsonSerializer.Deserialize<AuthResult>(json);

accessToken = result.AccessToken;
return response.IsSuccessStatusCode;
}
}

@@ -2,15 +2,24 @@

//partial class in order to be able to use source generators
public partial class LoginViewModel : ViewModel {
public LoginViewModel() {

private readonly ISpotifyService spotifyService;

public LoginViewModel(ISpotifyService spotifyService) {
this.spotifyService = spotifyService;
}

[ObservableProperty]
private bool showLogin;


[RelayCommand]
private void OpenLogin() {
ShowLogin = true;
}

public async Task HandleAuthCode(string authCode) {
await spotifyService.Initialize(authCode);
}

}
@@ -50,8 +50,26 @@ public partial class LoginView {
}
}

private void LoginWeb_Navigating(object sender, WebNavigatingEventArgs e) {
throw new NotImplementedException();
private async void LoginWeb_Navigating(object sender, WebNavigatingEventArgs e) {
//check if the URL is the redirect URL set up within the Spotify application online
//in order to avoid catching the login URL
if (!e.Url.Contains("redirect_uri") && e.Url.Contains("https://app/login")) {
var queryString = e.Url.Split("?").Last();
var parts = queryString.Split("&");

var parameters = parts.Select(x => x.Split("=")).ToDictionary(x => x.First(), x => x.Last());

var code = parameters["code"];
var returnState = parameters["state"];

//animating the login portal
if (returnState == state && !string.IsNullOrWhiteSpace(code)) {
_ = Task.Run(async () => await loginViewModel.HandleAuthCode(code));

await Login.TranslateTo(Login.X, this.Height, easing: Easing.Linear);
Login.IsVisible = false;
}
}
}

}
@@ -61,9 +61,13 @@
<None Remove="Views\" />
<None Remove="ViewModels\" />
<None Remove="Resources\Images\8bb41020a6234deba9e51a50ccabda32-removebg-preview.png" />
<None Remove="Services\" />
<None Remove="Models\" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\" />
<Folder Include="ViewModels\" />
<Folder Include="Services\" />
<Folder Include="Models\" />
</ItemGroup>
</Project>
27 app.sln
@@ -0,0 +1,27 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 17.0.31611.283
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "app", "app\app.csproj", "{E40CBDBA-2A17-477A-B283-EB69BD81D28E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E40CBDBA-2A17-477A-B283-EB69BD81D28E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E40CBDBA-2A17-477A-B283-EB69BD81D28E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E40CBDBA-2A17-477A-B283-EB69BD81D28E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{E40CBDBA-2A17-477A-B283-EB69BD81D28E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E40CBDBA-2A17-477A-B283-EB69BD81D28E}.Release|Any CPU.Build.0 = Release|Any CPU
{E40CBDBA-2A17-477A-B283-EB69BD81D28E}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {61F7FB11-1E47-470C-91E2-47F8143E1572}
EndGlobalSection
EndGlobal
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,2 +1,2 @@
objc[61091]: Class CDPCAReporter is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDPInternal.framework/CoreCDPInternal (0x15abd51d8) and /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDP.framework/CoreCDP (0x15376aeb0). One of the two will be used. Which one is undefined.
objc[61091]: Class CDPCABackupRecoveryReporter is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDPInternal.framework/CoreCDPInternal (0x15abd5ef8) and /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDP.framework/CoreCDP (0x15376b0e0). One of the two will be used. Which one is undefined.
objc[93152]: Class CDPCAReporter is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDPInternal.framework/CoreCDPInternal (0x15a5131d8) and /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDP.framework/CoreCDP (0x1530a8eb0). One of the two will be used. Which one is undefined.
objc[93152]: Class CDPCABackupRecoveryReporter is implemented in both /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDPInternal.framework/CoreCDPInternal (0x15a513ef8) and /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/PrivateFrameworks/CoreCDP.framework/CoreCDP (0x1530a90e0). One of the two will be used. Which one is undefined.

Large diffs are not rendered by default.

@@ -1 +1 @@
98378c31f6eaabff58f5bba47fe4fafa9dbcc3d9
634e2a0b790f7685ed393364c3f302be7651311d
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 5242d29

Please sign in to comment.