From 5e6ca18770f43db70ac88e842a534275a1a874ea Mon Sep 17 00:00:00 2001 From: yubaolee Date: Thu, 25 Jun 2026 23:42:43 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=84refactor:=20=E4=BD=BF=E7=94=A8OpenI?= =?UTF-8?q?ddict=20=E9=87=8D=E6=9E=84Identity?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CheckAndKillPort12796.bat | 25 ++ Infrastructure/Const/FlowInstanceStatus.cs | 2 +- OpenAuth.Identity/Config.cs | 102 ------ .../Controllers/AuthorizationController.cs | 165 +++++++++ .../Controllers/TokenController.cs | 135 +++++++ .../Controllers/UserinfoController.cs | 92 +++++ OpenAuth.Identity/CustomProfileService.cs | 101 ------ .../Handlers/OpenIddictValidationHandlers.cs | 129 +++++++ .../OpenAuth.IdentityServer.csproj | 2 +- OpenAuth.Identity/Program.cs | 12 +- .../Quickstart/Account/AccountController.cs | 341 +++--------------- .../Quickstart/Account/ExternalController.cs | 251 ------------- .../Quickstart/Consent/ConsentController.cs | 266 -------------- .../Quickstart/Consent/ConsentInputModel.cs | 16 - .../Quickstart/Consent/ConsentOptions.cs | 16 - .../Quickstart/Consent/ConsentViewModel.cs | 19 - .../Consent/ProcessConsentResult.cs | 19 - .../Quickstart/Consent/ScopeViewModel.cs | 16 - .../Device/DeviceAuthorizationInputModel.cs | 13 - .../Device/DeviceAuthorizationViewModel.cs | 14 - .../Quickstart/Device/DeviceController.cs | 243 ------------- .../Diagnostics/DiagnosticsController.cs | 29 -- .../Diagnostics/DiagnosticsViewModel.cs | 32 -- OpenAuth.Identity/Quickstart/Extensions.cs | 25 -- .../Quickstart/Grants/GrantsController.cs | 96 ----- .../Quickstart/Grants/GrantsViewModel.cs | 26 -- .../Quickstart/Home/ErrorViewModel.cs | 22 +- .../Quickstart/Home/HomeController.cs | 32 +- OpenAuth.Identity/Quickstart/TestUsers.cs | 27 -- OpenAuth.Identity/Startup.cs | 104 ++++-- OpenAuth.Identity/Views/Consent/Index.cshtml | 82 ----- OpenAuth.Identity/Views/Device/Success.cshtml | 6 - .../Views/Device/UserCodeCapture.cshtml | 14 - .../Views/Device/UserCodeConfirmation.cshtml | 93 ----- .../Views/Diagnostics/Index.cshtml | 32 -- OpenAuth.Identity/Views/Grants/Index.cshtml | 79 ---- OpenAuth.Identity/Views/Home/Index.cshtml | 22 +- OpenAuth.Identity/Views/Shared/Error.cshtml | 27 +- OpenAuth.Identity/Views/Shared/_Layout.cshtml | 5 +- .../Views/Shared/_ScopeListItem.cshtml | 34 -- OpenAuth.WebApi/OpenAuth.WebApi.csproj | 1 - OpenAuth.WebApi/Startup.cs | 4 +- 42 files changed, 697 insertions(+), 2074 deletions(-) create mode 100644 CheckAndKillPort12796.bat delete mode 100644 OpenAuth.Identity/Config.cs create mode 100644 OpenAuth.Identity/Controllers/AuthorizationController.cs create mode 100644 OpenAuth.Identity/Controllers/TokenController.cs create mode 100644 OpenAuth.Identity/Controllers/UserinfoController.cs delete mode 100644 OpenAuth.Identity/CustomProfileService.cs create mode 100644 OpenAuth.Identity/Handlers/OpenIddictValidationHandlers.cs delete mode 100644 OpenAuth.Identity/Quickstart/Account/ExternalController.cs delete mode 100644 OpenAuth.Identity/Quickstart/Consent/ConsentController.cs delete mode 100644 OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs delete mode 100644 OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs delete mode 100644 OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs delete mode 100644 OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs delete mode 100644 OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs delete mode 100644 OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs delete mode 100644 OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs delete mode 100644 OpenAuth.Identity/Quickstart/Device/DeviceController.cs delete mode 100644 OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs delete mode 100644 OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs delete mode 100644 OpenAuth.Identity/Quickstart/Extensions.cs delete mode 100644 OpenAuth.Identity/Quickstart/Grants/GrantsController.cs delete mode 100644 OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs delete mode 100644 OpenAuth.Identity/Quickstart/TestUsers.cs delete mode 100644 OpenAuth.Identity/Views/Consent/Index.cshtml delete mode 100644 OpenAuth.Identity/Views/Device/Success.cshtml delete mode 100644 OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml delete mode 100644 OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml delete mode 100644 OpenAuth.Identity/Views/Diagnostics/Index.cshtml delete mode 100644 OpenAuth.Identity/Views/Grants/Index.cshtml delete mode 100644 OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml diff --git a/CheckAndKillPort12796.bat b/CheckAndKillPort12796.bat new file mode 100644 index 00000000..ae906355 --- /dev/null +++ b/CheckAndKillPort12796.bat @@ -0,0 +1,25 @@ +@echo off +chcp 936 +setlocal enabledelayedexpansion +echo 正在检查12796端口占用情况... + +rem 查找占用12796端口的进程 +FOR /F "tokens=5" %%i IN ('netstat -ano ^| findstr :12796 ^| findstr LISTENING') DO ( + SET pid=%%i + echo 发现占用12796端口的进程,PID: %%i + echo 正在查询进程信息... + tasklist | findstr %%i + + echo 正在结束进程 %%i... + taskkill /F /PID %%i + + echo 验证端口是否已释放... + netstat -ano | findstr :12796 + goto :END +) + +echo 未发现占用12796端口的进程。 + +:END +echo 操作完成。 +pause \ No newline at end of file diff --git a/Infrastructure/Const/FlowInstanceStatus.cs b/Infrastructure/Const/FlowInstanceStatus.cs index 67d3ca47..2f7d84f1 100644 --- a/Infrastructure/Const/FlowInstanceStatus.cs +++ b/Infrastructure/Const/FlowInstanceStatus.cs @@ -6,7 +6,7 @@ public struct FlowInstanceStatus { /// - /// 鍙洖 + /// 鍙洖/鑽夌ǹ鐘舵 /// public const int Draft = -1; /// diff --git a/OpenAuth.Identity/Config.cs b/OpenAuth.Identity/Config.cs deleted file mode 100644 index cdb46858..00000000 --- a/OpenAuth.Identity/Config.cs +++ /dev/null @@ -1,102 +0,0 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; -using System.Security.Claims; -using IdentityModel; -using IdentityServer4; -using IdentityServer4.Models; - -namespace OpenAuth.IdentityServer -{ - public static class Config - { - public static IEnumerable GetIdentityResources() - { - return new IdentityResource[] - { - new IdentityResources.OpenId(), - new IdentityResources.Profile(), - }; - } - /// - /// API淇℃伅 - /// - /// - public static IEnumerable GetApis() - { - return new[] - { - new ApiResource("openauthapi", "OpenAuth.WebApi") - { - UserClaims = { ClaimTypes.Name, JwtClaimTypes.Name } - } - }; - } - /// - /// 瀹㈡埛绔俊鎭 - /// - /// - public static IEnumerable GetClients(bool isProduction) - { - var host = "http://localhost"; - if (isProduction) - { - host = "http://demo.openauth.net.cn"; //鍒囨崲涓鸿嚜宸辩殑鏈嶅姟鍣ㄤ俊鎭 - } - return new[] - { - new Client - { - ClientId = "OpenAuth.WebApi",//瀹㈡埛绔悕绉 - ClientName = "寮婧愮増webapi璁よ瘉",//瀹㈡埛绔弿杩 - AllowedGrantTypes = GrantTypes.Implicit,//Implicit 鏂瑰紡 - AllowAccessTokensViaBrowser = true,//鏄惁閫氳繃娴忚鍣ㄤ负姝ゅ鎴风浼犺緭璁块棶浠ょ墝 - RedirectUris = - { - $"{host}:52789/swagger/oauth2-redirect.html" - }, - AllowedScopes = { "openauthapi" } - }, - new Client - { - ClientId = "OpenAuth.Mvc", - ClientName = "寮婧愮増mvc璁よ瘉", - AllowedGrantTypes = GrantTypes.Implicit, - - // 鐧诲綍鎴愬姛鍥炶皟澶勭悊鍦板潃锛屽鐞嗗洖璋冭繑鍥炵殑鏁版嵁 - RedirectUris = { $"{host}:1802/signin-oidc" }, - - // where to redirect to after logout - PostLogoutRedirectUris = { $"{host}:1802/signout-callback-oidc" }, - - AllowedScopes = new List - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, - "openauthapi" - } - }, - new Client - { - ClientId = "OpenAuth.Pro",//浼佷笟鐗堝悕绉 - ClientName = "浼佷笟鐗坖s璇锋眰璁よ瘉",//浼佷笟鐗堟弿杩 - AllowedGrantTypes = GrantTypes.Code, - RequirePkce = true, - RequireClientSecret = false, - - RedirectUris = { $"{host}:1803/#/oidc-callback" }, - PostLogoutRedirectUris = { $"{host}:1803" }, - AllowedCorsOrigins = { $"{host}:1803" }, - AllowedScopes = - { - IdentityServerConstants.StandardScopes.OpenId, - IdentityServerConstants.StandardScopes.Profile, //璇锋眰鐢ㄦ埛鐨勫鍚嶏紝鏄电О绛 - "openauthapi" - } - } - }; - } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Controllers/AuthorizationController.cs b/OpenAuth.Identity/Controllers/AuthorizationController.cs new file mode 100644 index 00000000..5eea7343 --- /dev/null +++ b/OpenAuth.Identity/Controllers/AuthorizationController.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Infrastructure; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; +using static OpenIddict.Abstractions.OpenIddictConstants; + +namespace OpenAuth.IdentityServer.Controllers +{ + /// + /// 澶勭悊 OpenID Connect 鎺堟潈璇锋眰 + /// + public class AuthorizationController : Controller + { + private readonly UserManagerApp _userManager; + + public AuthorizationController(UserManagerApp userManager) + { + _userManager = userManager; + } + + /// + /// 澶勭悊鎺堟潈绔偣璇锋眰 /connect/authorize + /// + [HttpGet("~/connect/authorize")] + [HttpPost("~/connect/authorize")] + public async Task Authorize() + { + var request = HttpContext.GetOpenIddictServerRequest() + ?? throw new InvalidOperationException("鏃犳硶鑾峰彇 OpenID Connect 璇锋眰銆"); + + // 灏濊瘯鑾峰彇宸茬櫥褰曠敤鎴 + var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme); + + if (!result.Succeeded) + { + // 鏈櫥褰 鈫 閲嶅畾鍚戝埌鐧诲綍椤甸潰 + return Challenge( + authenticationSchemes: new[] { CookieAuthenticationDefaults.AuthenticationScheme }, + properties: new AuthenticationProperties + { + RedirectUri = Request.PathBase + Request.Path + QueryString.Create( + Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) + }); + } + + // 宸茬櫥褰 鈫 浠 Cookie 涓幏鍙栫敤鎴蜂俊鎭 + var userId = result.Principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; + if (string.IsNullOrEmpty(userId)) + { + return Challenge( + authenticationSchemes: new[] { CookieAuthenticationDefaults.AuthenticationScheme }, + properties: new AuthenticationProperties + { + RedirectUri = Request.PathBase + Request.Path + QueryString.Create( + Request.HasFormContentType ? Request.Form.ToList() : Request.Query.ToList()) + }); + } + + // 鍔犺浇鐢ㄦ埛淇℃伅 + var user = GetUser(userId); + if (user == null) + { + return Forbid(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + // 鏋勫缓 Claims + var claims = new List + { + new Claim(Claims.Subject, user.Id), + new Claim(Claims.Name, user.Name ?? user.Account), + new Claim(ClaimTypes.Name, user.Account), + }; + + var identity = new ClaimsIdentity(claims, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + var principal = new ClaimsPrincipal(identity); + + // 璁剧疆璇锋眰鐨 scopes + principal.SetScopes(request.GetScopes()); + + // 璁剧疆 access_token 鐨 audience锛堝搴 WebApi 鐨 options.Audience锛 + principal.SetResources("openauthapi"); + + // 璁剧疆 Claims 鐩爣锛堝喅瀹氬摢浜 Claims 鍑虹幇鍦 access_token / id_token 涓級 + foreach (var claim in principal.Claims) + { + claim.SetDestinations(GetDestinations(claim, principal)); + } + + // 绛惧彂鎺堟潈鐮 + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + /// + /// 澶勭悊鐧诲嚭绔偣璇锋眰 /connect/logout + /// + [HttpGet("~/connect/logout")] + [HttpPost("~/connect/logout")] + public async Task Logout() + { + // 娓呴櫎鏈湴 Cookie + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + + // 閫氱煡 OpenIddict 鐧诲嚭瀹屾垚 + return SignOut(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + /// + /// 鏍规嵁鐢ㄦ埛ID鑾峰彇鐢ㄦ埛 + /// + private OpenAuth.Repository.Domain.SysUser GetUser(string id) + { + if (id == Define.SYSTEM_USERNAME) + { + return new OpenAuth.Repository.Domain.SysUser + { + Account = Define.SYSTEM_USERNAME, + Id = Define.SYSTEM_USERNAME, + Name = Define.SYSTEM_USERNAME + }; + } + + return _userManager.Get(id); + } + + /// + /// 纭畾 Claim 鐨勭洰鏍囷紙access_token / id_token锛 + /// + private static IEnumerable GetDestinations(Claim claim, ClaimsPrincipal principal) + { + switch (claim.Type) + { + case Claims.Name: + yield return Destinations.AccessToken; + if (principal.HasScope(Scopes.Profile)) + yield return Destinations.IdentityToken; + yield break; + + case ClaimTypes.Name: + yield return Destinations.AccessToken; + if (principal.HasScope(Scopes.Profile)) + yield return Destinations.IdentityToken; + yield break; + + case Claims.Subject: + yield return Destinations.AccessToken; + yield return Destinations.IdentityToken; + yield break; + + default: + yield return Destinations.AccessToken; + yield break; + } + } + } +} diff --git a/OpenAuth.Identity/Controllers/TokenController.cs b/OpenAuth.Identity/Controllers/TokenController.cs new file mode 100644 index 00000000..62a632c7 --- /dev/null +++ b/OpenAuth.Identity/Controllers/TokenController.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Infrastructure; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; +using static OpenIddict.Abstractions.OpenIddictConstants; + +namespace OpenAuth.IdentityServer.Controllers +{ + /// + /// 澶勭悊 OpenID Connect Token 璇锋眰 + /// + public class TokenController : Controller + { + private readonly UserManagerApp _userManager; + + public TokenController(UserManagerApp userManager) + { + _userManager = userManager; + } + + /// + /// 澶勭悊浠ょ墝绔偣璇锋眰 /connect/token + /// + [HttpPost("~/connect/token")] + public async Task Exchange() + { + var request = HttpContext.GetOpenIddictServerRequest() + ?? throw new InvalidOperationException("鏃犳硶鑾峰彇 OpenID Connect 璇锋眰銆"); + + if (request.IsAuthorizationCodeGrantType()) + { + // 浠庢巿鏉冪爜涓繕鍘 ClaimsPrincipal + var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + if (!result.Succeeded) + { + return Forbid( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "鎺堟潈鐮佹棤鏁堟垨宸茶繃鏈熴" + })); + } + + var principal = result.Principal; + + // 鍙夛細琛ュ厖鏈鏂扮殑鐢ㄦ埛淇℃伅鍒 Claims + var userId = principal.GetClaim(Claims.Subject); + if (!string.IsNullOrEmpty(userId)) + { + var user = GetUser(userId); + if (user == null || user.Status != 0) + { + return Forbid( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "鐢ㄦ埛涓嶅瓨鍦ㄦ垨宸茶绂佺敤銆" + })); + } + } + + // 璁剧疆 Claims 鐩爣 + foreach (var claim in principal.Claims) + { + claim.SetDestinations(GetDestinations(claim, principal)); + } + + // 纭繚 access_token 鍖呭惈姝g‘鐨 audience + principal.SetResources("openauthapi"); + + return SignIn(principal, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + } + + throw new InvalidOperationException("涓嶆敮鎸佺殑鎺堟潈绫诲瀷銆"); + } + + /// + /// 鏍规嵁鐢ㄦ埛ID鑾峰彇鐢ㄦ埛 + /// + private OpenAuth.Repository.Domain.SysUser GetUser(string id) + { + if (id == Define.SYSTEM_USERNAME) + { + return new OpenAuth.Repository.Domain.SysUser + { + Account = Define.SYSTEM_USERNAME, + Id = Define.SYSTEM_USERNAME, + Name = Define.SYSTEM_USERNAME, + Status = 0 + }; + } + + return _userManager.Get(id); + } + + /// + /// 纭畾 Claim 鐨勭洰鏍 + /// + private static IEnumerable GetDestinations(Claim claim, ClaimsPrincipal principal) + { + switch (claim.Type) + { + case Claims.Name: + yield return Destinations.AccessToken; + if (principal.HasScope(Scopes.Profile)) + yield return Destinations.IdentityToken; + yield break; + + case ClaimTypes.Name: + yield return Destinations.AccessToken; + if (principal.HasScope(Scopes.Profile)) + yield return Destinations.IdentityToken; + yield break; + + case Claims.Subject: + yield return Destinations.AccessToken; + yield return Destinations.IdentityToken; + yield break; + + default: + yield return Destinations.AccessToken; + yield break; + } + } + } +} diff --git a/OpenAuth.Identity/Controllers/UserinfoController.cs b/OpenAuth.Identity/Controllers/UserinfoController.cs new file mode 100644 index 00000000..45d8bca9 --- /dev/null +++ b/OpenAuth.Identity/Controllers/UserinfoController.cs @@ -0,0 +1,92 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading.Tasks; +using Infrastructure; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Mvc; +using OpenAuth.App; +using OpenIddict.Abstractions; +using OpenIddict.Server.AspNetCore; +using static OpenIddict.Abstractions.OpenIddictConstants; + +namespace OpenAuth.IdentityServer.Controllers +{ + /// + /// 澶勭悊 OpenID Connect Userinfo 璇锋眰 + /// + public class UserinfoController : Controller + { + private readonly UserManagerApp _userManager; + + public UserinfoController(UserManagerApp userManager) + { + _userManager = userManager; + } + + /// + /// 澶勭悊 /connect/userinfo 璇锋眰锛岃繑鍥炲凡璁よ瘉鐢ㄦ埛鐨勪釜浜轰俊鎭 + /// + [HttpGet("~/connect/userinfo")] + [HttpPost("~/connect/userinfo")] + public async Task Userinfo() + { + var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme); + if (!result.Succeeded) + { + return Challenge( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidToken, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "璁块棶浠ょ墝鏃犳晥鎴栧凡杩囨湡銆" + })); + } + + var userId = result.Principal.GetClaim(Claims.Subject); + if (string.IsNullOrEmpty(userId)) + { + return Challenge( + authenticationSchemes: OpenIddictServerAspNetCoreDefaults.AuthenticationScheme, + properties: new AuthenticationProperties(new Dictionary + { + [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidToken, + [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "浠ょ墝涓己灏戠敤鎴锋爣璇嗐" + })); + } + + // 鏋勫缓 userinfo 鍝嶅簲 + var claims = new Dictionary + { + [Claims.Subject] = userId + }; + + // 濡傛灉璇锋眰浜 profile scope锛岃繑鍥炵敤鎴峰悕绉颁俊鎭 + if (result.Principal.HasScope(Scopes.Profile)) + { + var user = GetUser(userId); + if (user != null) + { + claims[Claims.Name] = user.Name ?? user.Account; + claims[Claims.PreferredUsername] = user.Account; + } + } + + return Ok(claims); + } + + private OpenAuth.Repository.Domain.SysUser GetUser(string id) + { + if (id == Define.SYSTEM_USERNAME) + { + return new OpenAuth.Repository.Domain.SysUser + { + Account = Define.SYSTEM_USERNAME, + Id = Define.SYSTEM_USERNAME, + Name = Define.SYSTEM_USERNAME + }; + } + + return _userManager.Get(id); + } + } +} diff --git a/OpenAuth.Identity/CustomProfileService.cs b/OpenAuth.Identity/CustomProfileService.cs deleted file mode 100644 index 6f79967f..00000000 --- a/OpenAuth.Identity/CustomProfileService.cs +++ /dev/null @@ -1,101 +0,0 @@ -锘縰sing System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using IdentityModel; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Test; -using Infrastructure; -using Microsoft.Extensions.Logging; -using OpenAuth.App; -using OpenAuth.Repository.Domain; - -namespace OpenAuth.IdentityServer -{ - public class CustomProfileService : IProfileService - { - /// - /// The logger - /// - protected readonly ILogger Logger; - - protected UserManagerApp UserManager; - - /// - /// Initializes a new instance of the class. - /// - /// The users. - /// The logger. - public CustomProfileService( ILogger logger, UserManagerApp userManager) - { - Logger = logger; - UserManager = userManager; - } - - /// - /// 鍙鏈夊叧鐢ㄦ埛鐨勮韩浠戒俊鎭崟鍏冭璇锋眰锛堜緥濡傚湪浠ょ墝鍒涘缓鏈熼棿鎴栭氳繃鐢ㄦ埛淇℃伅缁堢偣锛夛紝灏变細璋冪敤姝ゆ柟娉 - /// - /// The context. - /// - public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) - { - context.LogProfileRequest(Logger); - - //鍒ゆ柇鏄惁鏈夎姹侰laim淇℃伅 - if (context.RequestedClaimTypes.Any()) - { - var user = GetUserById(context.Subject.GetSubjectId()); - if (user != null) - { - //璋冪敤姝ゆ柟娉曚互鍚庡唴閮ㄤ細杩涜杩囨护锛屽彧灏嗙敤鎴疯姹傜殑Claim鍔犲叆鍒 context.IssuedClaims 闆嗗悎涓 杩欐牱鎴戜滑鐨勮姹傛柟渚胯兘姝e父鑾峰彇鍒版墍闇Claim - var claims = new[] - { - new Claim(ClaimTypes.Name, user.Account), //璇锋眰鐢ㄦ埛鐨勮处鍙凤紝杩欎釜鍙互淇濊瘉User.Identity.Name鏈夊 - new Claim(JwtClaimTypes.Name, user.Name), //璇锋眰鐢ㄦ埛鐨勫鍚 - }; - //杩斿洖apiresource涓畾涔夌殑claims - context.AddRequestedClaims(claims); - } - } - - context.LogIssuedClaims(Logger); - - return Task.CompletedTask; - } - - /// - /// 楠岃瘉鐢ㄦ埛鏄惁鏈夋晥 渚嬪锛歵oken鍒涘缓鎴栬呴獙璇 - /// - /// The context. - /// - public virtual Task IsActiveAsync(IsActiveContext context) - { - Logger.LogDebug("IsActive called from: {caller}", context.Caller); - - var user = GetUserById(context.Subject.GetSubjectId()); - context.IsActive = user?.Status == 0; - return Task.CompletedTask; - } - - private SysUser GetUserById(string id) - { - SysUser sysUser; - if (id == Define.SYSTEM_USERNAME) - { - sysUser = new SysUser - { - Account = Define.SYSTEM_USERNAME, - Id = Define.SYSTEM_USERNAME, - Name = Define.SYSTEM_USERNAME - }; - } - else - { - sysUser = UserManager.Get(id); - } - - return sysUser; - } - } -} diff --git a/OpenAuth.Identity/Handlers/OpenIddictValidationHandlers.cs b/OpenAuth.Identity/Handlers/OpenIddictValidationHandlers.cs new file mode 100644 index 00000000..70a007c1 --- /dev/null +++ b/OpenAuth.Identity/Handlers/OpenIddictValidationHandlers.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using OpenIddict.Server; +using static OpenIddict.Server.OpenIddictServerEvents; + +namespace OpenAuth.IdentityServer.Handlers +{ + /// + /// 闄嶇骇妯″紡涓嬬殑鑷畾涔夋巿鏉冭姹傞獙璇 handler + /// 楠岃瘉 client_id 鍜 redirect_uri 鏄惁鍚堟硶 + /// + public class ValidateAuthorizationRequestHandler : IOpenIddictServerHandler + { + /// + /// 宸叉敞鍐岀殑瀹㈡埛绔厤缃 + /// + private static readonly Dictionary Clients = new(StringComparer.OrdinalIgnoreCase) + { + ["OpenAuth.WebApi"] = new ClientConfig + { + RedirectUris = new[] + { + "http://localhost:52789", + "http://localhost:52789/swagger/oauth2-redirect.html", + "http://localhost:1803", + "http://localhost:1803/callback", + "http://localhost:1803/oidc-callback", + }, + AllowedScopes = new[] { "openid", "profile", "openauthapi" } + }, + ["OpenAuth.Pro"] = new ClientConfig + { + RedirectUris = new[] + { + "http://localhost:1803", + "http://localhost:1803/callback", + "http://localhost:1803/oidc-callback", + "http://localhost:1803/oidc-callback.html", + }, + AllowedScopes = new[] { "openid", "profile", "openauthapi" } + }, + ["OpenAuth.Mvc"] = new ClientConfig + { + RedirectUris = new[] + { + "http://localhost:5001", + "http://localhost:5001/signin-oidc", + }, + AllowedScopes = new[] { "openid", "profile", "openauthapi" } + } + }; + + public ValueTask HandleAsync(ValidateAuthorizationRequestContext context) + { + // 楠岃瘉 client_id + if (string.IsNullOrEmpty(context.ClientId) || !Clients.ContainsKey(context.ClientId)) + { + context.Reject( + error: OpenIddict.Abstractions.OpenIddictConstants.Errors.InvalidClient, + description: "鏈敞鍐岀殑瀹㈡埛绔"); + return default; + } + + var client = Clients[context.ClientId]; + + // 楠岃瘉 redirect_uri锛堝鏋滄彁渚涗簡锛 + if (!string.IsNullOrEmpty(context.RedirectUri)) + { + var isValidRedirectUri = client.RedirectUris.Any(uri => + context.RedirectUri.StartsWith(uri, StringComparison.OrdinalIgnoreCase)); + + if (!isValidRedirectUri) + { + context.Reject( + error: OpenIddict.Abstractions.OpenIddictConstants.Errors.InvalidClient, + description: "鏃犳晥鐨 redirect_uri銆"); + return default; + } + } + + return default; + } + + private class ClientConfig + { + public string[] RedirectUris { get; set; } = Array.Empty(); + public string[] AllowedScopes { get; set; } = Array.Empty(); + } + } + + /// + /// 闄嶇骇妯″紡涓嬬殑鑷畾涔 Token 璇锋眰楠岃瘉 handler + /// + public class ValidateTokenRequestHandler : IOpenIddictServerHandler + { + public ValueTask HandleAsync(ValidateTokenRequestContext context) + { + // 鍦ㄩ檷绾фā寮忎笅锛屾巿鏉冪爜娴佺▼鐨 token 璇锋眰涓嶉渶瑕 client_secret锛堝叕寮瀹㈡埛绔級 + // 鍙渶楠岃瘉 client_id 鏄惁宸叉敞鍐 + if (!string.IsNullOrEmpty(context.ClientId)) + { + var knownClients = new[] { "OpenAuth.WebApi", "OpenAuth.Pro", "OpenAuth.Mvc" }; + if (!knownClients.Contains(context.ClientId, StringComparer.OrdinalIgnoreCase)) + { + context.Reject( + error: OpenIddict.Abstractions.OpenIddictConstants.Errors.InvalidClient, + description: "鏈敞鍐岀殑瀹㈡埛绔"); + return default; + } + } + + return default; + } + } + + /// + /// 闄嶇骇妯″紡涓嬬殑鑷畾涔 Logout 璇锋眰楠岃瘉 handler + /// + public class ValidateLogoutRequestHandler : IOpenIddictServerHandler + { + public ValueTask HandleAsync(ValidateLogoutRequestContext context) + { + // 鍏佽鎵鏈夌櫥鍑鸿姹傦紙鍙牴鎹渶瑕侀獙璇 post_logout_redirect_uri锛 + return default; + } + } +} diff --git a/OpenAuth.Identity/OpenAuth.IdentityServer.csproj b/OpenAuth.Identity/OpenAuth.IdentityServer.csproj index 0505b0f3..2598711a 100644 --- a/OpenAuth.Identity/OpenAuth.IdentityServer.csproj +++ b/OpenAuth.Identity/OpenAuth.IdentityServer.csproj @@ -9,7 +9,7 @@ - + diff --git a/OpenAuth.Identity/Program.cs b/OpenAuth.Identity/Program.cs index c00d1859..72ec5ac0 100644 --- a/OpenAuth.Identity/Program.cs +++ b/OpenAuth.Identity/Program.cs @@ -1,8 +1,4 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; +锘縰sing System; using Autofac.Extensions.DependencyInjection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; @@ -16,7 +12,7 @@ namespace OpenAuth.IdentityServer { public static void Main(string[] args) { - Console.Title = "IdentityServer4"; + Console.Title = "OpenAuth.IdentityServer (OpenIddict)"; CreateWebHostBuilder(args).Build().Run(); } @@ -24,7 +20,7 @@ namespace OpenAuth.IdentityServer public static IHostBuilder CreateWebHostBuilder(string[] args) { return Host.CreateDefaultBuilder(args) - .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //灏嗛粯璁erviceProviderFactory鎸囧畾涓篈utofacServiceProviderFactory + .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseUrls("http://*:12796").UseStartup(); @@ -40,4 +36,4 @@ namespace OpenAuth.IdentityServer }); } } -} \ No newline at end of file +} diff --git a/OpenAuth.Identity/Quickstart/Account/AccountController.cs b/OpenAuth.Identity/Quickstart/Account/AccountController.cs index 073d20e2..7f2c483e 100644 --- a/OpenAuth.Identity/Quickstart/Account/AccountController.cs +++ b/OpenAuth.Identity/Quickstart/Account/AccountController.cs @@ -1,20 +1,10 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Linq; +using System.Collections.Generic; +using System.Security.Claims; using System.Threading.Tasks; -using IdentityModel; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Stores; using Infrastructure; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using OpenAuth.App; using OpenAuth.Repository.Domain; @@ -22,87 +12,48 @@ using OpenAuth.Repository.Domain; namespace OpenAuth.IdentityServer.Quickstart.Account { /// - /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. - /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! - /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval + /// 澶勭悊鐢ㄦ埛鐧诲綍/鐧诲嚭娴佺▼ /// - [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly UserManagerApp _userManager; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IAuthenticationSchemeProvider _schemeProvider; - private readonly IEventService _events; - public AccountController( - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IAuthenticationSchemeProvider schemeProvider, - IEventService events, UserManagerApp userManager) + public AccountController(UserManagerApp userManager) { - - _interaction = interaction; - _clientStore = clientStore; - _schemeProvider = schemeProvider; - _events = events; _userManager = userManager; } /// - /// Entry point into the login workflow + /// 鏄剧ず鐧诲綍椤甸潰 /// [HttpGet] - public async Task Login(string returnUrl) + public IActionResult Login(string returnUrl) { - // build a model so we know what to show on the login page - var vm = await BuildLoginViewModelAsync(returnUrl); - - if (vm.IsExternalLoginOnly) + var vm = new LoginViewModel { - // we only have one option for logging in and it's an external provider - return RedirectToAction("Challenge", "External", new { provider = vm.ExternalLoginScheme, returnUrl }); - } - + ReturnUrl = returnUrl, + EnableLocalLogin = true, + AllowRememberLogin = AccountOptions.AllowRememberLogin + }; return View(vm); } /// - /// Handle postback from username/password login + /// 澶勭悊鐧诲綍琛ㄥ崟鎻愪氦 /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model, string button) { - // check if we are in the context of an authorization request - var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - - // the user clicked the "cancel" button if (button != "login") { - if (context != null) + // 鐢ㄦ埛鍙栨秷鐧诲綍 + if (!string.IsNullOrEmpty(model.ReturnUrl)) { - // if the user cancels, send a result back into IdentityServer as if they - // denied the consent (even if this client does not require consent). - // this will send back an access denied OIDC error response to the client. - await _interaction.GrantConsentAsync(context, ConsentResponse.Denied); - - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null - if (await _clientStore.IsPkceClientAsync(context.ClientId)) - { - // if the client is PKCE then we assume it's native, so this change in how to - // return the response is for better UX for the end user. - return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); - } - return Redirect(model.ReturnUrl); } - else - { - // since we don't have a valid context, then we just go back to the home page - return Redirect("~/"); - } + return Redirect("~/"); } if (ModelState.IsValid) @@ -114,7 +65,9 @@ namespace OpenAuth.IdentityServer.Quickstart.Account { Account = Define.SYSTEM_USERNAME, Password = Define.SYSTEM_USERPWD, - Id = Define.SYSTEM_USERNAME + Id = Define.SYSTEM_USERNAME, + Name = Define.SYSTEM_USERNAME, + Status = 0 }; } else @@ -122,123 +75,63 @@ namespace OpenAuth.IdentityServer.Quickstart.Account sysUser = _userManager.GetByAccount(model.Username); } - if (sysUser != null &&(sysUser.Password ==model.Password)) + if (sysUser != null && sysUser.Password == model.Password) { - if (sysUser.Status != 0) //鍒ゆ柇鐢ㄦ埛鐘舵 + if (sysUser.Status != 0) { - await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid user status")); - ModelState.AddModelError(string.Empty, "user.status must be 0"); - var err = await BuildLoginViewModelAsync(model); - return View(err); + ModelState.AddModelError(string.Empty, "鐢ㄦ埛鐘舵佸紓甯革紝鏃犳硶鐧诲綍"); + return View(BuildLoginViewModel(model)); } - await _events.RaiseAsync(new UserLoginSuccessEvent(sysUser.Account, sysUser.Id, sysUser.Account)); - - // only set explicit expiration here if user chooses "remember me". - // otherwise we rely upon expiration configured in cookie middleware. - AuthenticationProperties props = null; - if (AccountOptions.AllowRememberLogin && model.RememberLogin) + // 鏋勫缓 Cookie Claims + var claims = new List { - props = new AuthenticationProperties - { - IsPersistent = true, - ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) - }; + new Claim(ClaimTypes.NameIdentifier, sysUser.Id), + new Claim(ClaimTypes.Name, sysUser.Account), }; - // issue authentication cookie with subject ID and username - await HttpContext.SignInAsync(sysUser.Id, sysUser.Account, props); + var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); + var principal = new ClaimsPrincipal(identity); - if (context != null) + var props = new AuthenticationProperties { - if (await _clientStore.IsPkceClientAsync(context.ClientId)) - { - // if the client is PKCE then we assume it's native, so this change in how to - // return the response is for better UX for the end user. - return View("Redirect", new RedirectViewModel { RedirectUrl = model.ReturnUrl }); - } + IsPersistent = model.RememberLogin, + }; - // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null + // 鍐欏叆 Cookie + await HttpContext.SignInAsync( + CookieAuthenticationDefaults.AuthenticationScheme, + principal, + props); + + // 鐧诲綍鎴愬姛鍚庨噸瀹氬悜 + if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl)) + { return Redirect(model.ReturnUrl); } - // request for a local page - if (Url.IsLocalUrl(model.ReturnUrl)) - { - return Redirect(model.ReturnUrl); - } - else if (string.IsNullOrEmpty(model.ReturnUrl)) - { - return Redirect("~/"); - } - else - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } + return Redirect("~/"); } - await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId:context?.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } - // something went wrong, show form with error - var vm = await BuildLoginViewModelAsync(model); - return View(vm); + // 鐧诲綍澶辫触锛岄噸鏂版樉绀鸿〃鍗 + return View(BuildLoginViewModel(model)); } - /// - /// Show logout page + /// 鏄剧ず鐧诲嚭椤甸潰 /// [HttpGet] public async Task Logout(string logoutId) { - // build a model so the logout page knows what to display - var vm = await BuildLogoutViewModelAsync(logoutId); - - if (vm.ShowLogoutPrompt == false) + await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + return View("LoggedOut", new LoggedOutViewModel { - // if the request for logout was properly authenticated from IdentityServer, then - // we don't need to show the prompt and can just log the user out directly. - return await Logout(vm); - } - - return View(vm); - } - - /// - /// Handle logout page postback - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Logout(LogoutInputModel model) - { - // build a model so the logged out page knows what to display - var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); - - if (User?.Identity.IsAuthenticated == true) - { - // delete local authentication cookie - await HttpContext.SignOutAsync(); - - // raise the logout event - await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); - } - - // check if we need to trigger sign-out at an upstream identity provider - if (vm.TriggerExternalSignout) - { - // build a return URL so the upstream provider will redirect back - // to us after the user has logged out. this allows us to then - // complete our single sign-out processing. - string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); - - // this triggers a redirect to the external provider for sign-out - return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); - } - - return View("LoggedOut", vm); + AutomaticRedirectAfterSignOut = true, + PostLogoutRedirectUri = "/" + }); } [HttpGet] @@ -247,138 +140,16 @@ namespace OpenAuth.IdentityServer.Quickstart.Account return View(); } - - /*****************************************/ - /* helper APIs for the AccountController */ - /*****************************************/ - private async Task BuildLoginViewModelAsync(string returnUrl) + private LoginViewModel BuildLoginViewModel(LoginInputModel model) { - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) - { - var local = context.IdP == IdentityServer4.IdentityServerConstants.LocalIdentityProvider; - - // this is meant to short circuit the UI and only trigger the one external IdP - var vm = new LoginViewModel - { - EnableLocalLogin = local, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - }; - - if (!local) - { - vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; - } - - return vm; - } - - var schemes = await _schemeProvider.GetAllSchemesAsync(); - - var providers = schemes - .Where(x => x.DisplayName != null || - (x.Name.Equals(AccountOptions.WindowsAuthenticationSchemeName, StringComparison.OrdinalIgnoreCase)) - ) - .Select(x => new ExternalProvider - { - DisplayName = x.DisplayName, - AuthenticationScheme = x.Name - }).ToList(); - - var allowLocal = true; - if (context?.ClientId != null) - { - var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId); - if (client != null) - { - allowLocal = client.EnableLocalLogin; - - if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) - { - providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); - } - } - } - return new LoginViewModel { - AllowRememberLogin = AccountOptions.AllowRememberLogin, - EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, - ReturnUrl = returnUrl, - Username = context?.LoginHint, - ExternalProviders = providers.ToArray() + Username = model.Username, + RememberLogin = model.RememberLogin, + ReturnUrl = model.ReturnUrl, + EnableLocalLogin = true, + AllowRememberLogin = AccountOptions.AllowRememberLogin }; } - - private async Task BuildLoginViewModelAsync(LoginInputModel model) - { - var vm = await BuildLoginViewModelAsync(model.ReturnUrl); - vm.Username = model.Username; - vm.RememberLogin = model.RememberLogin; - return vm; - } - - private async Task BuildLogoutViewModelAsync(string logoutId) - { - var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; - - if (User?.Identity.IsAuthenticated != true) - { - // if the user is not authenticated, then just show logged out page - vm.ShowLogoutPrompt = false; - return vm; - } - - var context = await _interaction.GetLogoutContextAsync(logoutId); - if (context?.ShowSignoutPrompt == false) - { - // it's safe to automatically sign-out - vm.ShowLogoutPrompt = false; - return vm; - } - - // show the logout prompt. this prevents attacks where the user - // is automatically signed out by another malicious web page. - return vm; - } - - private async Task BuildLoggedOutViewModelAsync(string logoutId) - { - // get context information (client name, post logout redirect URI and iframe for federated signout) - var logout = await _interaction.GetLogoutContextAsync(logoutId); - - var vm = new LoggedOutViewModel - { - AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, - PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, - ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, - SignOutIframeUrl = logout?.SignOutIFrameUrl, - LogoutId = logoutId - }; - - if (User?.Identity.IsAuthenticated == true) - { - var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; - if (idp != null && idp != IdentityServer4.IdentityServerConstants.LocalIdentityProvider) - { - var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); - if (providerSupportsSignout) - { - if (vm.LogoutId == null) - { - // if there's no current logout context, we need to create one - // this captures necessary info from the current logged in user - // before we signout and redirect away to the external IdP for signout - vm.LogoutId = await _interaction.CreateLogoutContextAsync(); - } - - vm.ExternalAuthenticationScheme = idp; - } - } - } - - return vm; - } } } diff --git a/OpenAuth.Identity/Quickstart/Account/ExternalController.cs b/OpenAuth.Identity/Quickstart/Account/ExternalController.cs deleted file mode 100644 index e3cd6963..00000000 --- a/OpenAuth.Identity/Quickstart/Account/ExternalController.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading.Tasks; -using IdentityModel; -using IdentityServer4.Events; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using IdentityServer4.Test; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; - -namespace OpenAuth.IdentityServer.Quickstart.Account -{ - [SecurityHeaders] - [AllowAnonymous] - public class ExternalController : Controller - { - private readonly TestUserStore _users; - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly ILogger _logger; - private readonly IEventService _events; - - public ExternalController( - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IEventService events, - ILogger logger, - TestUserStore users = null) - { - // if the TestUserStore is not in DI, then we'll just use the global users collection - // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) - _users = users ?? new TestUserStore(TestUsers.Users); - - _interaction = interaction; - _clientStore = clientStore; - _logger = logger; - _events = events; - } - - /// - /// initiate roundtrip to external authentication provider - /// - [HttpGet] - public async Task Challenge(string provider, string returnUrl) - { - if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; - - // validate returnUrl - either it is a valid OIDC URL or back to a local page - if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) - { - // user might have clicked on a malicious link - should be logged - throw new Exception("invalid return URL"); - } - - if (AccountOptions.WindowsAuthenticationSchemeName == provider) - { - // windows authentication needs special handling - return await ProcessWindowsLoginAsync(returnUrl); - } - else - { - // start challenge and roundtrip the return URL and scheme - var props = new AuthenticationProperties - { - RedirectUri = Url.Action(nameof(Callback)), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", provider }, - } - }; - - return Challenge(props, provider); - } - } - - /// - /// Post processing of external authentication - /// - [HttpGet] - public async Task Callback() - { - // read external identity from the temporary cookie - var result = await HttpContext.AuthenticateAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); - if (result?.Succeeded != true) - { - throw new Exception("External authentication error"); - } - - if (_logger.IsEnabled(LogLevel.Debug)) - { - var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); - _logger.LogDebug("External claims: {@claims}", externalClaims); - } - - // lookup our user and external provider info - var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); - if (user == null) - { - // this might be where you might initiate a custom workflow for user registration - // in this sample we don't show how that would be done, as our sample implementation - // simply auto-provisions new external user - user = AutoProvisionUser(provider, providerUserId, claims); - } - - // this allows us to collect any additonal claims or properties - // for the specific prtotocols used and store them in the local auth cookie. - // this is typically used to store data needed for signout from those protocols. - var additionalLocalClaims = new List(); - var localSignInProps = new AuthenticationProperties(); - ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); - ProcessLoginCallbackForWsFed(result, additionalLocalClaims, localSignInProps); - ProcessLoginCallbackForSaml2p(result, additionalLocalClaims, localSignInProps); - - // issue authentication cookie for user - await HttpContext.SignInAsync(user.SubjectId, user.Username, provider, localSignInProps, additionalLocalClaims.ToArray()); - - // delete temporary cookie used during external authentication - await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); - - // retrieve return URL - var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; - - // check if external login is in the context of an OIDC request - var context = await _interaction.GetAuthorizationContextAsync(returnUrl); - await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.ClientId)); - - if (context != null) - { - if (await _clientStore.IsPkceClientAsync(context.ClientId)) - { - // if the client is PKCE then we assume it's native, so this change in how to - // return the response is for better UX for the end user. - return View("Redirect", new RedirectViewModel { RedirectUrl = returnUrl }); - } - } - - return Redirect(returnUrl); - } - - private async Task ProcessWindowsLoginAsync(string returnUrl) - { - // see if windows auth has already been requested and succeeded - var result = await HttpContext.AuthenticateAsync(AccountOptions.WindowsAuthenticationSchemeName); - if (result?.Principal is WindowsPrincipal wp) - { - // we will issue the external cookie and then redirect the - // user back to the external callback, in essence, treating windows - // auth the same as any other external authentication mechanism - var props = new AuthenticationProperties() - { - RedirectUri = Url.Action("Callback"), - Items = - { - { "returnUrl", returnUrl }, - { "scheme", AccountOptions.WindowsAuthenticationSchemeName }, - } - }; - - var id = new ClaimsIdentity(AccountOptions.WindowsAuthenticationSchemeName); - id.AddClaim(new Claim(JwtClaimTypes.Subject, wp.Identity.Name)); - id.AddClaim(new Claim(JwtClaimTypes.Name, wp.Identity.Name)); - - // add the groups as claims -- be careful if the number of groups is too large - if (AccountOptions.IncludeWindowsGroups) - { - var wi = wp.Identity as WindowsIdentity; - var groups = wi.Groups.Translate(typeof(NTAccount)); - var roles = groups.Select(x => new Claim(JwtClaimTypes.Role, x.Value)); - id.AddClaims(roles); - } - - await HttpContext.SignInAsync( - IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme, - new ClaimsPrincipal(id), - props); - return Redirect(props.RedirectUri); - } - else - { - // trigger windows auth - // since windows auth don't support the redirect uri, - // this URL is re-triggered when we call challenge - return Challenge(AccountOptions.WindowsAuthenticationSchemeName); - } - } - - private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) - { - var externalUser = result.Principal; - - // try to determine the unique id of the external user (issued by the provider) - // the most common claim type for that are the sub claim and the NameIdentifier - // depending on the external provider, some other claim type might be used - var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? - externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? - throw new Exception("Unknown userid"); - - // remove the user id claim so we don't include it as an extra claim if/when we provision the user - var claims = externalUser.Claims.ToList(); - claims.Remove(userIdClaim); - - var provider = result.Properties.Items["scheme"]; - var providerUserId = userIdClaim.Value; - - // find external user - var user = _users.FindByExternalProvider(provider, providerUserId); - - return (user, provider, providerUserId, claims); - } - - private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) - { - var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); - return user; - } - - private void ProcessLoginCallbackForOidc(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - // if the external system sent a session id claim, copy it over - // so we can use it for single sign-out - var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); - if (sid != null) - { - localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); - } - - // if the external provider issued an id_token, we'll keep it for signout - var id_token = externalResult.Properties.GetTokenValue("id_token"); - if (id_token != null) - { - localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = id_token } }); - } - } - - private void ProcessLoginCallbackForWsFed(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - } - - private void ProcessLoginCallbackForSaml2p(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) - { - } - } -} diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs deleted file mode 100644 index 95343f8d..00000000 --- a/OpenAuth.Identity/Quickstart/Consent/ConsentController.cs +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Linq; -using System.Threading.Tasks; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using OpenAuth.IdentityServer.Quickstart.Account; - -namespace OpenAuth.IdentityServer.Quickstart.Consent -{ - /// - /// This controller processes the consent UI - /// - [SecurityHeaders] - [Authorize] - public class ConsentController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IResourceStore _resourceStore; - private readonly IEventService _events; - private readonly ILogger _logger; - - public ConsentController( - IIdentityServerInteractionService interaction, - IClientStore clientStore, - IResourceStore resourceStore, - IEventService events, - ILogger logger) - { - _interaction = interaction; - _clientStore = clientStore; - _resourceStore = resourceStore; - _events = events; - _logger = logger; - } - - /// - /// Shows the consent screen - /// - /// - /// - [HttpGet] - public async Task Index(string returnUrl) - { - var vm = await BuildViewModelAsync(returnUrl); - if (vm != null) - { - return View("Index", vm); - } - - return View("Error"); - } - - /// - /// Handles the consent screen postback - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Index(ConsentInputModel model) - { - var result = await ProcessConsent(model); - - if (result.IsRedirect) - { - if (await _clientStore.IsPkceClientAsync(result.ClientId)) - { - // if the client is PKCE then we assume it's native, so this change in how to - // return the response is for better UX for the end user. - return View("Redirect", new RedirectViewModel { RedirectUrl = result.RedirectUri }); - } - - return Redirect(result.RedirectUri); - } - - if (result.HasValidationError) - { - ModelState.AddModelError(string.Empty, result.ValidationError); - } - - if (result.ShowView) - { - return View("Index", result.ViewModel); - } - - return View("Error"); - } - - /*****************************************/ - /* helper APIs for the ConsentController */ - /*****************************************/ - private async Task ProcessConsent(ConsentInputModel model) - { - var result = new ProcessConsentResult(); - - // validate return url is still valid - var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); - if (request == null) return result; - - ConsentResponse grantedConsent = null; - - // user clicked 'no' - send back the standard 'access_denied' response - if (model?.Button == "no") - { - grantedConsent = ConsentResponse.Denied; - - // emit event - await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); - } - // user clicked 'yes' - validate the data - else if (model?.Button == "yes") - { - // if the user consented to some scope, build the response model - if (model.ScopesConsented != null && model.ScopesConsented.Any()) - { - var scopes = model.ScopesConsented; - if (ConsentOptions.EnableOfflineAccess == false) - { - scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); - } - - grantedConsent = new ConsentResponse - { - RememberConsent = model.RememberConsent, - ScopesConsented = scopes.ToArray() - }; - - // emit event - await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent)); - } - else - { - result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; - } - } - else - { - result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; - } - - if (grantedConsent != null) - { - // communicate outcome of consent back to identityserver - await _interaction.GrantConsentAsync(request, grantedConsent); - - // indicate that's it ok to redirect back to authorization endpoint - result.RedirectUri = model.ReturnUrl; - result.ClientId = request.ClientId; - } - else - { - // we need to redisplay the consent UI - result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); - } - - return result; - } - - private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) - { - var request = await _interaction.GetAuthorizationContextAsync(returnUrl); - if (request != null) - { - var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); - if (client != null) - { - var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); - if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) - { - return CreateConsentViewModel(model, returnUrl, request, client, resources); - } - else - { - _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); - } - } - else - { - _logger.LogError("Invalid client id: {0}", request.ClientId); - } - } - else - { - _logger.LogError("No consent request matching request: {0}", returnUrl); - } - - return null; - } - - private ConsentViewModel CreateConsentViewModel( - ConsentInputModel model, string returnUrl, - AuthorizationRequest request, - Client client, Resources resources) - { - var vm = new ConsentViewModel - { - RememberConsent = model?.RememberConsent ?? true, - ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), - - ReturnUrl = returnUrl, - - ClientName = client.ClientName ?? client.ClientId, - ClientUrl = client.ClientUri, - ClientLogoUrl = client.LogoUri, - AllowRememberConsent = client.AllowRememberConsent - }; - - vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) - { - vm.ResourceScopes = vm.ResourceScopes.Union(new ScopeViewModel[] { - GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) - }); - } - - return vm; - } - - private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) - { - return new ScopeViewModel - { - Name = identity.Name, - DisplayName = identity.DisplayName, - Description = identity.Description, - Emphasize = identity.Emphasize, - Required = identity.Required, - Checked = check || identity.Required - }; - } - - public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) - { - return new ScopeViewModel - { - Name = scope.Name, - DisplayName = scope.DisplayName, - Description = scope.Description, - Emphasize = scope.Emphasize, - Required = scope.Required, - Checked = check || scope.Required - }; - } - - private ScopeViewModel GetOfflineAccessScope(bool check) - { - return new ScopeViewModel - { - Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, - DisplayName = ConsentOptions.OfflineAccessDisplayName, - Description = ConsentOptions.OfflineAccessDescription, - Emphasize = true, - Checked = check - }; - } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs deleted file mode 100644 index 7aeb4c44..00000000 --- a/OpenAuth.Identity/Quickstart/Consent/ConsentInputModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace OpenAuth.IdentityServer.Quickstart.Consent -{ - public class ConsentInputModel - { - public string Button { get; set; } - public IEnumerable ScopesConsented { get; set; } - public bool RememberConsent { get; set; } - public string ReturnUrl { get; set; } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs deleted file mode 100644 index d4727259..00000000 --- a/OpenAuth.Identity/Quickstart/Consent/ConsentOptions.cs +++ /dev/null @@ -1,16 +0,0 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace OpenAuth.IdentityServer.Quickstart.Consent -{ - public class ConsentOptions - { - public static bool EnableOfflineAccess = true; - public static string OfflineAccessDisplayName = "Offline Access"; - public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; - - public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; - public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; - } -} diff --git a/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs b/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs deleted file mode 100644 index 77a90441..00000000 --- a/OpenAuth.Identity/Quickstart/Consent/ConsentViewModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; - -namespace OpenAuth.IdentityServer.Quickstart.Consent -{ - public class ConsentViewModel : ConsentInputModel - { - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public bool AllowRememberConsent { get; set; } - - public IEnumerable IdentityScopes { get; set; } - public IEnumerable ResourceScopes { get; set; } - } -} diff --git a/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs b/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs deleted file mode 100644 index e3b3f001..00000000 --- a/OpenAuth.Identity/Quickstart/Consent/ProcessConsentResult.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace OpenAuth.IdentityServer.Quickstart.Consent -{ - public class ProcessConsentResult - { - public bool IsRedirect => RedirectUri != null; - public string RedirectUri { get; set; } - public string ClientId { get; set; } - - public bool ShowView => ViewModel != null; - public ConsentViewModel ViewModel { get; set; } - - public bool HasValidationError => ValidationError != null; - public string ValidationError { get; set; } - } -} diff --git a/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs b/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs deleted file mode 100644 index a75451af..00000000 --- a/OpenAuth.Identity/Quickstart/Consent/ScopeViewModel.cs +++ /dev/null @@ -1,16 +0,0 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -namespace OpenAuth.IdentityServer.Quickstart.Consent -{ - public class ScopeViewModel - { - public string Name { get; set; } - public string DisplayName { get; set; } - public string Description { get; set; } - public bool Emphasize { get; set; } - public bool Required { get; set; } - public bool Checked { get; set; } - } -} diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs deleted file mode 100644 index c78b6a2a..00000000 --- a/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationInputModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using OpenAuth.IdentityServer.Quickstart.Consent; - -namespace OpenAuth.IdentityServer.Quickstart.Device -{ - public class DeviceAuthorizationInputModel : ConsentInputModel - { - public string UserCode { get; set; } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs b/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs deleted file mode 100644 index a1b2bb1c..00000000 --- a/OpenAuth.Identity/Quickstart/Device/DeviceAuthorizationViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using OpenAuth.IdentityServer.Quickstart.Consent; - -namespace OpenAuth.IdentityServer.Quickstart.Device -{ - public class DeviceAuthorizationViewModel : ConsentViewModel - { - public string UserCode { get; set; } - public bool ConfirmUserCode { get; set; } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Device/DeviceController.cs b/OpenAuth.Identity/Quickstart/Device/DeviceController.cs deleted file mode 100644 index 15fe30c0..00000000 --- a/OpenAuth.Identity/Quickstart/Device/DeviceController.cs +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Linq; -using System.Threading.Tasks; -using IdentityServer4.Configuration; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Models; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using OpenAuth.IdentityServer.Quickstart.Consent; - -namespace OpenAuth.IdentityServer.Quickstart.Device -{ - [Authorize] - [SecurityHeaders] - public class DeviceController : Controller - { - private readonly IDeviceFlowInteractionService _interaction; - private readonly IClientStore _clientStore; - private readonly IResourceStore _resourceStore; - private readonly IEventService _events; - private readonly IOptions _options; - private readonly ILogger _logger; - - public DeviceController( - IDeviceFlowInteractionService interaction, - IClientStore clientStore, - IResourceStore resourceStore, - IEventService eventService, - IOptions options, - ILogger logger) - { - _interaction = interaction; - _clientStore = clientStore; - _resourceStore = resourceStore; - _events = eventService; - _options = options; - _logger = logger; - } - - [HttpGet] - public async Task Index() - { - string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; - string userCode = Request.Query[userCodeParamName]; - if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); - - var vm = await BuildViewModelAsync(userCode); - if (vm == null) return View("Error"); - - vm.ConfirmUserCode = true; - return View("UserCodeConfirmation", vm); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task UserCodeCapture(string userCode) - { - var vm = await BuildViewModelAsync(userCode); - if (vm == null) return View("Error"); - - return View("UserCodeConfirmation", vm); - } - - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Callback(DeviceAuthorizationInputModel model) - { - if (model == null) throw new ArgumentNullException(nameof(model)); - - var result = await ProcessConsent(model); - if (result.HasValidationError) return View("Error"); - - return View("Success"); - } - - private async Task ProcessConsent(DeviceAuthorizationInputModel model) - { - var result = new ProcessConsentResult(); - - var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); - if (request == null) return result; - - ConsentResponse grantedConsent = null; - - // user clicked 'no' - send back the standard 'access_denied' response - if (model.Button == "no") - { - grantedConsent = ConsentResponse.Denied; - - // emit event - await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested)); - } - // user clicked 'yes' - validate the data - else if (model.Button == "yes") - { - // if the user consented to some scope, build the response model - if (model.ScopesConsented != null && model.ScopesConsented.Any()) - { - var scopes = model.ScopesConsented; - if (ConsentOptions.EnableOfflineAccess == false) - { - scopes = scopes.Where(x => x != IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess); - } - - grantedConsent = new ConsentResponse - { - RememberConsent = model.RememberConsent, - ScopesConsented = scopes.ToArray() - }; - - // emit event - await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.ClientId, request.ScopesRequested, grantedConsent.ScopesConsented, grantedConsent.RememberConsent)); - } - else - { - result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; - } - } - else - { - result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; - } - - if (grantedConsent != null) - { - // communicate outcome of consent back to identityserver - await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); - - // indicate that's it ok to redirect back to authorization endpoint - result.RedirectUri = model.ReturnUrl; - result.ClientId = request.ClientId; - } - else - { - // we need to redisplay the consent UI - result.ViewModel = await BuildViewModelAsync(model.UserCode, model); - } - - return result; - } - - private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) - { - var request = await _interaction.GetAuthorizationContextAsync(userCode); - if (request != null) - { - var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId); - if (client != null) - { - var resources = await _resourceStore.FindEnabledResourcesByScopeAsync(request.ScopesRequested); - if (resources != null && (resources.IdentityResources.Any() || resources.ApiResources.Any())) - { - return CreateConsentViewModel(userCode, model, client, resources); - } - else - { - _logger.LogError("No scopes matching: {0}", request.ScopesRequested.Aggregate((x, y) => x + ", " + y)); - } - } - else - { - _logger.LogError("Invalid client id: {0}", request.ClientId); - } - } - - return null; - } - - private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, Client client, Resources resources) - { - var vm = new DeviceAuthorizationViewModel - { - UserCode = userCode, - - RememberConsent = model?.RememberConsent ?? true, - ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), - - ClientName = client.ClientName ?? client.ClientId, - ClientUrl = client.ClientUri, - ClientLogoUrl = client.LogoUri, - AllowRememberConsent = client.AllowRememberConsent - }; - - vm.IdentityScopes = resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - vm.ResourceScopes = resources.ApiResources.SelectMany(x => x.Scopes).Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); - if (ConsentOptions.EnableOfflineAccess && resources.OfflineAccess) - { - vm.ResourceScopes = vm.ResourceScopes.Union(new[] - { - GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null) - }); - } - - return vm; - } - - private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) - { - return new ScopeViewModel - { - Name = identity.Name, - DisplayName = identity.DisplayName, - Description = identity.Description, - Emphasize = identity.Emphasize, - Required = identity.Required, - Checked = check || identity.Required - }; - } - - public ScopeViewModel CreateScopeViewModel(Scope scope, bool check) - { - return new ScopeViewModel - { - Name = scope.Name, - DisplayName = scope.DisplayName, - Description = scope.Description, - Emphasize = scope.Emphasize, - Required = scope.Required, - Checked = check || scope.Required - }; - } - private ScopeViewModel GetOfflineAccessScope(bool check) - { - return new ScopeViewModel - { - Name = IdentityServer4.IdentityServerConstants.StandardScopes.OfflineAccess, - DisplayName = ConsentOptions.OfflineAccessDisplayName, - Description = ConsentOptions.OfflineAccessDescription, - Emphasize = true, - Checked = check - }; - } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs deleted file mode 100644 index e4a9c456..00000000 --- a/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsController.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace OpenAuth.IdentityServer.Quickstart.Diagnostics -{ - [SecurityHeaders] - [Authorize] - public class DiagnosticsController : Controller - { - public async Task Index() - { - var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; - if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) - { - return NotFound(); - } - - var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); - return View(model); - } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs b/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs deleted file mode 100644 index e24c242e..00000000 --- a/OpenAuth.Identity/Quickstart/Diagnostics/DiagnosticsViewModel.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; -using System.Text; -using IdentityModel; -using Microsoft.AspNetCore.Authentication; -using Newtonsoft.Json; - -namespace OpenAuth.IdentityServer.Quickstart.Diagnostics -{ - public class DiagnosticsViewModel - { - public DiagnosticsViewModel(AuthenticateResult result) - { - AuthenticateResult = result; - - if (result.Properties.Items.ContainsKey("client_list")) - { - var encoded = result.Properties.Items["client_list"]; - var bytes = Base64Url.Decode(encoded); - var value = Encoding.UTF8.GetString(bytes); - - Clients = JsonConvert.DeserializeObject(value); - } - } - - public AuthenticateResult AuthenticateResult { get; } - public IEnumerable Clients { get; } = new List(); - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Extensions.cs b/OpenAuth.Identity/Quickstart/Extensions.cs deleted file mode 100644 index 560e4b9f..00000000 --- a/OpenAuth.Identity/Quickstart/Extensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using IdentityServer4.Stores; - -namespace OpenAuth.IdentityServer.Quickstart -{ - public static class Extensions - { - /// - /// Determines whether the client is configured to use PKCE. - /// - /// The store. - /// The client identifier. - /// - public static async Task IsPkceClientAsync(this IClientStore store, string client_id) - { - if (!string.IsNullOrWhiteSpace(client_id)) - { - var client = await store.FindEnabledClientByIdAsync(client_id); - return client?.RequirePkce == true; - } - - return false; - } - } -} diff --git a/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs b/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs deleted file mode 100644 index c1d26954..00000000 --- a/OpenAuth.Identity/Quickstart/Grants/GrantsController.cs +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using IdentityServer4.Events; -using IdentityServer4.Extensions; -using IdentityServer4.Services; -using IdentityServer4.Stores; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace OpenAuth.IdentityServer.Quickstart.Grants -{ - /// - /// This sample controller allows a user to revoke grants given to clients - /// - [SecurityHeaders] - [Authorize] - public class GrantsController : Controller - { - private readonly IIdentityServerInteractionService _interaction; - private readonly IClientStore _clients; - private readonly IResourceStore _resources; - private readonly IEventService _events; - - public GrantsController(IIdentityServerInteractionService interaction, - IClientStore clients, - IResourceStore resources, - IEventService events) - { - _interaction = interaction; - _clients = clients; - _resources = resources; - _events = events; - } - - /// - /// Show list of grants - /// - [HttpGet] - public async Task Index() - { - return View("Index", await BuildViewModelAsync()); - } - - /// - /// Handle postback to revoke a client - /// - [HttpPost] - [ValidateAntiForgeryToken] - public async Task Revoke(string clientId) - { - await _interaction.RevokeUserConsentAsync(clientId); - await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); - - return RedirectToAction("Index"); - } - - private async Task BuildViewModelAsync() - { - var grants = await _interaction.GetAllUserConsentsAsync(); - - var list = new List(); - foreach(var grant in grants) - { - var client = await _clients.FindClientByIdAsync(grant.ClientId); - if (client != null) - { - var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); - - var item = new GrantViewModel() - { - ClientId = client.ClientId, - ClientName = client.ClientName ?? client.ClientId, - ClientLogoUrl = client.LogoUri, - ClientUrl = client.ClientUri, - Created = grant.CreationTime, - Expires = grant.Expiration, - IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), - ApiGrantNames = resources.ApiResources.Select(x => x.DisplayName ?? x.Name).ToArray() - }; - - list.Add(item); - } - } - - return new GrantsViewModel - { - Grants = list - }; - } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs b/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs deleted file mode 100644 index 4281aebf..00000000 --- a/OpenAuth.Identity/Quickstart/Grants/GrantsViewModel.cs +++ /dev/null @@ -1,26 +0,0 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; -using System.Collections.Generic; - -namespace OpenAuth.IdentityServer.Quickstart.Grants -{ - public class GrantsViewModel - { - public IEnumerable Grants { get; set; } - } - - public class GrantViewModel - { - public string ClientId { get; set; } - public string ClientName { get; set; } - public string ClientUrl { get; set; } - public string ClientLogoUrl { get; set; } - public DateTime Created { get; set; } - public DateTime? Expires { get; set; } - public IEnumerable IdentityGrantNames { get; set; } - public IEnumerable ApiGrantNames { get; set; } - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs b/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs index cf06e2cc..30fd200d 100644 --- a/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs +++ b/OpenAuth.Identity/Quickstart/Home/ErrorViewModel.cs @@ -1,22 +1,8 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using IdentityServer4.Models; - -namespace OpenAuth.IdentityServer.Quickstart.Home +锘縩amespace OpenAuth.IdentityServer.Quickstart.Home { public class ErrorViewModel { - public ErrorViewModel() - { - } - - public ErrorViewModel(string error) - { - Error = new ErrorMessage { Error = error }; - } - - public ErrorMessage Error { get; set; } + public string ErrorId { get; set; } + public string ErrorMessage { get; set; } } -} \ No newline at end of file +} diff --git a/OpenAuth.Identity/Quickstart/Home/HomeController.cs b/OpenAuth.Identity/Quickstart/Home/HomeController.cs index 2f89b2a7..1f58b658 100644 --- a/OpenAuth.Identity/Quickstart/Home/HomeController.cs +++ b/OpenAuth.Identity/Quickstart/Home/HomeController.cs @@ -1,9 +1,3 @@ -// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Threading.Tasks; -using IdentityServer4.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Hosting; @@ -11,17 +5,14 @@ using Microsoft.Extensions.Logging; namespace OpenAuth.IdentityServer.Quickstart.Home { - [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { - private readonly IIdentityServerInteractionService _interaction; private readonly IHostEnvironment _environment; private readonly ILogger _logger; - public HomeController(IIdentityServerInteractionService interaction, IHostEnvironment environment, ILogger logger) + public HomeController(IHostEnvironment environment, ILogger logger) { - _interaction = interaction; _environment = environment; _logger = logger; } @@ -30,7 +21,6 @@ namespace OpenAuth.IdentityServer.Quickstart.Home { if (_environment.IsDevelopment()) { - // only show in development return View(); } @@ -39,24 +29,14 @@ namespace OpenAuth.IdentityServer.Quickstart.Home } /// - /// Shows the error page + /// 鏄剧ず閿欒椤甸潰 /// - public async Task Error(string errorId) + public IActionResult Error(string errorId) { - var vm = new ErrorViewModel(); - - // retrieve error details from identityserver - var message = await _interaction.GetErrorContextAsync(errorId); - if (message != null) + var vm = new ErrorViewModel { - vm.Error = message; - - if (!_environment.IsDevelopment()) - { - // only show in development - message.ErrorDescription = null; - } - } + ErrorId = errorId + }; return View("Error", vm); } diff --git a/OpenAuth.Identity/Quickstart/TestUsers.cs b/OpenAuth.Identity/Quickstart/TestUsers.cs deleted file mode 100644 index 7735c100..00000000 --- a/OpenAuth.Identity/Quickstart/TestUsers.cs +++ /dev/null @@ -1,27 +0,0 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System.Collections.Generic; -using System.Security.Claims; -using IdentityModel; -using IdentityServer4.Test; -using Infrastructure; -using OpenAuth.App; - -namespace OpenAuth.IdentityServer.Quickstart -{ - public class TestUsers - { - public static List Users = new List - { - new TestUser{SubjectId = "System", Username = Define.SYSTEM_USERNAME, Password = Define.SYSTEM_USERPWD, - Claims = - { - new Claim(JwtClaimTypes.Name, "System"), - new Claim(JwtClaimTypes.GivenName, "yubao"), - new Claim(JwtClaimTypes.FamilyName, "lee")} - } - }; - } -} \ No newline at end of file diff --git a/OpenAuth.Identity/Startup.cs b/OpenAuth.Identity/Startup.cs index 7386dcd5..32c88fdf 100644 --- a/OpenAuth.Identity/Startup.cs +++ b/OpenAuth.Identity/Startup.cs @@ -1,11 +1,8 @@ -锘// Copyright (c) Brock Allen & Dominick Baier. All rights reserved. -// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. - - -using System; +锘縰sing System; using System.Linq; using Autofac; using Infrastructure; +using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; @@ -14,7 +11,12 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using OpenAuth.App; using OpenAuth.Repository; +using OpenIddict.Abstractions; using SqlSugar; +using OpenIddict.Server; +using OpenAuth.IdentityServer.Handlers; +using static OpenIddict.Abstractions.OpenIddictConstants; +using static OpenIddict.Server.OpenIddictServerEvents; namespace OpenAuth.IdentityServer { @@ -32,41 +34,69 @@ namespace OpenAuth.IdentityServer { services.AddControllersWithViews(); - var builder = services.AddIdentityServer() - .AddInMemoryIdentityResources(Config.GetIdentityResources()) - .AddInMemoryApiResources(Config.GetApis()) - .AddInMemoryClients(Config.GetClients(Environment.IsProduction())) - .AddProfileService(); + // Cookie 璁よ瘉锛堢敤浜庣櫥褰曢〉闈㈢淮鎸佷細璇濓級 + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => + { + options.LoginPath = "/Account/Login"; + options.LogoutPath = "/Account/Logout"; + }); + + // OpenIddict 閰嶇疆 + services.AddOpenIddict() + .AddServer(options => + { + // 鍚敤鎺堟潈鐮佹祦绋 + PKCE锛圴ue3 鍓嶇浣跨敤锛 + options.AllowAuthorizationCodeFlow() + .RequireProofKeyForCodeExchange(); + + // 鍚敤闅愬紡娴佺▼锛圫wagger 浣跨敤锛 + options.AllowImplicitFlow(); + + // 閰嶇疆绔偣 URI + options.SetAuthorizationEndpointUris("/connect/authorize") + .SetTokenEndpointUris("/connect/token") + .SetUserinfoEndpointUris("/connect/userinfo") + .SetLogoutEndpointUris("/connect/logout"); + + // 娉ㄥ唽 scope + options.RegisterScopes(Scopes.OpenId, Scopes.Profile, "openauthapi"); + + // 寮鍙戠幆澧冧娇鐢ㄤ复鏃惰瘉涔 + options.AddDevelopmentEncryptionCertificate() + .AddDevelopmentSigningCertificate(); + + // 绂佺敤 access_token 鍔犲瘑锛岃 WebApi 鐨 JwtBearer 鑳界洿鎺ラ獙璇 + options.DisableAccessTokenEncryption(); + + // 绂佺敤闄嶇骇妯″紡涓嬬殑寮哄埗 scope/client 楠岃瘉 + options.UseAspNetCore() + .EnableAuthorizationEndpointPassthrough() + .EnableTokenEndpointPassthrough() + .EnableUserinfoEndpointPassthrough() + .EnableLogoutEndpointPassthrough() + .DisableTransportSecurityRequirement(); // 寮鍙戠幆澧冨厑璁 HTTP锛堢敓浜х幆澧冨簲绉婚櫎锛 + + // 闄嶇骇妯″紡锛氫笉浣跨敤鏁版嵁搴撳瓨鍌紝鎵嬪姩澶勭悊楠岃瘉 + options.EnableDegradedMode(); + + // 娉ㄥ唽鑷畾涔夐獙璇 handler锛堥檷绾фā寮忓繀椤伙級 + options.AddEventHandler(builder => + builder.UseSingletonHandler()); + options.AddEventHandler(builder => + builder.UseSingletonHandler()); + options.AddEventHandler(builder => + builder.UseSingletonHandler()); + }) + .AddValidation(options => + { + options.UseLocalServer(); + options.UseAspNetCore(); + }); services.ConfigureNonBreakingSameSiteCookies(); services.AddCors(); -// todo:濡傛灉姝e紡 鐜璇风敤涓嬮潰鐨勬柟寮忛檺鍒堕殢鎰忚闂法鍩 -// var origins = new [] -// { -// "http://localhost:1803", -// "http://localhost:52789" -// }; -// if (Environment.IsProduction()) -// { -// origins = new [] -// { -// "http://demo.openauth.net.cn:1803", -// "http://demo.openauth.net.cn:52789" -// }; -// } -// services.AddCors(option=>option.AddPolicy("cors", policy => -// policy.AllowAnyHeader().AllowAnyMethod().AllowCredentials().WithOrigins(origins))); - - //鍏ㄩ儴鐢ㄦ祴璇曠幆澧冿紝姝e紡鐜璇峰弬鑰僪ttps://www.cnblogs.com/guolianyu/p/9872661.html - //if (Environment.IsDevelopment()) - //{ - builder.AddDeveloperSigningCredential(); - //} - //else - //{ - // throw new Exception("need to configure key material"); - //} services.AddAuthentication(); @@ -153,7 +183,7 @@ namespace OpenAuth.IdentityServer app.UseStaticFiles(); app.UseRouting(); - app.UseIdentityServer(); + app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => diff --git a/OpenAuth.Identity/Views/Consent/Index.cshtml b/OpenAuth.Identity/Views/Consent/Index.cshtml deleted file mode 100644 index e990320c..00000000 --- a/OpenAuth.Identity/Views/Consent/Index.cshtml +++ /dev/null @@ -1,82 +0,0 @@ -@model OpenAuth.IdentityServer.Quickstart.Consent.ConsentViewModel - - \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/Success.cshtml b/OpenAuth.Identity/Views/Device/Success.cshtml deleted file mode 100644 index f41cdefb..00000000 --- a/OpenAuth.Identity/Views/Device/Success.cshtml +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml b/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml deleted file mode 100644 index cbe38dd7..00000000 --- a/OpenAuth.Identity/Views/Device/UserCodeCapture.cshtml +++ /dev/null @@ -1,14 +0,0 @@ -@model string - - \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml b/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml deleted file mode 100644 index 9861210c..00000000 --- a/OpenAuth.Identity/Views/Device/UserCodeConfirmation.cshtml +++ /dev/null @@ -1,93 +0,0 @@ -@model OpenAuth.IdentityServer.Quickstart.Device.DeviceAuthorizationViewModel - - \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Diagnostics/Index.cshtml b/OpenAuth.Identity/Views/Diagnostics/Index.cshtml deleted file mode 100644 index c14c6d4f..00000000 --- a/OpenAuth.Identity/Views/Diagnostics/Index.cshtml +++ /dev/null @@ -1,32 +0,0 @@ -@model OpenAuth.IdentityServer.Quickstart.Diagnostics.DiagnosticsViewModel - -

Authentication cookie

- -

Claims

-
- @foreach (var claim in Model.AuthenticateResult.Principal.Claims) - { -
@claim.Type
-
@claim.Value
- } -
- -

Properties

-
- @foreach (var prop in Model.AuthenticateResult.Properties.Items) - { -
@prop.Key
-
@prop.Value
- } -
- -@if (Model.Clients.Any()) -{ -

Clients

-
    - @foreach (var client in Model.Clients) - { -
  • @client
  • - } -
-} \ No newline at end of file diff --git a/OpenAuth.Identity/Views/Grants/Index.cshtml b/OpenAuth.Identity/Views/Grants/Index.cshtml deleted file mode 100644 index d34f007e..00000000 --- a/OpenAuth.Identity/Views/Grants/Index.cshtml +++ /dev/null @@ -1,79 +0,0 @@ -锘緻model OpenAuth.IdentityServer.Quickstart.Grants.GrantsViewModel - -
- - - @if (Model.Grants.Any() == false) - { -
-
-
- You have not given access to any applications -
-
-
- } - else - { - foreach (var grant in Model.Grants) - { -
-
- @if (grant.ClientLogoUrl != null) - { - - } -
-
-
@grant.ClientName
-
- Created: @grant.Created.ToString("yyyy-MM-dd") -
- @if (grant.Expires.HasValue) - { -
- Expires: @grant.Expires.Value.ToString("yyyy-MM-dd") -
- } - @if (grant.IdentityGrantNames.Any()) - { -
-
Identity Grants
-
    - @foreach (var name in grant.IdentityGrantNames) - { -
  • @name
  • - } -
-
- } - @if (grant.ApiGrantNames.Any()) - { -
-
API Grants
-
    - @foreach (var name in grant.ApiGrantNames) - { -
  • @name
  • - } -
-
- } -
-
-
- - -
-
-
- } - } -
\ No newline at end of file diff --git a/OpenAuth.Identity/Views/Home/Index.cshtml b/OpenAuth.Identity/Views/Home/Index.cshtml index 57205690..332e2a41 100644 --- a/OpenAuth.Identity/Views/Home/Index.cshtml +++ b/OpenAuth.Identity/Views/Home/Index.cshtml @@ -1,14 +1,10 @@ -@{ - var version = typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.GetName().Version.ToString(); -} -
@@ -21,19 +17,5 @@ where you can find metadata and links to all the endpoints, key material, etc.

-
-

- Click here to manage your stored grants. -

-
- -
-
-

- Here are links to the - source code repository, - and ready to use samples. -

-
diff --git a/OpenAuth.Identity/Views/Shared/Error.cshtml b/OpenAuth.Identity/Views/Shared/Error.cshtml index 407f0579..efb38c01 100644 --- a/OpenAuth.Identity/Views/Shared/Error.cshtml +++ b/OpenAuth.Identity/Views/Shared/Error.cshtml @@ -1,11 +1,5 @@ @model OpenAuth.IdentityServer.Quickstart.Home.ErrorViewModel -@{ - var error = Model?.Error?.Error; - var errorDescription = Model?.Error?.ErrorDescription; - var request_id = Model?.Error?.RequestId; -} -
diff --git a/OpenAuth.Identity/Views/Shared/_Layout.cshtml b/OpenAuth.Identity/Views/Shared/_Layout.cshtml index f16732aa..2d1a3c04 100644 --- a/OpenAuth.Identity/Views/Shared/_Layout.cshtml +++ b/OpenAuth.Identity/Views/Shared/_Layout.cshtml @@ -1,9 +1,8 @@ -锘緻using IdentityServer4.Extensions -@{ +锘緻{ string name = null; if (!true.Equals(ViewData["signed-out"])) { - name = Context.User?.GetDisplayName(); + name = Context.User?.Identity?.IsAuthenticated == true ? Context.User.Identity.Name : null; } } diff --git a/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml b/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml deleted file mode 100644 index 1a3a6fd9..00000000 --- a/OpenAuth.Identity/Views/Shared/_ScopeListItem.cshtml +++ /dev/null @@ -1,34 +0,0 @@ -锘緻model OpenAuth.IdentityServer.Quickstart.Consent.ScopeViewModel - -
  • - - @if (Model.Required) - { - (required) - } - @if (Model.Description != null) - { - - } -
  • \ No newline at end of file diff --git a/OpenAuth.WebApi/OpenAuth.WebApi.csproj b/OpenAuth.WebApi/OpenAuth.WebApi.csproj index fa4e5bb8..9230fe61 100644 --- a/OpenAuth.WebApi/OpenAuth.WebApi.csproj +++ b/OpenAuth.WebApi/OpenAuth.WebApi.csproj @@ -30,7 +30,6 @@ - diff --git a/OpenAuth.WebApi/Startup.cs b/OpenAuth.WebApi/Startup.cs index e7e640bf..83f8c748 100644 --- a/OpenAuth.WebApi/Startup.cs +++ b/OpenAuth.WebApi/Startup.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; using Autofac; -using IdentityServer4.AccessTokenValidation; +using Microsoft.AspNetCore.Authentication.JwtBearer; using Infrastructure; using Infrastructure.Extensions.AutofacManager; using Infrastructure.Middleware; @@ -52,7 +52,7 @@ namespace OpenAuth.WebApi { services.AddAuthorization(); - services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme) + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = identityServer;