| @@ -0,0 +1,16 @@ | |||
| using ProtoBuf; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Threading.Tasks; | |||
| namespace GrpcShared.DTO | |||
| { | |||
| [ProtoContract] | |||
| public class TokenMessage | |||
| { | |||
| [ProtoMember(1)] | |||
| public string? Token { get; set; } | |||
| } | |||
| } | |||
| @@ -0,0 +1,20 @@ | |||
| using ProtoBuf; | |||
| using System; | |||
| using System.Collections.Generic; | |||
| using System.Linq; | |||
| using System.Text; | |||
| using System.Threading.Tasks; | |||
| namespace GrpcShared.DTO.User | |||
| { | |||
| [ProtoContract] | |||
| public class UserInfoResponse | |||
| { | |||
| [ProtoMember(1)] | |||
| public string? email { get; set; } | |||
| [ProtoMember(2)] | |||
| public string? id { get; set; } | |||
| [ProtoMember(3)] | |||
| public string? display_name { get; set; } | |||
| } | |||
| } | |||
| @@ -8,7 +8,9 @@ | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> | |||
| <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" /> | |||
| <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | |||
| <PackageReference Include="protobuf-net.BuildTools" Version="3.1.17"> | |||
| <PrivateAssets>all</PrivateAssets> | |||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | |||
| @@ -1,4 +1,6 @@ | |||
| using GrpcShared.DTO.Auth; | |||
| using GrpcShared.DTO; | |||
| using GrpcShared.DTO.Auth; | |||
| using GrpcShared.DTO.User; | |||
| using ProtoBuf.Grpc.Configuration; | |||
| namespace GrpcShared.Interfaces | |||
| @@ -8,6 +10,7 @@ namespace GrpcShared.Interfaces | |||
| { | |||
| Task<TokenResponse> GetAccessToken(TokenRequest code); | |||
| Task<CodeRequest> GetAuthParams(); | |||
| Task<UserInfoResponse> GetUserInfo(TokenMessage token); | |||
| //Task<ClientSecrets> GetClientSecrets(); | |||
| } | |||
| } | |||
| @@ -15,6 +15,7 @@ | |||
| <PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" /> | |||
| <PackageReference Include="protobuf-net.Grpc.AspNetCore.Reflection" Version="1.0.152" /> | |||
| <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" /> | |||
| <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.22.0" /> | |||
| </ItemGroup> | |||
| <ItemGroup> | |||
| @@ -5,6 +5,7 @@ using ProtoBuf.Grpc.Server; | |||
| using Microsoft.Extensions.Options; | |||
| using GrpcShared.DTO.Auth; | |||
| using SpotifyService.Services; | |||
| using Blazored.SessionStorage; | |||
| var builder = WebApplication.CreateBuilder(args); | |||
| #if DEBUG | |||
| @@ -36,6 +37,7 @@ builder.Services.AddEndpointsApiExplorer(); | |||
| builder.Services.AddGrpc(); | |||
| builder.Services.AddCodeFirstGrpc(); | |||
| builder.Services.AddCodeFirstGrpcReflection(); | |||
| builder.Services.AddBlazoredSessionStorage(); | |||
| //call spotify api | |||
| builder.Services.AddHttpClient(); | |||
| @@ -1,11 +1,17 @@ | |||
| //using IdentityProvider.Protos.AuthService; | |||
| using Blazored.SessionStorage; | |||
| using Grpc.Net.Client; | |||
| using GrpcShared; | |||
| using GrpcShared.DTO; | |||
| using GrpcShared.DTO.Auth; | |||
| using GrpcShared.DTO.User; | |||
| using GrpcShared.Interfaces; | |||
| using Microsoft.Extensions.Options; | |||
| using Microsoft.Net.Http.Headers; | |||
| using Newtonsoft.Json; | |||
| using System.Diagnostics; | |||
| using System.IdentityModel.Tokens.Jwt; | |||
| using System.IO; | |||
| using System.Net.Http.Headers; | |||
| using System.Text; | |||
| using System.Text.Json; | |||
| @@ -17,6 +23,7 @@ namespace IdentityProvider.Services | |||
| private readonly ILogger<AuthService> _logger; | |||
| private readonly CodeRequest _params; | |||
| private readonly IHttpClientFactory _httpClientFactory; | |||
| //private ISessionStorageService _sessionStorageService; | |||
| public AuthService(ILogger<AuthService> logger, IOptions<CodeRequest> options, IHttpClientFactory httpClientFactory) | |||
| { | |||
| _logger = logger; | |||
| @@ -74,5 +81,30 @@ namespace IdentityProvider.Services | |||
| return await Task.FromResult(authParams); | |||
| } | |||
| 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; | |||
| var userInfo = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync())!; | |||
| return userInfo; | |||
| //http.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + token.Token); | |||
| //var response = await http.GetAsync(http.BaseAddress + "me"); | |||
| //var user = JsonConvert.DeserializeObject<UserInfoResponse>(await response.Content.ReadAsStringAsync()); | |||
| //return await Task.FromResult(user!); | |||
| } | |||
| } | |||
| } | |||
| @@ -14,7 +14,7 @@ | |||
| "AuthParams": { | |||
| "ClientId": "83e1d09876b049c4bb1953185a4b3bfb", | |||
| "RedirectURI": "https://localhost:44342/callback", | |||
| "Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read", | |||
| "Scope": "user-read-currently-playing user-read-email user-library-modify user-top-read user-read-private", | |||
| "ClientSecret": "ea752433d0774fad87fab5c1ee788c8d" | |||
| } | |||
| } | |||
| @@ -10,6 +10,7 @@ | |||
| <PackageReference Include="Blazored.SessionStorage" Version="2.2.0" /> | |||
| <PackageReference Include="Grpc.Net.Client" Version="2.47.0" /> | |||
| <PackageReference Include="Grpc.Net.Client.Web" Version="2.47.0" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="6.0.8" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="6.0.7" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="6.0.7" PrivateAssets="all" /> | |||
| <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | |||
| @@ -1,32 +1,51 @@ | |||
| @page "/search" | |||
| @using Grpc.Core | |||
| @using GrpcShared.DTO | |||
| @using GrpcShared.DTO.Search | |||
| @using GrpcShared.DTO.Track.MultipleTrack | |||
| @using GrpcShared.DTO.Track.SingleTrack | |||
| @using NemAnBlazor.Services.Interfaces | |||
| @using System.Diagnostics | |||
| @*@inject HttpClient Http*@ | |||
| @inject Blazored.SessionStorage.ISessionStorageService sessionStorage | |||
| @inject ITrackClientService SearchService | |||
| @inject IAuthClientService AuthService | |||
| <PageTitle>Search</PageTitle> | |||
| <h1>Search</h1> | |||
| <button class="btn btn-primary" @onclick="Click">Click me</button> | |||
| @code { | |||
| protected override async Task OnInitializedAsync() | |||
| { | |||
| var token = await sessionStorage.GetItemAsync<string>("token"); | |||
| //SearchRequest request = new() { Query = "aitch", Type = "track", Token = token }; | |||
| //SearchResponse searchResponse = await SearchService.GetListSearchAsync(request); | |||
| MultipleTrackRequest mreq = new() { Ids = new List<string>(){"3JAeYOjyJodI4PRs44lx2l", "6clZa1yrZe7pJrYFUcD9KW"}, Token = token }; | |||
| MultipleTrackResponse multipleTrackResponse = await SearchService.GetListMultipleTrackAsync(mreq); | |||
| //MultipleTrackRequest mreq = new() { Ids = new List<string>(){"3JAeYOjyJodI4PRs44lx2l", "6clZa1yrZe7pJrYFUcD9KW"}, Token = token }; | |||
| //MultipleTrackResponse multipleTrackResponse = await SearchService.GetListMultipleTrackAsync(mreq); | |||
| //SingleTrackRequest singleTrackRequest = new() { Id = "3JAeYOjyJodI4PRs44lx2l", Token = token }; | |||
| //SingleTrackResponse singleTrackResponse = await SearchService.GetListSingleTrackAsync(singleTrackRequest); | |||
| } | |||
| private async Task Click(){ | |||
| var token = await sessionStorage.GetItemAsync<string>("token"); | |||
| TokenMessage tm = new() { Token = token }; | |||
| if ((await AuthService.GetUserInfo(tm)) != null) | |||
| { | |||
| SearchRequest request = new() { Query = "aitch", Type = "track", Token = token }; | |||
| SearchResponse searchResponse = await SearchService.GetListSearchAsync(request); | |||
| } | |||
| } | |||
| } | |||
| @@ -4,6 +4,11 @@ using GrpcShared.Interfaces; | |||
| using NemAnBlazor.Services.Interfaces; | |||
| using ProtoBuf.Grpc.Client; | |||
| using GrpcShared; | |||
| using GrpcShared.DTO.User; | |||
| using GrpcShared.DTO; | |||
| using System.Security.Claims; | |||
| using Microsoft.AspNetCore.Components.Authorization; | |||
| using Blazored.SessionStorage; | |||
| namespace NemAnBlazor.Services | |||
| { | |||
| @@ -24,6 +29,35 @@ namespace NemAnBlazor.Services | |||
| { | |||
| return await _serviceClient.GetAuthParams(); | |||
| } | |||
| public async Task<UserInfoResponse> GetUserInfo(TokenMessage token) | |||
| { | |||
| return await _serviceClient.GetUserInfo(token); | |||
| } | |||
| //public override async Task<AuthenticationState> GetAuthenticationStateAsync() | |||
| //{ | |||
| // string token = await _sessionStorage.GetItemAsync<string>("token"); | |||
| // //token = "BQBMgFm6jnFNWWeZEMGIRP_f-ENPid7Kw8JubAyuWAe4JK0S1DPFGlaAdZ_Fey6ePkCnz8-cqC0oyRmrciWUy5ISUTQKDe8PTQn4iBRMYCgM0n4GnS1xAErHJcm4Vpu2TAngk-4vQUOfTQRcedNTfCaHKP4uFJgTlTI7JHGrtB-_EZLnFcZ2OQe31oFQIJ1wM3ZtvwnN"; | |||
| // if (token == null) return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())); | |||
| // var userInfo = await _serviceClient.GetUserInfo(new TokenMessage { Token = token }); | |||
| // List<Claim> claims = new(); | |||
| // claims.Add(new Claim("email", userInfo.email!)); | |||
| // claims.Add(new Claim("id", userInfo.id!)); | |||
| // claims.Add(new Claim("name", userInfo.display_name!)); | |||
| // ClaimsIdentity identity = new(claims, "jwt"); | |||
| // //ClaimsIdentity identity = new(); | |||
| // ClaimsPrincipal user = new(identity); | |||
| // AuthenticationState state = new(user); | |||
| // NotifyAuthenticationStateChanged(Task.FromResult(state)); | |||
| // return state; | |||
| //} | |||
| } | |||
| } | |||
| @@ -1,5 +1,7 @@ | |||
| using GrpcShared; | |||
| using GrpcShared.DTO; | |||
| using GrpcShared.DTO.Auth; | |||
| using GrpcShared.DTO.User; | |||
| namespace NemAnBlazor.Services.Interfaces | |||
| { | |||
| @@ -7,5 +9,6 @@ namespace NemAnBlazor.Services.Interfaces | |||
| { | |||
| Task<TokenResponse> GetAccessToken(TokenRequest tokenRequest); | |||
| Task<CodeRequest> GetAuthParams(); | |||
| Task<UserInfoResponse> GetUserInfo(TokenMessage token); | |||
| } | |||
| } | |||
| @@ -1,7 +1,11 @@ | |||
| using Microsoft.AspNetCore.Server.Kestrel.Core; | |||
| using NemAnBlazor.Services.Interfaces; | |||
| using NemAnBlazor.Services; | |||
| using ProtoBuf.Grpc.Server; | |||
| using SpotifyService.Services; | |||
| using GrpcShared.Interfaces; | |||
| using Polly; | |||
| using Polly.Extensions.Http; | |||
| var builder = WebApplication.CreateBuilder(args); | |||
| @@ -23,7 +27,46 @@ builder.Services.AddHttpClient("HttpClient", c => | |||
| { | |||
| c.BaseAddress = new Uri(SpotifyService.GLOBALS.SPOTIFYURL); | |||
| c.DefaultRequestHeaders.Add("Accept", SpotifyService.GLOBALS.MEDIATYPE); | |||
| }); | |||
| }) | |||
| .SetHandlerLifetime(TimeSpan.FromMinutes(5)) | |||
| .AddPolicyHandler(GetRetryPolicy()) | |||
| .AddPolicyHandler(GetCircuitBreaker()); | |||
| IAsyncPolicy<HttpResponseMessage> GetCircuitBreaker() | |||
| { | |||
| return HttpPolicyExtensions | |||
| .HandleTransientHttpError() | |||
| //the circuit will be cut if 25% of requests fail in a 60 second window, with a minimum of 7 requests in the 60 second window, | |||
| //then the circuit should be cut for 30 seconds: | |||
| .AdvancedCircuitBreakerAsync(0.25, TimeSpan.FromSeconds(60), | |||
| 7, TimeSpan.FromSeconds(30), OnBreak, OnReset, OnHalfOpen); | |||
| } | |||
| void OnHalfOpen() | |||
| { | |||
| Console.WriteLine("Circuit in test mode, one request will be allowed."); | |||
| } | |||
| void OnReset() | |||
| { | |||
| Console.WriteLine("Circuit closed, requests flow normally."); | |||
| } | |||
| void OnBreak(DelegateResult<HttpResponseMessage> result, TimeSpan ts) | |||
| { | |||
| Console.WriteLine("Circuit cut, requests will not flow."); | |||
| } | |||
| IAsyncPolicy<HttpResponseMessage> GetRetryPolicy() | |||
| { | |||
| return HttpPolicyExtensions | |||
| // HttpRequestException, 5XX and 408 | |||
| .HandleTransientHttpError() | |||
| // 404 | |||
| .OrResult(msg => msg.StatusCode == System.Net.HttpStatusCode.NotFound) | |||
| // Retry two times after delay | |||
| .WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(3, retryAttempt))); | |||
| } | |||
| builder.Services.AddControllersWithViews(); | |||
| builder.Services.AddRazorPages(); | |||
| @@ -64,8 +107,9 @@ app.UseGrpcWeb(); | |||
| app.MapRazorPages(); | |||
| app.MapControllers(); | |||
| //app.MapGrpcService<WeatherService>(); | |||
| //app.MapGrpcService<SearchService>().EnableGrpcWeb(); | |||
| //app.MapGrpcService<AuthService>(); | |||
| //app.MapGrpcService<AuthService>().EnableGrpcWeb(); | |||
| app.MapCodeFirstGrpcReflectionService(); | |||
| @@ -1,11 +1,14 @@ | |||
| using Grpc.Core; | |||
| using Google.Rpc; | |||
| using Grpc.Core; | |||
| using GrpcShared; | |||
| using GrpcShared.DTO.Search; | |||
| using GrpcShared.DTO.Track.MultipleTrack; | |||
| using GrpcShared.DTO.Track.SaveTracks; | |||
| using GrpcShared.DTO.Track.SingleTrack; | |||
| using GrpcShared.Interfaces; | |||
| using Microsoft.AspNetCore.Authorization; | |||
| using Microsoft.Net.Http.Headers; | |||
| using NemAnBlazor.Services; | |||
| using Newtonsoft.Json; | |||
| using System.Text; | |||
| using System.Text.Json; | |||
| @@ -17,16 +20,14 @@ namespace SpotifyService.Services | |||
| public class TrackService : ITrackService | |||
| { | |||
| private readonly IHttpClientFactory _httpClientFactory; | |||
| public TrackService(IHttpClientFactory httpClientFactory) | |||
| { | |||
| _httpClientFactory = httpClientFactory; | |||
| } | |||
| public async Task<SearchResponse> ListSearchAsync(SearchRequest request) | |||
| { | |||
| var client = _httpClientFactory.CreateClient("HttpClient"); | |||
| client.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + request.Token ); | |||
| @@ -7,10 +7,13 @@ | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <PackageReference Include="Google.Api.CommonProtos" Version="2.6.0" /> | |||
| <PackageReference Include="Grpc.AspNetCore" Version="2.40.0" /> | |||
| <PackageReference Include="Grpc.AspNetCore.Web" Version="2.47.0" /> | |||
| <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="6.0.8" /> | |||
| <PackageReference Include="Microsoft.Extensions.Http.Polly" Version="6.0.8" /> | |||
| <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> | |||
| <PackageReference Include="Polly" Version="7.2.3" /> | |||
| <PackageReference Include="protobuf-net.Grpc" Version="1.0.171" /> | |||
| <PackageReference Include="protobuf-net.Grpc.AspNetCore" Version="1.0.152" /> | |||
| <PackageReference Include="protobuf-net.Grpc.AspNetCore.Reflection" Version="1.0.152" /> | |||