From 93ce4db4d79fe0ab0f02da829c389f4eb457ffbd Mon Sep 17 00:00:00 2001 From: Ricardo Carneiro Date: Fri, 31 Jan 2025 18:19:35 -0300 Subject: [PATCH] Chat funcional com login Microsoft. --- Chat.sln | 37 ++++++++++ Chat/Controllers/ChatController.cs | 10 ++- Chat/Controllers/DocumentsController.cs | 94 +++++++++++++++++++++++++ Chat/Controllers/LoginController.cs | 21 ++++-- Chat/Managers/TokenManager.cs | 63 +++++++++++++++++ Chat/Program.cs | 13 ++-- Chat/Properties/launchSettings.json | 4 +- Chat/Views/Chat/Index.cshtml | 4 +- Chat/Views/Documents/Index.cshtml | 50 +++++++++++++ Chat/Views/Documents/New.cshtml | 31 ++++++++ Chat/Views/Shared/_Layout.cshtml | 3 + 11 files changed, 318 insertions(+), 12 deletions(-) create mode 100644 Chat.sln create mode 100644 Chat/Controllers/DocumentsController.cs create mode 100644 Chat/Managers/TokenManager.cs create mode 100644 Chat/Views/Documents/Index.cshtml create mode 100644 Chat/Views/Documents/New.cshtml diff --git a/Chat.sln b/Chat.sln new file mode 100644 index 0000000..1165615 --- /dev/null +++ b/Chat.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatMvc", "Chat\ChatMvc.csproj", "{E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chat.Infra", "Chat.Infra\Chat.Infra.csproj", "{FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chat.Domain", "Chat.Domain\Chat.Domain.csproj", "{C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E4FE7FF1-A6A7-4A40-A940-E32C0679FFB0}.Release|Any CPU.Build.0 = Release|Any CPU + {FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FE24AD1A-B5A7-4CD4-A4AF-B0E4DE3A0E53}.Release|Any CPU.Build.0 = Release|Any CPU + {C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3E28CA0-1BD6-4E09-ACC7-D7E32096F027}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EC4AB36F-49CB-4819-ACA4-3A14CB1D9B50} + EndGlobalSection +EndGlobal diff --git a/Chat/Controllers/ChatController.cs b/Chat/Controllers/ChatController.cs index 90f7a6b..148a52d 100644 --- a/Chat/Controllers/ChatController.cs +++ b/Chat/Controllers/ChatController.cs @@ -7,6 +7,8 @@ using System.Text.Json; using Newtonsoft.Json; using Microsoft.AspNetCore.Antiforgery; using Microsoft.AspNetCore.Authorization; +using System.Linq; +using Microsoft.AspNetCore.Authentication; namespace ChatMvc.Controllers { @@ -27,6 +29,8 @@ namespace ChatMvc.Controllers public IActionResult Index() { var tokens = _antiforgery.GetAndStoreTokens(HttpContext); + var token = HttpContext.GetTokenAsync("access_token").Result; + ViewBag.Token = token; return View(); } @@ -41,9 +45,13 @@ namespace ChatMvc.Controllers return BadRequest("Requisição inválida"); } + var userId = User.Claims.FirstOrDefault(f => f.Type == "UserId").Value; + var name = User.Claims.FirstOrDefault(f => f.Type == "FirstName").Value; + var company = User.Claims.FirstOrDefault(f => f.Type == "CompanyName").Value; + var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value; + var client = _httpClientFactory.CreateClient(); var baseUrl = _configuration["ExternalApiBaseUrl"]; - var token = Request.Headers["Authorization"].ToString(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); diff --git a/Chat/Controllers/DocumentsController.cs b/Chat/Controllers/DocumentsController.cs new file mode 100644 index 0000000..8291fa7 --- /dev/null +++ b/Chat/Controllers/DocumentsController.cs @@ -0,0 +1,94 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Net.Http.Headers; +using static System.Net.Mime.MediaTypeNames; + +namespace ChatMvc.Controllers +{ + [Authorize] + public class DocumentsController : Controller + { + private readonly HttpClient _httpClient; + + public DocumentsController() + { + _httpClient = new HttpClient(); + _httpClient.BaseAddress = new Uri("http://localhost:5020/"); + } + + public async Task Index() + { + var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value; + + _httpClient.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + + var response = await _httpClient.GetAsync("Chat/texts"); + if (response.IsSuccessStatusCode) + { + var texts = await response.Content.ReadFromJsonAsync>(); + return View(texts); + } + return View(new List()); + } + + public IActionResult New() + { + return View(); + } + + public async Task Edit(string id) + { + var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value; + + _httpClient.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + + var response = await _httpClient.GetAsync(string.Format("chat/texts/id/{0}", id)); + if (response.IsSuccessStatusCode) + { + var text = await response.Content.ReadFromJsonAsync(); + var request = new TextRequest() + { + Id = text.Id, + Title = text.Title, + Content = text.Content + }; + return View("New", request); + } + return View("New"); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public async Task Save([FromForm] TextRequest request) + { + var token = User.Claims.FirstOrDefault(f => f.Type == "TokenExternal").Value; + + _httpClient.DefaultRequestHeaders.Authorization = + new AuthenticationHeaderValue("Bearer", token.Replace("Bearer ", "")); + + var response = await _httpClient.PostAsJsonAsync("chat/savetext", request); + if (response.IsSuccessStatusCode) + { + return RedirectToAction("Index"); + } + return View("Novo", request); + } + } + + public class TextResponse + { + public string Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + } + + public class TextRequest + { + public string? Id { get; set; } + public string Title { get; set; } + public string Content { get; set; } + } +} + diff --git a/Chat/Controllers/LoginController.cs b/Chat/Controllers/LoginController.cs index f2e6129..3308eef 100644 --- a/Chat/Controllers/LoginController.cs +++ b/Chat/Controllers/LoginController.cs @@ -5,6 +5,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication; using Stripe; +using ChatMvc.Managers; namespace ChatMvc.Controllers { @@ -12,12 +13,14 @@ namespace ChatMvc.Controllers { private readonly ILogger logger; private readonly IHttpClientFactory httpClientFactory; + private readonly TokenManager tokenManager; private readonly StripeClient _stripeClient; - public LoginController(ILogger logger, IHttpClientFactory httpClientFactory) + public LoginController(ILogger logger, IHttpClientFactory httpClientFactory, TokenManager tokenManager) { this.logger = logger; this.httpClientFactory = httpClientFactory; + this.tokenManager = tokenManager; } public IActionResult Index() @@ -55,12 +58,17 @@ namespace ChatMvc.Controllers if (HttpContext.User.FindFirst(ClaimTypes.GivenName) != null) { + var token = await tokenManager.GetToken(emailExist, "Domvs iT", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value); + claims = new List { new Claim(ClaimTypes.Email, emailExist), new Claim("FirstName", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value), new Claim("FullName", HttpContext.User.FindFirst(ClaimTypes.GivenName).Value + " " + HttpContext.User.FindFirst(ClaimTypes.Surname).Value), - new Claim(ClaimTypes.Role, "User"), + new Claim("CompanyName", "Domvs iT"), + new Claim("UserId", emailExist), + new Claim("TokenExternal", token), + new Claim(ClaimTypes.Role, "User"), }; } else if (HttpContext.User.FindFirst(ClaimTypes.Name)!=null) @@ -68,12 +76,17 @@ namespace ChatMvc.Controllers var name = HttpContext.User.FindFirst(ClaimTypes.Name).Value; var firstName = name.Split(' ')[0]; + var token = await tokenManager.GetToken(emailExist, "Domvs iT", firstName); + claims = new List { new Claim(ClaimTypes.Email, emailExist), new Claim("FirstName", firstName), - new Claim("FullName", name), - new Claim(ClaimTypes.Role, "User"), + new Claim("FullName", name), + new Claim("UserId", emailExist), + new Claim("CompanyName", "Domvs iT"), + new Claim("TokenExternal", token), + new Claim(ClaimTypes.Role, "User"), }; } diff --git a/Chat/Managers/TokenManager.cs b/Chat/Managers/TokenManager.cs new file mode 100644 index 0000000..bec4634 --- /dev/null +++ b/Chat/Managers/TokenManager.cs @@ -0,0 +1,63 @@ +using ChatMvc.Controllers; +using Newtonsoft.Json; +using Stripe.Forwarding; +using System.Net.Http; +using System.Text; + +namespace ChatMvc.Managers +{ + public class TokenManager + { + private readonly IHttpClientFactory _httpClientFactory; + private readonly IConfiguration _configuration; + + public TokenManager(IHttpClientFactory httpClientFactory, IConfiguration configuration) + { + this._httpClientFactory = httpClientFactory; + this._configuration = configuration; + } + + public async Task GetToken(string userId, string company, string name) + { + var client = _httpClientFactory.CreateClient(); + var baseUrl = _configuration["ExternalApiBaseUrl"]; + + // Primeira requisição - newclient + var newClientRequest = new + { + localId = userId, + companyTenant = company, + name = name + }; + + var newClientResponse = await client.PostAsync( + $"{baseUrl}/login/newclient", + new StringContent(JsonConvert.SerializeObject(newClientRequest), + Encoding.UTF8, "application/json")); + + newClientResponse.EnsureSuccessStatusCode(); + var clientContent = await newClientResponse.Content.ReadAsStringAsync(); + var clientResult = JsonConvert.DeserializeObject(clientContent); + + // Segunda requisição - token + var tokenRequest = new + { + clientId = userId, + clientName = name, + clientSecret = clientResult.Secret + }; + + var tokenResponse = await client.PostAsync( + $"{baseUrl}/login/token", + new StringContent(JsonConvert.SerializeObject(tokenRequest), + Encoding.UTF8, "application/json")); + + tokenResponse.EnsureSuccessStatusCode(); + var tokenContent = await tokenResponse.Content.ReadAsStringAsync(); + var tokenResult = JsonConvert.DeserializeObject(tokenContent); + + return tokenResult.Token; + + } + } +} diff --git a/Chat/Program.cs b/Chat/Program.cs index f4c07c7..c28f7c3 100644 --- a/Chat/Program.cs +++ b/Chat/Program.cs @@ -1,4 +1,5 @@ using ChatMvc.LogConfig; +using ChatMvc.Managers; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Google; using Microsoft.AspNetCore.Authentication.MicrosoftAccount; @@ -37,9 +38,12 @@ options => }) .AddCookie(options => { - options.ExpireTimeSpan = TimeSpan.FromMinutes(20); - options.SlidingExpiration = true; + //options.ExpireTimeSpan = TimeSpan.FromMinutes(20); + //options.SlidingExpiration = true; options.AccessDeniedPath = "/Forbidden/"; + options.Cookie.Name = ".AspNet.SharedCookie"; + options.ExpireTimeSpan = TimeSpan.FromDays(30); // Define o tempo de expiração + options.SlidingExpiration = true; // Renova o cookie a cada acesso }) .AddGoogle(googleOptions => { @@ -51,8 +55,7 @@ options => microsoftOptions.ClientId = config.GetSection("Microsoft_ClientId").Value; //microsoftOptions.ClientSecret = "2a7cb1bd-037a-49fa-9e5e-2b2655431af9"; microsoftOptions.ClientSecret = config.GetSection("Microsoft_ClientSecret").Value; -}) -; +}); builder.Services.AddLocalization(options => options.ResourcesPath = "Resources"); @@ -91,6 +94,8 @@ builder.Services.AddAntiforgery(options => options.Cookie.SecurePolicy = CookieSecurePolicy.Always; }); +builder.Services.AddScoped(); + var app = builder.Build(); var locOptions = app.Services.GetService>(); diff --git a/Chat/Properties/launchSettings.json b/Chat/Properties/launchSettings.json index 24f44d9..f1e825d 100644 --- a/Chat/Properties/launchSettings.json +++ b/Chat/Properties/launchSettings.json @@ -11,12 +11,14 @@ }, "https": { "commandName": "Project", + "windowsAuthentication": false, + "anonymousAuthentication": true, "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "dotnetRunMessages": true, - "applicationUrl": "https://localhost:7078;http://localhost:5094" + "applicationUrl": "https://192.168.0.13:7078;http://192.168.0.13:5094;https://localhost:7078;http://localhost:5094" }, "IIS Express": { "commandName": "IISExpress", diff --git a/Chat/Views/Chat/Index.cshtml b/Chat/Views/Chat/Index.cshtml index 0f58515..2295a17 100644 --- a/Chat/Views/Chat/Index.cshtml +++ b/Chat/Views/Chat/Index.cshtml @@ -53,8 +53,8 @@ const messageInput = $('#message-input'); const chatForm = $('#chat-form'); - token = await autenticar(id, company, name); - + //token = await autenticar(id, company, name); + token = '@ViewBag.Token'; // Configuração do Marked marked.setOptions({ breaks: true, diff --git a/Chat/Views/Documents/Index.cshtml b/Chat/Views/Documents/Index.cshtml new file mode 100644 index 0000000..33f9351 --- /dev/null +++ b/Chat/Views/Documents/Index.cshtml @@ -0,0 +1,50 @@ +@using ChatMvc.Controllers +@model IEnumerable + +
+
+
+
+

Documentos

+ + Novo + +
+ + + + + + + + + + @foreach (var item in Model) + { + + + + + + } + +
TítuloConteúdoAções
@item.Title@(item.Content?.Length > 80 ? item.Content.Substring(0, 80) + "..." : item.Content) + + Editar + +
+
+
+
+ +@section Styles { + +} \ No newline at end of file diff --git a/Chat/Views/Documents/New.cshtml b/Chat/Views/Documents/New.cshtml new file mode 100644 index 0000000..2552c99 --- /dev/null +++ b/Chat/Views/Documents/New.cshtml @@ -0,0 +1,31 @@ +@using ChatMvc.Controllers +@model TextRequest + +
+
+
+

Novo Texto

+ @using (Html.BeginForm("Save", "Documents", FormMethod.Post)) + { + @Html.AntiForgeryToken() + + @if (Model!=null && Model.Id != null) + { + @Html.HiddenFor(m => m.Id); + } + +
+ @Html.LabelFor(m => m.Title, "Título") + @Html.TextBoxFor(m => m.Title, new { @class = "form-control" }) +
+
+ @Html.LabelFor(m => m.Content, "Conteúdo") + @Html.TextAreaFor(m => m.Content, new { @class = "form-control", rows = "5" }) +
+
+ +
+ } +
+
+
diff --git a/Chat/Views/Shared/_Layout.cshtml b/Chat/Views/Shared/_Layout.cshtml index 22274d3..828a31f 100644 --- a/Chat/Views/Shared/_Layout.cshtml +++ b/Chat/Views/Shared/_Layout.cshtml @@ -79,6 +79,9 @@ + } @if (User!=null && User.Identity!=null && !User.Identity.IsAuthenticated)