Skip to content

Commit

Permalink
feat:add Microsoft AzureAD login support in demo webapp.
Browse files Browse the repository at this point in the history
  • Loading branch information
arthuridea committed Dec 26, 2023
1 parent 9cf8413 commit 4c681ca
Show file tree
Hide file tree
Showing 13 changed files with 158 additions and 37 deletions.
2 changes: 1 addition & 1 deletion NetCore.AIGC.sln
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLMService.Baidu.ErnieVilg"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "03.Docs", "03.Docs", "{7E5E6C28-ABB2-4564-AF1B-D9FCC81B8353}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LLMService.OpenAI.ChatGPT", "src\LLMService.OpenAI.ChatGPT\LLMService.OpenAI.ChatGPT.csproj", "{09322116-1405-47CE-BBB5-35E02167905D}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LLMService.OpenAI.ChatGPT", "src\LLMService.OpenAI.ChatGPT\LLMService.OpenAI.ChatGPT.csproj", "{09322116-1405-47CE-BBB5-35E02167905D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down
6 changes: 3 additions & 3 deletions docs/docfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
]
}
],
"dest": "dist/api/v0.0.2.0-preview"
"dest": "dist/api/v0.0.2.2-preview"
}
],
"build": {
Expand All @@ -51,7 +51,7 @@
]
}
],
"output": "dist/_site/v0.0.2.0-preview",
"output": "dist/_site/v0.0.2.2-preview",
"template": [
"default",
"modern",
Expand All @@ -62,7 +62,7 @@
"globalMetadata": {
"_appName": "Aeex.LLMService",
"_appTitle": "Aeex.LLMService",
"_currentVersion": "0.0.2.0-preview",
"_currentVersion": "0.0.2.2-preview",
"_enableSearch": true,
"pdf": true
}
Expand Down
4 changes: 2 additions & 2 deletions docs/toc.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- name: Docs
href: docs/
- name: API(v0.0.2.0-preview)
href: dist/api/v0.0.2.0-preview/
- name: API(v0.0.2.2-preview)
href: dist/api/v0.0.2.2-preview/
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@page
@model LLMServiceHub.Areas.MicrosoftIdentity.Pages.Account.SignedOutModel
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace LLMServiceHub.Areas.MicrosoftIdentity.Pages.Account
{
/// <summary>
/// Model for the SignOut page.
/// </summary>
[AllowAnonymous]
public class SignedOutModel : PageModel
{
/// <summary>
/// Method handling the HTTP GET method.
/// </summary>
/// <returns>A Sign Out page or Home page.</returns>
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);
}
}
}
7 changes: 7 additions & 0 deletions src/LLMServiceHub/Common/BuildInUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,12 @@ public class BuildInUser
/// The password.
/// </value>
public string Password { get; set; }
/// <summary>
/// Gets or sets the display name.
/// </summary>
/// <value>
/// The display name.
/// </value>
public string DisplayName { get; set; }
}
}
17 changes: 13 additions & 4 deletions src/LLMServiceHub/LLMServiceHub.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.0.0" />
<PackageReference Include="IdentityModel" Version="6.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Components.QuickGrid.EntityFrameworkAdapter" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.0" />
<PackageReference Include="Microsoft.Identity.Web" Version="2.16.0" />
<PackageReference Include="Microsoft.Identity.Web.UI" Version="2.16.0" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="8.0.0" />
<PackageReference Include="Polly.Extensions" Version="8.2.0" />
<PackageReference Include="Polly.Extensions.Http" Version="3.0.0" />
<!--<PackageReference Include="NSwag.AspNetCore" Version="13.20.0" />-->
Expand All @@ -63,8 +67,14 @@
<ItemGroup>
<PackageReference Include="Aeex.LLMService.Baidu.Wenxin" Version="0.0.2.1-preview" />
<PackageReference Include="Aeex.LLMService.Baidu.ErnieVilg" Version="0.0.2.1-preview" />
<!--<PackageReference Include="Aeex.LLMService.Shared" Version="0.0.2.1-preview" />-->
<PackageReference Include="Aeex.LLMService.OpenAI.ChatGPT" Version="0.0.2.1-preview" />
<PackageReference Include="Aeex.LLMService.OpenAI.ChatGPT" Version="0.0.2.2-preview" />
</ItemGroup>

