From 84698ed2439961ba66d68a1fb96c7e6f4b1eba69 Mon Sep 17 00:00:00 2001 From: arthuridea w <2337583+arthuridea@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:56:58 +0800 Subject: [PATCH] fix:alter project structure.add oidc support. --- NetCore.AIGC.sln | 10 +- .../ChatService/ChatServiceBase.cs | 4 +- .../Controllers/AccountController.cs | 126 ++++++++++++++++++ .../Pages/Account/SignedOut.cshtml | 2 + .../Pages/Account/SignedOut.cshtml.cs | 33 +++++ src/LLMServiceHub/Common/AppConsts.cs | 12 ++ .../Common/AppRuntimeExtensions.cs | 27 ++++ .../v1_0/BaiduApiController.cs | 2 +- .../v1_0/ChatGPTApiController.cs | 2 +- src/LLMServiceHub/LLMServiceHub.csproj | 5 + src/LLMServiceHub/Pages/Index.cshtml | 32 ++--- src/LLMServiceHub/Pages/Index.cshtml.cs | 34 +++-- src/LLMServiceHub/Pages/SignOut.cshtml.cs | 15 ++- src/LLMServiceHub/Program.cs | 11 +- src/LLMServiceHub/Startup.cs | 20 ++- src/LLMServiceHub/appsettings.json | 4 + .../wwwroot/images/Microsoft_logo.svg | 1 + .../wwwroot/images/github-mark.svg | 1 + 18 files changed, 301 insertions(+), 40 deletions(-) create mode 100644 src/LLMServiceHub/Areas/GitHubIdentity/Controllers/AccountController.cs create mode 100644 src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml create mode 100644 src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml.cs create mode 100644 src/LLMServiceHub/Common/AppRuntimeExtensions.cs rename src/LLMServiceHub/{Controller => Controllers}/v1_0/BaiduApiController.cs (98%) rename src/LLMServiceHub/{Controller => Controllers}/v1_0/ChatGPTApiController.cs (96%) create mode 100644 src/LLMServiceHub/wwwroot/images/Microsoft_logo.svg create mode 100644 src/LLMServiceHub/wwwroot/images/github-mark.svg diff --git a/NetCore.AIGC.sln b/NetCore.AIGC.sln index cfdbf46..430b9bc 100644 --- a/NetCore.AIGC.sln +++ b/NetCore.AIGC.sln @@ -23,9 +23,17 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLMService.Baidu.ErnieVilg", "src\LLMService.Baidu.ErnieVilg\LLMService.Baidu.ErnieVilg.csproj", "{E3AE89E4-31A0-4EDA-9154-2D089E1FD8A4}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03.Docs", "03.Docs", "{7E5E6C28-ABB2-4564-AF1B-D9FCC81B8353}" + ProjectSection(SolutionItems) = preProject + docs\build.cmd = docs\build.cmd + docs\docfx.json = docs\docfx.json + docs\index.md = docs\index.md + docs\toc.yml = docs\toc.yml + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLMService.OpenAI.ChatGPT", "src\LLMService.OpenAI.ChatGPT\LLMService.OpenAI.ChatGPT.csproj", "{09322116-1405-47CE-BBB5-35E02167905D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "02.Sample", "02.Sample", "{0B000384-47E8-4013-A1CE-59A465017EF2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -57,7 +65,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {3CA26111-12C9-4AF1-966F-45019EA52385} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16} + {3CA26111-12C9-4AF1-966F-45019EA52385} = {0B000384-47E8-4013-A1CE-59A465017EF2} {DC8A11BE-3268-4F1E-B83F-27958041ABF1} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16} {E73CB18F-26D3-4A6C-8AA2-E89FF17B65C5} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16} {E3AE89E4-31A0-4EDA-9154-2D089E1FD8A4} = {01705FE5-59E6-4B65-B545-5DFD3AFB1F16} diff --git a/src/LLMService.Shared/ChatService/ChatServiceBase.cs b/src/LLMService.Shared/ChatService/ChatServiceBase.cs index c222c4d..2b25385 100644 --- a/src/LLMService.Shared/ChatService/ChatServiceBase.cs +++ b/src/LLMService.Shared/ChatService/ChatServiceBase.cs @@ -13,8 +13,8 @@ namespace LLMService.Shared.ChatService /// /// /// - /// The type of the request dto. - /// The type of the response dto. + /// The type of the request dto. + /// The type of the response dto. /// The type of the backend request dto. /// The type of the backend response dto. /// The type of the chat message. diff --git a/src/LLMServiceHub/Areas/GitHubIdentity/Controllers/AccountController.cs b/src/LLMServiceHub/Areas/GitHubIdentity/Controllers/AccountController.cs new file mode 100644 index 0000000..7443277 --- /dev/null +++ b/src/LLMServiceHub/Areas/GitHubIdentity/Controllers/AccountController.cs @@ -0,0 +1,126 @@ +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Identity.Web; +using Microsoft.AspNetCore.Authorization; +using LLMServiceHub.Common; + +#nullable enable +namespace LLMServiceHub.Areas.GitHubIdentity.Controllers +{ + /// + /// + /// + /// + //[NonController] + [AllowAnonymous] + [Area("GitHubIdentity")] + [Route("[area]/[controller]/[action]")] + public class AccountController : Controller + { + /// + /// Handles user sign in. + /// + /// Authentication scheme. + /// Redirect URI. + /// Challenge generating a redirect to Azure AD to sign in the user. + [HttpGet("{scheme?}")] + public IActionResult SignIn( + [FromRoute] string scheme, + [FromQuery] string redirectUri) + { + scheme ??= AppConsts.GitHubAuthScheme; + string redirect; + if (!string.IsNullOrEmpty(redirectUri) && Url.IsLocalUrl(redirectUri)) + { + redirect = redirectUri; + } + else + { + redirect = Url.Content("~/")!; + } + + return Challenge( + new AuthenticationProperties { RedirectUri = redirect }, + scheme); + } + + /// + /// Challenges the user. + /// + /// Redirect URI. + /// Scopes to request. + /// Login hint. + /// Domain hint. + /// Claims. + /// AAD B2C policy. + /// Authentication scheme. + /// Challenge generating a redirect to Azure AD to sign in the user. + [HttpGet("{scheme?}")] + public IActionResult Challenge( + string redirectUri, + string scope, + string loginHint, + string domainHint, + string claims, + string policy, + [FromRoute] string scheme) + { + scheme ??= AppConsts.GitHubAuthScheme; + Dictionary items = new Dictionary + { + { Constants.Claims, claims }, + { Constants.Policy, policy }, + }; + Dictionary parameters = new Dictionary + { + { Constants.LoginHint, loginHint }, + { Constants.DomainHint, domainHint }, + }; + + OAuthChallengeProperties oAuthChallengeProperties = new OAuthChallengeProperties(items, parameters); + if (scope != null) + { + oAuthChallengeProperties.Scope = scope.Split(" "); + } + oAuthChallengeProperties.RedirectUri = redirectUri; + + return Challenge( + oAuthChallengeProperties, + scheme); + } + + /// + /// Handles the user sign-out. + /// + /// Authentication scheme. + /// Sign out result. + [HttpGet("{scheme?}")] + public IActionResult SignOut( + [FromRoute] string scheme) + { + if (AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled) + { + if (AppServicesAuthenticationInformation.LogoutUrl != null) + { + return LocalRedirect(AppServicesAuthenticationInformation.LogoutUrl); + } + return Ok(); + } + else + { + scheme ??= AppConsts.GitHubAuthScheme; + var callbackUrl = Url.Page("/Account/SignedOut", pageHandler: null, values: null, protocol: Request.Scheme); + return SignOut( + new AuthenticationProperties + { + RedirectUri = callbackUrl, + }, + CookieAuthenticationDefaults.AuthenticationScheme); + } + } + } +} +#nullable disable \ No newline at end of file diff --git a/src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml b/src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml new file mode 100644 index 0000000..e1aa0e3 --- /dev/null +++ b/src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml @@ -0,0 +1,2 @@ +@page +@model LLMServiceHub.Areas.GitHubIdentity.Pages.Account.SignedOutModel diff --git a/src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml.cs b/src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml.cs new file mode 100644 index 0000000..d7f78f9 --- /dev/null +++ b/src/LLMServiceHub/Areas/GitHubIdentity/Pages/Account/SignedOut.cshtml.cs @@ -0,0 +1,33 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace LLMServiceHub.Areas.GitHubIdentity.Pages.Account +{ + /// + /// Model for the SignOut page. + /// + [AllowAnonymous] + public class SignedOutModel : PageModel + { + /// + /// Method handling the HTTP GET method. + /// + /// A Sign Out page or Home page. + public IActionResult OnGet() + { + if (User?.Identity?.IsAuthenticated ?? false) + { + //return LocalRedirect("~/"); + } + + //return Page(); + var returnUrl = HttpContext.Request.Query["returnUrl"]; + if(string.IsNullOrEmpty(returnUrl)) + { + returnUrl = "~/"; + } + return LocalRedirect(returnUrl); + } + } +} diff --git a/src/LLMServiceHub/Common/AppConsts.cs b/src/LLMServiceHub/Common/AppConsts.cs index f2f2f80..0bd664a 100644 --- a/src/LLMServiceHub/Common/AppConsts.cs +++ b/src/LLMServiceHub/Common/AppConsts.cs @@ -13,5 +13,17 @@ public static class AppConsts /// The authentication cookie name /// public const string AuthCookieName = "LLMService.Auth"; + /// + /// The antiforgery cookie name + /// + public const string AntiforgeryCookieName = "LLMService.Antiforgery"; + /// + /// The microsoft authentication scheme + /// + public const string MicrosoftAuthScheme = "Microsoft"; + /// + /// The git hub authentication scheme + /// + public const string GitHubAuthScheme = "GitHub"; } } diff --git a/src/LLMServiceHub/Common/AppRuntimeExtensions.cs b/src/LLMServiceHub/Common/AppRuntimeExtensions.cs new file mode 100644 index 0000000..e488718 --- /dev/null +++ b/src/LLMServiceHub/Common/AppRuntimeExtensions.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Authentication; + +namespace LLMServiceHub.Common +{ + /// + /// + /// + public static class AppRuntimeExtensions + { + /// + /// Gets the external providers asynchronous. + /// + /// The context. + /// + /// + public static async Task GetExternalProvidersAsync(this HttpContext context) + { + ArgumentNullException.ThrowIfNull(context); + + var schemes = context.RequestServices.GetRequiredService(); + + return (from scheme in await schemes.GetAllSchemesAsync() + where !string.IsNullOrEmpty(scheme.DisplayName) + select scheme).ToArray(); + } + } +} diff --git a/src/LLMServiceHub/Controller/v1_0/BaiduApiController.cs b/src/LLMServiceHub/Controllers/v1_0/BaiduApiController.cs similarity index 98% rename from src/LLMServiceHub/Controller/v1_0/BaiduApiController.cs rename to src/LLMServiceHub/Controllers/v1_0/BaiduApiController.cs index 8debb5f..33b00e7 100644 --- a/src/LLMServiceHub/Controller/v1_0/BaiduApiController.cs +++ b/src/LLMServiceHub/Controllers/v1_0/BaiduApiController.cs @@ -9,7 +9,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -namespace LLMServiceHub.Controller.v1_0 +namespace LLMServiceHub.Controllers.v1_0 { /// /// 集成百度AI类api diff --git a/src/LLMServiceHub/Controller/v1_0/ChatGPTApiController.cs b/src/LLMServiceHub/Controllers/v1_0/ChatGPTApiController.cs similarity index 96% rename from src/LLMServiceHub/Controller/v1_0/ChatGPTApiController.cs rename to src/LLMServiceHub/Controllers/v1_0/ChatGPTApiController.cs index 8daaf99..543b527 100644 --- a/src/LLMServiceHub/Controller/v1_0/ChatGPTApiController.cs +++ b/src/LLMServiceHub/Controllers/v1_0/ChatGPTApiController.cs @@ -5,7 +5,7 @@ using LLMServiceHub.Common; using Microsoft.AspNetCore.Mvc; -namespace LLMServiceHub.Controller.v1_0 +namespace LLMServiceHub.Controllers.v1_0 { /// /// diff --git a/src/LLMServiceHub/LLMServiceHub.csproj b/src/LLMServiceHub/LLMServiceHub.csproj index b6fea9c..4fcf696 100644 --- a/src/LLMServiceHub/LLMServiceHub.csproj +++ b/src/LLMServiceHub/LLMServiceHub.csproj @@ -40,6 +40,7 @@ + @@ -51,6 +52,7 @@ + @@ -71,6 +73,9 @@ + + + diff --git a/src/LLMServiceHub/Pages/Index.cshtml b/src/LLMServiceHub/Pages/Index.cshtml index 9397bd5..632927a 100644 --- a/src/LLMServiceHub/Pages/Index.cshtml +++ b/src/LLMServiceHub/Pages/Index.cshtml @@ -360,34 +360,28 @@
or
- diff --git a/src/LLMServiceHub/Pages/Index.cshtml.cs b/src/LLMServiceHub/Pages/Index.cshtml.cs index 30b0d28..9eac365 100644 --- a/src/LLMServiceHub/Pages/Index.cshtml.cs +++ b/src/LLMServiceHub/Pages/Index.cshtml.cs @@ -47,6 +47,14 @@ public class IndexModel : PageModel [TempData] public string ErrorMessage { get; set; } + /// + /// Gets or sets the OAuth schemes. + /// + /// + /// The OAuth schemes. + /// + public AuthenticationScheme[] OAuthSchemes { get; set; } + /// /// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. @@ -101,17 +109,12 @@ public async Task OnGet(string returnUrl = null) ModelState.AddModelError(string.Empty, ErrorMessage); } + OAuthSchemes = await HttpContext.GetExternalProvidersAsync(); + returnUrl ??= Url.Content("~/"); - if(!User.Identity.IsAuthenticated) - { - var result = await HttpContext.AuthenticateAsync(OpenIdConnectDefaults.AuthenticationScheme); - if (result.Succeeded) - { - await ExecSignIn(AppConsts.DefaultAuthScheme, result.Principal.Identity.Name, true, returnUrl); - Redirect(returnUrl); - } - } + await ChallengeExternal(AppConsts.MicrosoftAuthScheme, returnUrl); + await ChallengeExternal(AppConsts.GitHubAuthScheme, returnUrl); ReturnUrl = returnUrl; } @@ -148,6 +151,19 @@ public async Task OnPostAsync(string returnUrl = null) return Page(); } + private async Task ChallengeExternal(string scheme, string returnUrl) + { + if (!User.Identity.IsAuthenticated) + { + var result = await HttpContext.AuthenticateAsync(scheme); + if (result.Succeeded) + { + await ExecSignIn(AppConsts.DefaultAuthScheme, result.Principal.Identity.Name, true, returnUrl); + Redirect(returnUrl); + } + } + } + private async Task ExecSignIn(string scheme, string identityUserName, bool rememberme, string returnUrl) { // set authentication cookie diff --git a/src/LLMServiceHub/Pages/SignOut.cshtml.cs b/src/LLMServiceHub/Pages/SignOut.cshtml.cs index 8109c97..26f832d 100644 --- a/src/LLMServiceHub/Pages/SignOut.cshtml.cs +++ b/src/LLMServiceHub/Pages/SignOut.cshtml.cs @@ -23,12 +23,19 @@ public async Task OnGetAsync(string returnUrl = null) // Clear the existing external cookie to ensure a clean login process await HttpContext.SignOutAsync(); - var oauthResult = await HttpContext.AuthenticateAsync(OpenIdConnectDefaults.AuthenticationScheme); - if (oauthResult.Succeeded) + return await SignOutExternal([AppConsts.MicrosoftAuthScheme, AppConsts.GitHubAuthScheme], returnUrl); + } + + private async Task SignOutExternal(string[] schemes, string returnUrl = null) + { + foreach (var scheme in schemes) { - return RedirectToAction("SignOut", "Account", new { Area = "MicrosoftIdentity", returnUrl }); + var oauthResult = await HttpContext.AuthenticateAsync(scheme); + if (oauthResult.Succeeded) + { + return RedirectToAction("SignOut", "Account", new { Area = $"{scheme}Identity", scheme, returnUrl }); + } } - return LocalRedirect(returnUrl); } } diff --git a/src/LLMServiceHub/Program.cs b/src/LLMServiceHub/Program.cs index 7caf18e..02d3137 100644 --- a/src/LLMServiceHub/Program.cs +++ b/src/LLMServiceHub/Program.cs @@ -43,6 +43,7 @@ public static int Main(string[] args) /// public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() + .ConfigureDefaults(args) .ConfigureAppConfiguration((hostingContext, config) => { config = _loadConfig(args, hostingContext, config); @@ -50,10 +51,18 @@ public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaul .ConfigureWebHostDefaults(webBuilder => { webBuilder + .UseDefaultServiceProvider(conf => + { + }) .UseContentRoot(Directory.GetCurrentDirectory()) .UseIISIntegration() .UseStartup(); - }); + }) + .ConfigureWebHost(config => + { + }) + ; + private static IConfigurationBuilder _loadConfig(string[] args, HostBuilderContext hostingContext, IConfigurationBuilder config = null) { diff --git a/src/LLMServiceHub/Startup.cs b/src/LLMServiceHub/Startup.cs index 3b518dc..ec512bb 100644 --- a/src/LLMServiceHub/Startup.cs +++ b/src/LLMServiceHub/Startup.cs @@ -74,6 +74,12 @@ public void ConfigureServices(IServiceCollection services) options.HandleSameSiteCookieCompatibility(); }); + services.AddAntiforgery(options => + { + options.Cookie.Name = AppConsts.AntiforgeryCookieName; + }); + + services.AddRouting(); // Ensure HttpContext injected. It will be accessed in Chat service. services.AddHttpContextAccessor(); @@ -94,6 +100,7 @@ public void ConfigureServices(IServiceCollection services) /********************************************* End Dependency Injection **********************************************/ + services.AddControllers(); services.AddMvc().AddMicrosoftIdentityUI(); //services.AddControllersWithViews().AddMicrosoftIdentityUI(); @@ -154,10 +161,18 @@ public void ConfigureServices(IServiceCollection services) options.RequireHttpsMetadata = appSettings.RequireHttpsMetadata; options.Audience = appSettings.OidcApiName; }) + .AddGitHub(AppConsts.GitHubAuthScheme, options => + { + options.ClientId = Configuration["GitHub:ClientId"] ?? string.Empty; + options.ClientSecret = Configuration["GitHub:ClientSecret"] ?? string.Empty; + options.EnterpriseDomain = Configuration["GitHub:EnterpriseDomain"] ?? string.Empty; + options.Scope.Add("user:email"); + //options.CallbackPath = new PathString("/signin-github"); + }) //.AddMicrosoftIdentityWebApp(options => Configuration.Bind("AzureAd", options)); ; - services.AddMicrosoftIdentityWebAppAuthentication(configuration: Configuration); + services.AddMicrosoftIdentityWebAppAuthentication(configuration: Configuration, openIdConnectScheme: AppConsts.MicrosoftAuthScheme); services.AddAuthorization(options => { @@ -453,7 +468,8 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, AppSetti app.UseEndpoints(endpoints => { - //endpoints.MapControllers(); + endpoints.MapControllers(); + endpoints.MapDefaultControllerRoute(); endpoints.MapRazorPages(); endpoints.MapControllerRoute( name: "area-route", diff --git a/src/LLMServiceHub/appsettings.json b/src/LLMServiceHub/appsettings.json index 1198f89..560870c 100644 --- a/src/LLMServiceHub/appsettings.json +++ b/src/LLMServiceHub/appsettings.json @@ -35,6 +35,10 @@ "ClientSecret": "", "TokenEndpoint": "" }, + "GitHub": { + "ClientId": "", + "ClientSecret": "" + }, "Users": [ { "UserName": "", diff --git a/src/LLMServiceHub/wwwroot/images/Microsoft_logo.svg b/src/LLMServiceHub/wwwroot/images/Microsoft_logo.svg new file mode 100644 index 0000000..5334aa7 --- /dev/null +++ b/src/LLMServiceHub/wwwroot/images/Microsoft_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/LLMServiceHub/wwwroot/images/github-mark.svg b/src/LLMServiceHub/wwwroot/images/github-mark.svg new file mode 100644 index 0000000..37fa923 --- /dev/null +++ b/src/LLMServiceHub/wwwroot/images/github-mark.svg @@ -0,0 +1 @@ + \ No newline at end of file