| using Newtonsoft.Json; | |||||
| using ProtoBuf; | |||||
| using System; | |||||
| using System.Collections.Generic; | |||||
| using System.Linq; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.DTO.Auth | |||||
| { | |||||
| [ProtoContract] | |||||
| public class RefreshTokenResponse : StatusCodeMessage | |||||
| { | |||||
| [ProtoMember(1)] | |||||
| [JsonProperty("access_token")] | |||||
| public string? AccessToken { get; set; } | |||||
| [ProtoMember(2)] | |||||
| [JsonProperty("expires_in")] | |||||
| public int? Expiration { get; set; } | |||||
| } | |||||
| } |
| namespace GrpcShared.DTO.Search | namespace GrpcShared.DTO.Search | ||||
| { | { | ||||
| [ProtoContract] | [ProtoContract] | ||||
| public class SearchResponse | |||||
| public class SearchResponse : StatusCodeMessage | |||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public Tracks? Tracks { get; set; } | public Tracks? Tracks { get; set; } |
| using ProtoBuf; | |||||
| using System.Net; | |||||
| using System.Text; | |||||
| using System.Threading.Tasks; | |||||
| namespace GrpcShared.DTO | |||||
| { | |||||
| [ProtoContract] | |||||
| [ProtoInclude(5, typeof(Search.SearchResponse))] | |||||
| [ProtoInclude(6, typeof(TopItem.TopItemResponse))] | |||||
| [ProtoInclude(7, typeof(Track.MultipleTrack.MultipleTrackResponse))] | |||||
| [ProtoInclude(8, typeof(Track.SingleTrack.SingleTrackResponse))] | |||||
| [ProtoInclude(9, typeof(TrackByID.TrackResponse))] | |||||
| [ProtoInclude(10, typeof(User.UserInfoResponse))] | |||||
| [ProtoInclude(11, typeof(Track.CurrentTrackResponse))] | |||||
| [ProtoInclude(12, typeof(Auth.RefreshTokenResponse))] | |||||
| public class StatusCodeMessage | |||||
| { | |||||
| [ProtoMember(1)] | |||||
| public HttpStatusCode ResponseMsg { get; set; } | |||||
| } | |||||
| } |
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public string? Token { get; set; } | public string? Token { get; set; } | ||||
| [ProtoMember(2)] | |||||
| public string? RefreshToken { get; set; } | |||||
| } | } | ||||
| } | } |
| namespace GrpcShared.DTO.TopItem | namespace GrpcShared.DTO.TopItem | ||||
| { | { | ||||
| [ProtoContract] | [ProtoContract] | ||||
| public class TopItemResponse | |||||
| public class TopItemResponse : StatusCodeMessage | |||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| [JsonProperty("items")] | [JsonProperty("items")] |
| namespace GrpcShared.DTO.Track | namespace GrpcShared.DTO.Track | ||||
| { | { | ||||
| [ProtoContract] | [ProtoContract] | ||||
| public class CurrentTrackResponse | |||||
| public class CurrentTrackResponse : StatusCodeMessage | |||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| [JsonProperty("timestamp")] | [JsonProperty("timestamp")] |
| namespace GrpcShared.DTO.Track.MultipleTrack | namespace GrpcShared.DTO.Track.MultipleTrack | ||||
| { | { | ||||
| [ProtoContract] | [ProtoContract] | ||||
| public class MultipleTrackResponse | |||||
| public class MultipleTrackResponse : StatusCodeMessage | |||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public List<AudioFeature>? Audio_Features { get; set; } | public List<AudioFeature>? Audio_Features { get; set; } |
| namespace GrpcShared.DTO.Track.SingleTrack | namespace GrpcShared.DTO.Track.SingleTrack | ||||
| { | { | ||||
| [ProtoContract] | [ProtoContract] | ||||
| public class SingleTrackResponse | |||||
| public class SingleTrackResponse : StatusCodeMessage | |||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| public float? Danceability { get; set; } | public float? Danceability { get; set; } |
| namespace GrpcShared.DTO.TrackByID | namespace GrpcShared.DTO.TrackByID | ||||
| { | { | ||||
| [ProtoContract] | [ProtoContract] | ||||
| public class TrackResponse | |||||
| public class TrackResponse : StatusCodeMessage | |||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| [JsonProperty("album")] | [JsonProperty("album")] |
| namespace GrpcShared.DTO.User | namespace GrpcShared.DTO.User | ||||
| { | { | ||||
| [ProtoContract] | [ProtoContract] | ||||
| public class UserInfoResponse | |||||
| public class UserInfoResponse : StatusCodeMessage | |||||
| { | { | ||||
| [ProtoMember(1)] | [ProtoMember(1)] | ||||
| [JsonProperty("email")] | [JsonProperty("email")] |
| Task<TokenResponse> GetAccessToken(TokenRequest code); | Task<TokenResponse> GetAccessToken(TokenRequest code); | ||||
| Task<CodeRequest> GetAuthParams(); | Task<CodeRequest> GetAuthParams(); | ||||
| Task<UserInfoResponse> GetUserInfo(TokenMessage token); | Task<UserInfoResponse> GetUserInfo(TokenMessage token); | ||||
| Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage msg); | |||||
| //Task<ClientSecrets> GetClientSecrets(); | //Task<ClientSecrets> GetClientSecrets(); | ||||
| } | } | ||||
| } | } |
| <ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" /> | <ProjectReference Include="..\NemAnCore\NemAnBlazor.csproj" /> | ||||
| </ItemGroup> | </ItemGroup> | ||||
| <ItemGroup> | |||||
| <Folder Include="Services\" /> | |||||
| </ItemGroup> | |||||
| </Project> | </Project> |
| using GrpcShared; | using GrpcShared; | ||||
| using IdentityProvider.Services; | |||||
| using SpotifyService.Services; | |||||
| using Microsoft.AspNetCore.Server.Kestrel.Core; | using Microsoft.AspNetCore.Server.Kestrel.Core; | ||||
| using ProtoBuf.Grpc.Server; | using ProtoBuf.Grpc.Server; | ||||
| using Microsoft.Extensions.Options; | using Microsoft.Extensions.Options; | ||||
| using GrpcShared.DTO.Auth; | using GrpcShared.DTO.Auth; | ||||
| using SpotifyService.Services; | |||||
| using Blazored.LocalStorage; | using Blazored.LocalStorage; | ||||
| var builder = WebApplication.CreateBuilder(args); | var builder = WebApplication.CreateBuilder(args); | ||||
| builder.Services.AddOptions(); | builder.Services.AddOptions(); | ||||
| // Additional configuration is required to successfully run gRPC on macOS. | // Additional configuration is required to successfully run gRPC on macOS. | ||||
| // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 | // For instructions on how to configure Kestrel and gRPC clients on macOS, visit https://go.microsoft.com/fwlink/?linkid=2099682 | ||||
| builder.Services.Configure<CodeRequest>(builder.Configuration.GetSection("AuthParams")); | |||||
| builder.Services.AddControllersWithViews(); | builder.Services.AddControllersWithViews(); | ||||
| builder.Services.AddRazorPages(); | builder.Services.AddRazorPages(); | ||||
| builder.Services.AddBlazoredLocalStorage(); | builder.Services.AddBlazoredLocalStorage(); | ||||
| //call spotify api | //call spotify api | ||||
| builder.Services.AddHttpClient(); | builder.Services.AddHttpClient(); | ||||
| builder.Services.Configure<CodeRequest>(builder.Configuration.GetSection("AuthParams")); | |||||
| var app = builder.Build(); | var app = builder.Build(); | ||||
| @using NemAnBlazor.Pages | @using NemAnBlazor.Pages | ||||
| <CascadingAuthenticationState> | |||||
| <Router AppAssembly="@typeof(App).Assembly"> | <Router AppAssembly="@typeof(App).Assembly"> | ||||
| <Found Context="routeData"> | <Found Context="routeData"> | ||||
| <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" > | <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" > | ||||
| <Authorizing> | <Authorizing> | ||||
| <text>Please wait, we are authorizint the user.</text> | |||||
| </Authorizing> | |||||
| <text>Please wait, we are authorizing the user.</text> | |||||
| </Authorizing> | |||||
| </AuthorizeRouteView> | </AuthorizeRouteView> | ||||
| <FocusOnNavigate RouteData="@routeData" Selector="h1" /> | <FocusOnNavigate RouteData="@routeData" Selector="h1" /> | ||||
| </Found> | </Found> | ||||
| </LayoutView> | </LayoutView> | ||||
| </NotFound> | </NotFound> | ||||
| </Router> | </Router> | ||||
| </CascadingAuthenticationState> |
| { | { | ||||
| var token = await localStorage.GetItemAsync<string>("token"); | var token = await localStorage.GetItemAsync<string>("token"); | ||||
| TokenMessage tm = new() { Token = token }; | |||||
| string refreshT = await localStorage.GetItemAsync<string>("refresh_token"); | |||||
| TokenMessage tokenM = new TokenMessage{Token = token, RefreshToken = refreshT}; | |||||
| SearchRequest request = new() { Query = "aitch", Type = "track", Token = token }; | SearchRequest request = new() { Query = "aitch", Type = "track", Token = token }; | ||||
| SearchResponse searchResponse = await SearchService.GetListSearchAsync(request); | SearchResponse searchResponse = await SearchService.GetListSearchAsync(request); | ||||
| if (searchResponse.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | |||||
| { | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, tokenM, localStorage); | |||||
| token = tempToken == null ? token : tempToken; | |||||
| } | |||||
| } | } | ||||
| @inject Blazored.LocalStorage.ILocalStorageService localStorage | @inject Blazored.LocalStorage.ILocalStorageService localStorage | ||||
| @inject IStatsClientService spotifyService | @inject IStatsClientService spotifyService | ||||
| @inject ITrackClientService trackService | @inject ITrackClientService trackService | ||||
| @inject IAuthClientService AuthService | |||||
| <h3>Home</h3> | <h3>Home</h3> | ||||
| protected override async Task OnInitializedAsync() | protected override async Task OnInitializedAsync() | ||||
| { | { | ||||
| string tokenS = await localStorage.GetItemAsync<string>("token"); | string tokenS = await localStorage.GetItemAsync<string>("token"); | ||||
| TokenMessage token = new TokenMessage{Token = tokenS}; | |||||
| string refreshT = await localStorage.GetItemAsync<string>("refresh_token"); | |||||
| CurrentTrackResponse response = await spotifyService.GetCurrentlyPlayingTrack(token); | |||||
| //tokenS = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | |||||
| TokenMessage token = new TokenMessage { Token = tokenS, RefreshToken = refreshT }; | |||||
| //napravi komponentu koja ce da prikazuje sta trenutno slusas i passuj joj parametre | |||||
| CurrentTrackResponse response = await spotifyService.GetCurrentlyPlayingTrack(token); | |||||
| //4fy1A2WBTPX55mUI16TQXa | |||||
| //if token expired, refresh it | |||||
| if (response.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | |||||
| { | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, localStorage); | |||||
| tokenS = tempToken == null ? tokenS : tempToken; | |||||
| //if refreshed token is null, that means that refresh token was invalid, so you should redirect to login | |||||
| } | |||||
| //napravi komponentu koja ce da prikazuje sta trenutno slusas i passuj joj parametre | |||||
| //var trackById = await trackService.GetById(new GrpcShared.DTO.TrackByID.TrackRequest { TrackID = "4fy1A2WBTPX55mUI16TQXa", Token = tokenS }); | //var trackById = await trackService.GetById(new GrpcShared.DTO.TrackByID.TrackRequest { TrackID = "4fy1A2WBTPX55mUI16TQXa", Token = tokenS }); | ||||
| var items = await spotifyService.GetTopItems(new GrpcShared.DTO.TopItem.TopItemRequest { Token = tokenS, IsTracks = false, Offset = 5}); | |||||
| var items = await spotifyService.GetTopItems(new GrpcShared.DTO.TopItem.TopItemRequest { Token = tokenS, IsTracks = false, Offset = 5 }); | |||||
| if (items.ResponseMsg == System.Net.HttpStatusCode.Unauthorized) | |||||
| { | |||||
| string? tempToken = await SpotifyHelper.TryRefreshToken(AuthService, token, localStorage); | |||||
| tokenS = tempToken == null ? tokenS : tempToken; | |||||
| } | |||||
| } | } | ||||
| } | } |
| @using System.Security.Claims | @using System.Security.Claims | ||||
| <AuthorizeView> | <AuthorizeView> | ||||
| <Authorized> | <Authorized> | ||||
| Dobrodosli @context.User.Claims.FirstOrDefault(x => x.Type == "name")?.Value.ToUpper() | |||||
| <p>Dobrodosli @context.User.Claims.FirstOrDefault(x => x.Type == "name")?.Value.ToUpper()</p> | |||||
| </Authorized> | </Authorized> | ||||
| <NotAuthorized> | <NotAuthorized> | ||||
| Nisi autorizovan. | Nisi autorizovan. | ||||
| <button class="btn btn-primary" @onclick="LoginUser">Autorizuj</button> | |||||
| <button class="btn btn-primary" @onclick="LoginUser">Login</button> | |||||
| </NotAuthorized> | </NotAuthorized> | ||||
| </AuthorizeView> | </AuthorizeView> | ||||
| @code { | @code { | ||||
| private string? message; | private string? message; | ||||
| protected override async Task OnInitializedAsync() | |||||
| { | |||||
| message = "Cao"; | |||||
| } | |||||
| private async Task LoginUser() | private async Task LoginUser() | ||||
| { | { |
| return channel; | return channel; | ||||
| }); | }); | ||||
| builder.Services.AddAuthorizationCore(); | builder.Services.AddAuthorizationCore(); | ||||
| builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | |||||
| //builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | |||||
| builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | ||||
| builder.Services.AddScoped<IAuthClientService, AuthClientService>(); | builder.Services.AddScoped<IAuthClientService, AuthClientService>(); | ||||
| builder.Services.AddScoped<IStatsClientService, StatsClientService>(); | builder.Services.AddScoped<IStatsClientService, StatsClientService>(); | ||||
| builder.Services.AddBlazoredLocalStorage(); | |||||
| builder.Services.AddBlazoredLocalStorage(); | |||||
| builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | ||||
| builder.Services.AddAuthorizationCore(); | builder.Services.AddAuthorizationCore(); | ||||
| { | { | ||||
| private IAuthService _serviceClient; | private IAuthService _serviceClient; | ||||
| private ILocalStorageService _sessionStorage; | |||||
| private ILocalStorageService _localStorage; | |||||
| public AuthClientService(GrpcChannel grpcChannel, ILocalStorageService sessionStorage) | public AuthClientService(GrpcChannel grpcChannel, ILocalStorageService sessionStorage) | ||||
| { | { | ||||
| _serviceClient = grpcChannel.CreateGrpcService<IAuthService>(); | _serviceClient = grpcChannel.CreateGrpcService<IAuthService>(); | ||||
| _sessionStorage = sessionStorage; | |||||
| _localStorage = sessionStorage; | |||||
| } | } | ||||
| public async Task<TokenResponse> GetAccessToken(TokenRequest request) | public async Task<TokenResponse> GetAccessToken(TokenRequest request) | ||||
| { | { | ||||
| //public override async Task<AuthenticationState> GetAuthenticationStateAsync() | //public override async Task<AuthenticationState> GetAuthenticationStateAsync() | ||||
| //{ | //{ | ||||
| // string token = await _sessionStorage.GetItemAsync<string>("token"); | |||||
| // string token = await _localStorage.GetItemAsync<string>("token"); | |||||
| // //token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | |||||
| // token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | |||||
| // if (token == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); | // if (token == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); | ||||
| // var userInfo = await _serviceClient.GetUserInfo(new TokenMessage ( token )); | |||||
| // var userInfo = await _serviceClient.GetUserInfo(new TokenMessage { Token = token }); | |||||
| // List<Claim> claims = new(); | // List<Claim> claims = new(); | ||||
| // claims.Add(new Claim("name", userInfo.display_name!)); | // claims.Add(new Claim("name", userInfo.display_name!)); | ||||
| // ClaimsIdentity identity = new(claims, "jwt"); | // ClaimsIdentity identity = new(claims, "jwt"); | ||||
| // //ClaimsIdentity identity = new(); | |||||
| // ClaimsIdentity identity = new(); | |||||
| // ClaimsPrincipal user = new(identity); | // ClaimsPrincipal user = new(identity); | ||||
| // AuthenticationState state = new(user); | // AuthenticationState state = new(user); | ||||
| public override async Task<AuthenticationState> GetAuthenticationStateAsync() | public override async Task<AuthenticationState> GetAuthenticationStateAsync() | ||||
| { | { | ||||
| await Task.Delay(1500); | |||||
| //return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); | |||||
| await Task.Delay(500); | |||||
| string token = await _sessionStorage.GetItemAsync<string>("token"); | |||||
| string token = await _localStorage.GetItemAsync<string>("token"); | |||||
| string refreshT = await _localStorage.GetItemAsync<string>("refresh_token"); | |||||
| //token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | //token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | ||||
| if (token == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); | if (token == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); | ||||
| var userInfo = await _serviceClient.GetUserInfo(new TokenMessage{Token = token}); | |||||
| var userInfo = await _serviceClient.GetUserInfo(new TokenMessage { Token = token, RefreshToken = refreshT }); | |||||
| List<Claim> claims = new(); | List<Claim> claims = new(); | ||||
| ClaimsPrincipal user = new(identity); | ClaimsPrincipal user = new(identity); | ||||
| AuthenticationState state = new(user); | AuthenticationState state = new(user); | ||||
| NotifyAuthenticationStateChanged(Task.FromResult(state)); | |||||
| // NotifyAuthenticationStateChanged(Task.FromResult(state)); | |||||
| return state; | return state; | ||||
| } | } | ||||
| public async Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage token) | |||||
| { | |||||
| return await _serviceClient.RefreshAccessToken(token); | |||||
| } | |||||
| } | } | ||||
| } | } |
| Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest); | Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest); | ||||
| Task<CodeRequest> GetAuthParams(); | Task<CodeRequest> GetAuthParams(); | ||||
| Task<UserInfoResponse> GetUserInfo(TokenMessage token); | Task<UserInfoResponse> GetUserInfo(TokenMessage token); | ||||
| Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage token); | |||||
| } | } | ||||
| } | } |
| using Blazored.LocalStorage; | |||||
| using GrpcShared.DTO; | |||||
| using NemAnBlazor.Services.Interfaces; | |||||
| namespace NemAnBlazor | |||||
| { | |||||
| public static class SpotifyHelper | |||||
| { | |||||
| public static async Task<string?> TryRefreshToken(IAuthClientService authService, TokenMessage msg, ILocalStorageService localStorage) | |||||
| { | |||||
| var refreshResponse = await authService.RefreshAccessToken(msg); | |||||
| if (refreshResponse.AccessToken != null) | |||||
| { | |||||
| await localStorage.SetItemAsync<string>("token", refreshResponse.AccessToken); | |||||
| return refreshResponse.AccessToken; | |||||
| } | |||||
| else return null; | |||||
| } | |||||
| } | |||||
| } |
| using GrpcShared.Interfaces; | using GrpcShared.Interfaces; | ||||
| using Polly; | using Polly; | ||||
| using Polly.Extensions.Http; | using Polly.Extensions.Http; | ||||
| using GrpcShared.DTO.Auth; | |||||
| var builder = WebApplication.CreateBuilder(args); | var builder = WebApplication.CreateBuilder(args); | ||||
| // Retry two times after delay | // Retry two times after delay | ||||
| .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt))); | .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt))); | ||||
| } | } | ||||
| builder.Services.AddOptions(); | |||||
| builder.Services.AddControllersWithViews(); | builder.Services.AddControllersWithViews(); | ||||
| builder.Services.AddRazorPages(); | builder.Services.AddRazorPages(); | ||||
| builder.Services.AddCodeFirstGrpcReflection(); | builder.Services.AddCodeFirstGrpcReflection(); | ||||
| var app = builder.Build(); | var app = builder.Build(); | ||||
| builder.Services.Configure<CodeRequest>(builder.Configuration.GetSection("AuthParams")); | |||||
| app.UseSwagger(); | app.UseSwagger(); | ||||
| app.UseSwaggerUI(); | app.UseSwaggerUI(); |
| using Microsoft.Net.Http.Headers; | using Microsoft.Net.Http.Headers; | ||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
| using System.Diagnostics; | using System.Diagnostics; | ||||
| using System.IdentityModel.Tokens.Jwt; | |||||
| using System.IO; | using System.IO; | ||||
| using System.Net; | |||||
| using System.Net.Http.Headers; | using System.Net.Http.Headers; | ||||
| using System.Text; | using System.Text; | ||||
| using System.Text.Json; | using System.Text.Json; | ||||
| namespace IdentityProvider.Services | |||||
| namespace SpotifyService.Services | |||||
| { | { | ||||
| public class AuthService : IAuthService | public class AuthService : IAuthService | ||||
| { | { | ||||
| _httpClientFactory = httpClientFactory; | _httpClientFactory = httpClientFactory; | ||||
| } | } | ||||
| public async Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest) | public async Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest) | ||||
| { | { | ||||
| var http = _httpClientFactory.CreateClient(); | |||||
| var http = _httpClientFactory.CreateClient("HttpClient"); | |||||
| string url = "https://accounts.spotify.com/api/token"; | string url = "https://accounts.spotify.com/api/token"; | ||||
| http.BaseAddress = new Uri(url); | http.BaseAddress = new Uri(url); | ||||
| var contents = JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync()); | var contents = JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync()); | ||||
| return contents; | return contents; | ||||
| } | } | ||||
| public async Task<CodeRequest> GetAuthParams() | public async Task<CodeRequest> GetAuthParams() | ||||
| public async Task<UserInfoResponse> GetUserInfo(TokenMessage tokenM) | public async Task<UserInfoResponse> GetUserInfo(TokenMessage tokenM) | ||||
| { | { | ||||
| //var des = JsonConvert.DeserializeObject<TokenMessage>(tokenM); | |||||
| //var tokenStorage = _sessionStorageService.GetItemAsync<string>("token"); | |||||
| //hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken); | |||||
| //var response = hc.GetAsync(userInfoUrl).Result; | |||||
| //dynamic userInfo = response.Content.ReadAsAsync().Result; | |||||
| //return userInfo; | |||||
| var http = _httpClientFactory.CreateClient(); | |||||
| http.BaseAddress = new Uri("https://api.spotify.com/v1/me"); | |||||
| http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenM.Token); | |||||
| var response = http.GetAsync(http.BaseAddress).Result; | |||||
| // expired token example "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN" | |||||
| var http = _httpClientFactory.CreateClient("HttpClient"); | |||||
| http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokenM.Token!); | |||||
| var response = await http.GetAsync("me"); | |||||
| //make this a method in http utils | |||||
| if (response.StatusCode == HttpStatusCode.Unauthorized) | |||||
| { | |||||
| //refresh the token | |||||
| var refreshResponse = await RefreshAccessToken(tokenM); | |||||
| //if response is invalid redirect to login | |||||
| http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", refreshResponse.AccessToken); | |||||
| response = await http.GetAsync("me"); | |||||
| } | |||||
| //var headerError = response.Headers.WwwAuthenticate.; | |||||
| var userInfo = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync())!; | var userInfo = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync())!; | ||||
| userInfo.ResponseMsg = response.StatusCode; | |||||
| return userInfo; | return userInfo; | ||||
| //http.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + token.Token); | |||||
| } | |||||
| public async Task<RefreshTokenResponse> RefreshAccessToken(TokenMessage tokenM) | |||||
| { | |||||
| var client = _httpClientFactory.CreateClient("HttpClient"); | |||||
| client.BaseAddress = new Uri("https://accounts.spotify.com/api/token"); | |||||
| //BODY PARAMS | |||||
| var requestBody = new Dictionary<string, string>(); | |||||
| requestBody["refresh_token"] = tokenM.RefreshToken!; | |||||
| requestBody["grant_type"] = "refresh_token"; | |||||
| var secrets = await GetAuthParams(); | |||||
| byte[] contentType = Encoding.UTF8.GetBytes($"{secrets.ClientId}:{secrets.ClientSecret}"); | |||||
| //var response = await http.GetAsync(http.BaseAddress + "me"); | |||||
| //AUTHORIZATION HEADER | |||||
| client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Basic " + Convert.ToBase64String(contentType)); | |||||
| //var user = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync()); | |||||
| //REQUEST | |||||
| var response = await client.PostAsync("https://accounts.spotify.com/api/token", new FormUrlEncodedContent(requestBody)); | |||||
| //return await Task.FromResult(user!); | |||||
| if (response.StatusCode == HttpStatusCode.Unauthorized) | |||||
| { | |||||
| //delete tokens from localstorage | |||||
| //redirect to login | |||||
| } | |||||
| var contents = JsonConvert.DeserializeObject<RefreshTokenResponse>(await response.Content.ReadAsStringAsync())!; | |||||
| return contents; | |||||
| } | } | ||||
| } | } | ||||
| } | } |
| var client = _httpClientFactory.CreateClient("HttpClient"); | var client = _httpClientFactory.CreateClient("HttpClient"); | ||||
| string url = "me/player/currently-playing"; | string url = "me/player/currently-playing"; | ||||
| return await HttpUtils<CurrentTrackResponse>.GetData(client, url, token.Token!); | |||||
| var response = await HttpUtils<CurrentTrackResponse>.GetData(client, url, token.Token!); | |||||
| return response; | |||||
| } | } | ||||
| using Google.Rpc; | using Google.Rpc; | ||||
| using Grpc.Core; | using Grpc.Core; | ||||
| using GrpcShared; | using GrpcShared; | ||||
| using GrpcShared.DTO; | |||||
| using GrpcShared.DTO.Search; | using GrpcShared.DTO.Search; | ||||
| using GrpcShared.DTO.Track.MultipleTrack; | using GrpcShared.DTO.Track.MultipleTrack; | ||||
| using GrpcShared.DTO.Track.SaveTracks; | using GrpcShared.DTO.Track.SaveTracks; | ||||
| string url = $"me/tracks/{query}"; | string url = $"me/tracks/{query}"; | ||||
| //the response type has nothing to do with the method, it's there so that the method can be called | //the response type has nothing to do with the method, it's there so that the method can be called | ||||
| await HttpUtils.HttpUtils<MultipleTrackResponse>.PutData(client, url, request.Token!); | |||||
| await HttpUtils.HttpUtils<StatusCodeMessage>.PutData(client, url, request.Token!); | |||||
| } | } | ||||
| public static string UriUtil(Dictionary<string, List<string>> param) | public static string UriUtil(Dictionary<string, List<string>> param) |
| "EndpointDefaults": { | "EndpointDefaults": { | ||||
| "Protocols": "Http1AndHttp2" | "Protocols": "Http1AndHttp2" | ||||
| } | } | ||||
| }, | |||||
| "AuthParams": { | |||||
| "ClientId": "83e1d09876b049c4bb1953185a4b3bfb", | |||||
| "RedirectURI": "https://localhost:44342/callback", | |||||
| "Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read user-read-private", | |||||
| "ClientSecret": "ea752433d0774fad87fab5c1ee788c8d" | |||||
| } | } | ||||
| } | } |