<ItemGroup>
<Folder Include="Areas\MicrosoftIdentity\Controllers\" />
<Folder Include="Areas\MicrosoftIdentity\Data\" />
<Folder Include="Areas\MicrosoftIdentity\Models\" />
<Folder Include="Areas\MicrosoftIdentity\Views\" />
</ItemGroup>

<!--<ItemGroup Condition="'$(Configuration)' == 'DEBUG'">
Expand All @@ -75,8 +85,7 @@
<Target Name="_ResolveCopyLocalNuGetPkgXmls" AfterTargets="ResolveReferences">
<ItemGroup>
<!-- Copy XML files from all PackageReferences to output dir -->
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'!='' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
<ReferenceCopyLocalPaths Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')" Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)'!='' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
</Project>
11 changes: 10 additions & 1 deletion src/LLMServiceHub/Pages/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,16 @@
<div class="card-body">
<div class="row">
<div class="col">
<p>暂不支持第三方登录</p>
<p>
<environment exclude="Development">
暂不支持第三方登录
</environment>
<environment exclude="Production">
<a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">MS Sign in</a>
</environment>


</p>
</div>
@* <div class="col">
<a href="#" class="btn w-100">
Expand Down
58 changes: 39 additions & 19 deletions src/LLMServiceHub/Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using LLMServiceHub.Common;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.IdentityModel.Tokens;
using Polly;
using System.ComponentModel.DataAnnotations;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
Expand Down Expand Up @@ -92,7 +94,7 @@ public IndexModel(
/// <summary>
/// Called when [get].
/// </summary>
public void OnGet(string returnUrl = null)
public async Task OnGet(string returnUrl = null)
{
if (!string.IsNullOrEmpty(ErrorMessage))
{
Expand All @@ -101,6 +103,16 @@ public void OnGet(string returnUrl = null)

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);
}
}

ReturnUrl = returnUrl;
}

Expand All @@ -121,20 +133,8 @@ public async Task<IActionResult> OnPostAsync(string returnUrl = null)
if (result.Succeeded)
{
_logger.LogInformation("User logged in.");
// set authentication cookie
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, Input.UserName),
new Claim(ClaimTypes.Role, "User")
};
var authProperties = new AuthenticationProperties
{
IsPersistent = Input.RememberMe,// persistance.
RedirectUri = string.IsNullOrWhiteSpace(returnUrl) ? "/Index" : returnUrl,
};
var identity = new ClaimsIdentity(claims, AppConsts.DefaultAuthScheme);
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(AppConsts.DefaultAuthScheme, principal, authProperties);

await ExecSignIn(AppConsts.DefaultAuthScheme, result.IdentityName, Input.RememberMe, returnUrl);
return LocalRedirect(returnUrl);
}
else
Expand All @@ -148,15 +148,35 @@ public async Task<IActionResult> OnPostAsync(string returnUrl = null)
return Page();
}

private (bool Succeeded, string Message) ValidateUser(string username, string password, bool persist = false)
private async Task ExecSignIn(string scheme, string identityUserName, bool rememberme, string returnUrl)
{
// set authentication cookie
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, identityUserName),
new Claim(ClaimTypes.Role, "User")
};
var authProperties = new AuthenticationProperties
{
IsPersistent = rememberme,// persistance.
RedirectUri = string.IsNullOrWhiteSpace(returnUrl) ? "/Index" : returnUrl,
};
var identity = new ClaimsIdentity(claims, scheme);
var principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(scheme, principal, authProperties);
}

