| @@ -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; } | |||
| } | |||
| } | |||
| @@ -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,6 @@ namespace GrpcShared.Interfaces | |||
| { | |||
| Task<TokenResponse> GetAccessToken(TokenRequest code); | |||
| Task<CodeRequest> GetAuthParams(); | |||
| //Task<ClientSecrets> GetClientSecrets(); | |||
| Task<UserInfoResponse> GetUserInfo(TokenMessage token); | |||
| } | |||
| } | |||
| @@ -1,7 +1,9 @@ | |||
| //using IdentityProvider.Protos.AuthService; | |||
| 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; | |||
| @@ -40,9 +42,9 @@ namespace IdentityProvider.Services | |||
| //AUTHORIZATION HEADER | |||
| http.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Basic " + Convert.ToBase64String(contentType)); | |||
| //ACCEPT HEADER | |||
| http.DefaultRequestHeaders.Accept.Add( | |||
| new MediaTypeWithQualityHeaderValue("application/json")); | |||
| ////ACCEPT HEADER | |||
| //http.DefaultRequestHeaders.Accept.Add( | |||
| // new MediaTypeWithQualityHeaderValue("application/json")); | |||
| //BODY PARAMS | |||
| var requestBody = new Dictionary<string, string>(); | |||
| @@ -54,12 +56,14 @@ namespace IdentityProvider.Services | |||
| var response = await http.PostAsync(url, new FormUrlEncodedContent(requestBody)); | |||
| var contents = JsonConvert.DeserializeObject<TokenResponse>(await response.Content.ReadAsStringAsync()); | |||
| return new TokenResponse | |||
| { | |||
| access_token = contents!.access_token, | |||
| refresh_token = contents!.refresh_token, | |||
| expires_in = contents!.expires_in | |||
| }; | |||
| if (contents != null) | |||
| return new TokenResponse | |||
| { | |||
| access_token = contents!.access_token, | |||
| refresh_token = contents!.refresh_token, | |||
| expires_in = contents!.expires_in | |||
| }; | |||
| else return null; | |||
| } | |||
| public async Task<CodeRequest> GetAuthParams() | |||
| @@ -74,5 +78,19 @@ namespace IdentityProvider.Services | |||
| return await Task.FromResult(authParams); | |||
| } | |||
| public async Task<UserInfoResponse> GetUserInfo(TokenMessage token) | |||
| { | |||
| var http = _httpClientFactory.CreateClient(); | |||
| http.BaseAddress = new Uri(SpotifyService.GLOBALS.SPOTIFYURL); | |||
| //AUTHORIZATION HEADER | |||
| 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" | |||
| } | |||
| } | |||
| @@ -1,8 +1,9 @@ | |||
| <CascadingAuthenticationState> | |||
| @using NemAnBlazor.Pages | |||
| <CascadingAuthenticationState> | |||
| <Router AppAssembly="@typeof(App).Assembly"> | |||
| <Found Context="routeData"> | |||
| <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" > | |||
| <NotAuthorized>Sorry nisi autorizovan</NotAuthorized> | |||
| <NotAuthorized><LoginRedirect/></NotAuthorized> | |||
| </AuthorizeRouteView> | |||
| <FocusOnNavigate RouteData="@routeData" Selector="h1" /> | |||
| </Found> | |||
| @@ -1,26 +0,0 @@ | |||
| using Blazored.SessionStorage; | |||
| using System.Security.Claims; | |||
| namespace NemAnBlazor | |||
| { | |||
| public class AuthProvider : AuthenticationStateProvider | |||
| { | |||
| private readonly ISessionStorageService _sessionStorage; | |||
| public AuthProvider(ISessionStorageService sessionStorage) | |||
| { | |||
| _sessionStorage = sessionStorage; | |||
| } | |||
| public override async Task<AuthenticationState> GetAuthenticationStateAsync() | |||
| { | |||
| string token = await _sessionStorage.GetItemAsync<string>("token"); | |||
| ClaimsIdentity identity = new (); | |||
| ClaimsPrincipal user = new (identity); | |||
| AuthenticationState state = new(user); | |||
| NotifyAuthenticationStateChanged(Task.FromResult(state)); | |||
| return state; | |||
| } | |||
| } | |||
| } | |||
| @@ -6,6 +6,14 @@ | |||
| <ImplicitUsings>enable</ImplicitUsings> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'"> | |||
| <Optimize>True</Optimize> | |||
| </PropertyGroup> | |||
| <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> | |||
| <Optimize>False</Optimize> | |||
| </PropertyGroup> | |||
| <ItemGroup> | |||
| <PackageReference Include="Blazored.SessionStorage" Version="2.2.0" /> | |||
| <PackageReference Include="Grpc.Net.Client" Version="2.47.0" /> | |||
| @@ -10,8 +10,8 @@ | |||
| @code { | |||
| protected override async Task OnInitializedAsync() | |||
| { | |||
| string url = NavigationMgr.Uri; | |||
| @@ -21,11 +21,12 @@ | |||
| var response = await AuthService.GetAccessToken(new GrpcShared.DTO.Auth.TokenRequest { code = code}); | |||
| //if (response.access_token == null) NavigationMgr.NavigateTo("/"); | |||
| //store access token in local storage | |||
| await sessionStorage.SetItemAsync("token", response.access_token); | |||
| await sessionStorage.SetItemAsync("refresh_token", response.refresh_token); | |||
| //redirect to home | |||
| NavigationMgr.NavigateTo("/home"); | |||
| } | |||
| @@ -1,4 +1,5 @@ | |||
| @page "/home" | |||
| @attribute [Authorize] | |||
| <h3>Home</h3> | |||
| <p>login radi</p> | |||
| @@ -7,7 +8,7 @@ | |||
| <p>autorizovan si</p> | |||
| </Authorized> | |||
| <NotAuthorized> | |||
| <p>nisi autorizovan</p> | |||
| <LoginRedirect/> | |||
| </NotAuthorized> | |||
| </AuthorizeView> | |||
| @code { | |||
| @@ -7,7 +7,7 @@ | |||
| @using NemAnBlazor.Services.Interfaces | |||
| @inject NavigationManager NavigationManager | |||
| @inject IAuthClientService AuthService | |||
| @inject ITrackClientService SearchService | |||
| <PageTitle>Index</PageTitle> | |||
| <h1>Pozdrav Diligent!</h1> | |||
| @@ -0,0 +1,7 @@ | |||
| @inject NavigationManager UriHelper | |||
| @code { | |||
| protected override void OnInitialized() | |||
| { | |||
| UriHelper.NavigateTo("/"); | |||
| } | |||
| } | |||
| @@ -26,7 +26,8 @@ builder.Services.AddScoped(_ => | |||
| builder.Services.AddScoped<ITrackClientService, TrackClientService>(); | |||
| builder.Services.AddScoped<IAuthClientService, AuthClientService>(); | |||
| builder.Services.AddBlazoredSessionStorage(); | |||
| builder.Services.AddScoped<AuthenticationStateProvider, AuthProvider>(); | |||
| builder.Services.AddScoped<AuthenticationStateProvider, AuthClientService>(); | |||
| builder.Services.AddAuthorizationCore(); | |||
| await builder.Build().RunAsync(); | |||
| @@ -4,26 +4,62 @@ 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 Blazored.SessionStorage; | |||
| namespace NemAnBlazor.Services | |||
| { | |||
| public class AuthClientService : IAuthClientService | |||
| public class AuthClientService : AuthenticationStateProvider, IAuthClientService | |||
| { | |||
| private IAuthService _serviceClient; | |||
| public AuthClientService(GrpcChannel grpcChannel) | |||
| private readonly ISessionStorageService _sessionStorage; | |||
| public AuthClientService(GrpcChannel grpcChannel, ISessionStorageService sessionStorage) | |||
| { | |||
| _serviceClient = grpcChannel.CreateGrpcService<IAuthService>(); | |||
| _sessionStorage = sessionStorage; | |||
| } | |||
| public async Task<TokenResponse> GetAccessToken(TokenRequest request) | |||
| { | |||
| return await _serviceClient.GetAccessToken(request); | |||
| } | |||
| 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; | |||
| } | |||
| public async Task<CodeRequest> GetAuthParams() | |||
| { | |||
| return await _serviceClient.GetAuthParams(); | |||
| } | |||
| public async Task<UserInfoResponse> GetUserInfo(TokenMessage token) | |||
| { | |||
| return await _serviceClient.GetUserInfo(token); | |||
| } | |||
| } | |||
| } | |||
| @@ -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); | |||
| } | |||
| } | |||
| @@ -9,4 +9,5 @@ | |||
| @using NemAnBlazor | |||
| @using NemAnBlazor.Shared | |||
| @using System.Web | |||
| @using Microsoft.AspNetCore.Components.Authorization | |||
| @using Microsoft.AspNetCore.Components.Authorization | |||
| @using Microsoft.AspNetCore.Authorization | |||