private (bool Succeeded, string IdentityName, string Message) ValidateUser(string username, string password)
{
var ret = (Succeeded: false, Message: "用户不存在");
var ret = (Succeeded: false, IdentityName: "", Message: "用户不存在");
var validusers = _configuration.GetSection("Users")
.Get<List<BuildInUser>>();
if(validusers.Any(x=> x.UserName== username && x.Password == password))
var user = validusers.FirstOrDefault(x => x.UserName == username && x.Password == password);
if (user != null)
{
ret.Succeeded = true;
ret.Message = "Ok";
ret.IdentityName = user.DisplayName;
ret.Message = "ok";
}
return ret;
}
Expand Down
2 changes: 1 addition & 1 deletion src/LLMServiceHub/Pages/Shared/_LoginPartial.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<a href="javascript:void(0)" class="nav-link d-flex lh-1 text-reset p-0">
<span class="avatar avatar-sm"></span>
<div class="d-none d-xl-block ps-2">
<div></div>
<div>@(User.Identity.Name)</div>
<div class="mt-1 small text-secondary">测试用户</div>
</div>
</a>
Expand Down
10 changes: 8 additions & 2 deletions src/LLMServiceHub/Pages/SignOut.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using LLMServiceHub.Common;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
Expand All @@ -20,8 +21,13 @@ public async Task<IActionResult> OnGetAsync(string returnUrl = null)
{
returnUrl ??= Url.Content("~/");
// Clear the existing external cookie to ensure a clean login process
//await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
await HttpContext.SignOutAsync(AppConsts.DefaultAuthScheme);
await HttpContext.SignOutAsync();

var oauthResult = await HttpContext.AuthenticateAsync(OpenIdConnectDefaults.AuthenticationScheme);
if (oauthResult.Succeeded)
{
return RedirectToAction("SignOut", "Account", new { Area = "MicrosoftIdentity", returnUrl });
}

return LocalRedirect(returnUrl);
}
Expand Down
32 changes: 29 additions & 3 deletions src/LLMServiceHub/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
using Serilog;
using Swashbuckle.AspNetCore.SwaggerGen;
using Unchase.Swashbuckle.AspNetCore.Extensions.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.Authorization;

namespace LLMServiceHub
{
Expand Down Expand Up @@ -57,6 +63,17 @@ public void ConfigureServices(IServiceCollection services)
var appSettings = Configuration.GetSection(nameof(AppSettings)).Get<AppSettings>();
services.AddSingleton(appSettings);

/***** cookies ***/

services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
// Handling SameSite cookie according to https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
options.HandleSameSiteCookieCompatibility();
});


// Ensure HttpContext injected. It will be accessed in Chat service.
services.AddHttpContextAccessor();
Expand All @@ -77,6 +94,9 @@ public void ConfigureServices(IServiceCollection services)

/********************************************* End Dependency Injection **********************************************/

services.AddMvc().AddMicrosoftIdentityUI();
//services.AddControllersWithViews().AddMicrosoftIdentityUI();


// Add services to the container.
services.AddRazorPages();
Expand All @@ -85,7 +105,7 @@ public void ConfigureServices(IServiceCollection services)
// .AddJsonOptions(options => {
// options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
// });
services.AddMvc();
//services.AddMvc();


#region authentication
Expand Down Expand Up @@ -113,7 +133,7 @@ public void ConfigureServices(IServiceCollection services)

options.Events.OnRedirectToLogin = context =>
{
if(context.Request.Path.StartsWithSegments("/api")||
if (context.Request.Path.StartsWithSegments("/api") ||
string.Equals(context.HttpContext.Request.Query["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal) ||
string.Equals(context.HttpContext.Request.Headers["X-Requested-With"], "XMLHttpRequest", StringComparison.Ordinal))
{
Expand All @@ -124,14 +144,20 @@ public void ConfigureServices(IServiceCollection services)
context.Response.Redirect(context.RedirectUri);
return Task.FromResult(0);
};

//options.ForwardAuthenticate = OpenIdConnectDefaults.AuthenticationScheme;
})
// api use jwt authentication
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = appSettings.IdentityServerBaseUrl;
options.RequireHttpsMetadata = appSettings.RequireHttpsMetadata;
options.Audience = appSettings.OidcApiName;
});
})
//.AddMicrosoftIdentityWebApp(options => Configuration.Bind("AzureAd", options));
;

services.AddMicrosoftIdentityWebAppAuthentication(configuration: Configuration);

services.AddAuthorization(options =>
{
Expand Down
11 changes: 10 additions & 1 deletion src/LLMServiceHub/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@
"CorsAllowAnyOrigin": true,
"CorsAllowOrigins": []
},
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
"TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Azure portal. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]",
"ClientId": "[Enter the Client Id (Application ID obtained from the Azure portal), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]",
"CallbackPath": "/signin-oidc",
"SignedOutCallbackPath": "/signout-callback-oidc"
},
"BaiduWenxinSettings": {
"ClientId": "",
"ClientSecret": "",
Expand All @@ -30,7 +38,8 @@
"Users": [
{
"UserName": "",
"Password": ""
"Password": "",
"DisplayName": ""
}
]
}

0 comments on commit 4c681ca

Please sign in to comment.