mirror of
https://github.com/nsnail/IGeekFan.AspNetCore.Knife4jUI.git
synced 2025-04-16 22:02:52 +08:00
parent
1d63f2c585
commit
8ea53827f5
@ -22,6 +22,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SwaggerUI_IndexStream_Knife
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IGeekFan.AspNetCore.Knife4jUI", "src\IGeekFan.AspNetCore.Knife4jUI\IGeekFan.AspNetCore.Knife4jUI.csproj", "{6C784918-BE29-4FEF-8AC3-9D34A38DE822}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IGeekFan.AspNetCore.Knife4jUI", "src\IGeekFan.AspNetCore.Knife4jUI\IGeekFan.AspNetCore.Knife4jUI.csproj", "{6C784918-BE29-4FEF-8AC3-9D34A38DE822}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebSites", "WebSites", "{86851B6C-3504-4879-8464-1DB422D46BA0}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OAuth2Integration", "test\WebSites\OAuth2Integration\OAuth2Integration.csproj", "{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -44,6 +48,10 @@ Global
|
|||||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Release|Any CPU.Build.0 = Release|Any CPU
|
{6C784918-BE29-4FEF-8AC3-9D34A38DE822}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -53,6 +61,8 @@ Global
|
|||||||
{86A24FA0-E85D-4BDD-97D7-B990C50A40A9} = {75C51574-4CBD-403B-8182-8BF2A6DCFD43}
|
{86A24FA0-E85D-4BDD-97D7-B990C50A40A9} = {75C51574-4CBD-403B-8182-8BF2A6DCFD43}
|
||||||
{1D6FD5CA-5D58-4895-8545-A93099CE1AD4} = {C146A419-15E0-4475-9623-706C5E2DCE0B}
|
{1D6FD5CA-5D58-4895-8545-A93099CE1AD4} = {C146A419-15E0-4475-9623-706C5E2DCE0B}
|
||||||
{6C784918-BE29-4FEF-8AC3-9D34A38DE822} = {929BB2D7-C678-4BE8-8AA9-F271A2AE4545}
|
{6C784918-BE29-4FEF-8AC3-9D34A38DE822} = {929BB2D7-C678-4BE8-8AA9-F271A2AE4545}
|
||||||
|
{86851B6C-3504-4879-8464-1DB422D46BA0} = {75C51574-4CBD-403B-8182-8BF2A6DCFD43}
|
||||||
|
{9E8D8F42-33F0-4F2D-9B56-1AB1B33DE1FA} = {86851B6C-3504-4879-8464-1DB422D46BA0}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {9D77CCB4-F597-421B-9EF9-52D4B0AC382D}
|
SolutionGuid = {9D77CCB4-F597-421B-9EF9-52D4B0AC382D}
|
||||||
|
@ -11,14 +11,14 @@
|
|||||||
<RepositoryType>git</RepositoryType>
|
<RepositoryType>git</RepositoryType>
|
||||||
<RepositoryUrl>https://github.com/luoyunchong/IGeekFan.AspNetCore.Knife4jUI.git</RepositoryUrl>
|
<RepositoryUrl>https://github.com/luoyunchong/IGeekFan.AspNetCore.Knife4jUI.git</RepositoryUrl>
|
||||||
<RootNamespace>IGeekFan.AspNetCore.Knife4jUI</RootNamespace>
|
<RootNamespace>IGeekFan.AspNetCore.Knife4jUI</RootNamespace>
|
||||||
<Version>0.0.4</Version>
|
<Version>0.0.6</Version>
|
||||||
<Company />
|
<Company />
|
||||||
<Authors>igeekfan;xiaoym;</Authors>
|
<Authors>igeekfan;xiaoym;</Authors>
|
||||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
<Copyright>Apache License 2.0</Copyright>
|
<Copyright>Apache License 2.0</Copyright>
|
||||||
<PackageLicenseExpression></PackageLicenseExpression>
|
<PackageLicenseExpression></PackageLicenseExpression>
|
||||||
<AssemblyVersion>0.0.4.0</AssemblyVersion>
|
<AssemblyVersion>0.0.6.0</AssemblyVersion>
|
||||||
<FileVersion>0.0.4.0</FileVersion>
|
<FileVersion>0.0.6.0</FileVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
|
||||||
|
@ -6,16 +6,17 @@
|
|||||||
<meta name=viewport content="width=device-width,initial-scale=1">
|
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||||
<link rel=icon href=favicon.ico>
|
<link rel=icon href=favicon.ico>
|
||||||
<title>%(DocumentTitle)</title>
|
<title>%(DocumentTitle)</title>
|
||||||
<link href=knife4j/css/app.b2e62e71.css rel=preload as= style>
|
<link href=knife4j/css/app.8efd8010.css rel=preload as= style>
|
||||||
<link href=knife4j/js/app.ed7acb83.js rel=preload as=script>
|
<link href=knife4j/js/app.765df824.js rel=preload as=script>
|
||||||
<link href=knife4j/js/chunk-vendors.e86fea24.js rel=preload as=script>
|
<link href=knife4j/js/chunk-vendors.e86fea24.js rel=preload as=script>
|
||||||
<link href=knife4j/css/app.b2e62e71.css rel=stylesheet>
|
<link href=knife4j/css/app.8efd8010.css rel=stylesheet>
|
||||||
%(HeadContent)
|
%(HeadContent)
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript><strong>We're sorry but knife4j-vue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id=app></div>
|
<noscript><strong>We're sorry but knife4j-vue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript>
|
||||||
|
<div id=app></div>
|
||||||
<script src=knife4j/js/chunk-vendors.e86fea24.js></script>
|
<script src=knife4j/js/chunk-vendors.e86fea24.js></script>
|
||||||
<script src=knife4j/js/app.ed7acb83.js></script>
|
<script src=knife4j/js/app.765df824.js></script>
|
||||||
<script>
|
<script>
|
||||||
window.onload = function () {
|
window.onload = function () {
|
||||||
|
|
||||||
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 306 KiB After Width: | Height: | Size: 306 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
File diff suppressed because one or more lines are too long
BIN
src/IGeekFan.AspNetCore.Knife4jUI/knife4j/js/app.765df824.js.gz
Normal file
BIN
src/IGeekFan.AspNetCore.Knife4jUI/knife4j/js/app.765df824.js.gz
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
57
test/WebSites/OAuth2Integration/AuthServer/Config.cs
Normal file
57
test/WebSites/OAuth2Integration/AuthServer/Config.cs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
using IdentityServer4.Test;
|
||||||
|
|
||||||
|
namespace OAuth2Integration.AuthServer
|
||||||
|
{
|
||||||
|
public static class Config
|
||||||
|
{
|
||||||
|
internal static IEnumerable<Client> Clients()
|
||||||
|
{
|
||||||
|
yield return new Client
|
||||||
|
{
|
||||||
|
ClientId = "test-id",
|
||||||
|
ClientName = "Test client (Code with PKCE)",
|
||||||
|
|
||||||
|
RedirectUris = new[] {
|
||||||
|
"http://localhost:55202/resource-server/swagger/oauth2-redirect.html", // IIS Express
|
||||||
|
"http://localhost:5000/resource-server/swagger/oauth2-redirect.html", // Kestrel
|
||||||
|
},
|
||||||
|
|
||||||
|
ClientSecrets = { new Secret("test-secret".Sha256()) },
|
||||||
|
RequireConsent = true,
|
||||||
|
|
||||||
|
AllowedGrantTypes = GrantTypes.Code,
|
||||||
|
RequirePkce = true,
|
||||||
|
AllowedScopes = new[] { "readAccess", "writeAccess" },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IEnumerable<ApiResource> ApiResources()
|
||||||
|
{
|
||||||
|
yield return new ApiResource
|
||||||
|
{
|
||||||
|
Name = "api",
|
||||||
|
DisplayName = "API",
|
||||||
|
Scopes = new[]
|
||||||
|
{
|
||||||
|
new Scope("readAccess", "Access read operations"),
|
||||||
|
new Scope("writeAccess", "Access write operations")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static List<TestUser> TestUsers()
|
||||||
|
{
|
||||||
|
return new List<TestUser>
|
||||||
|
{
|
||||||
|
new TestUser
|
||||||
|
{
|
||||||
|
SubjectId = "joebloggs",
|
||||||
|
Username = "joebloggs",
|
||||||
|
Password = "pass123"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using IdentityServer4;
|
||||||
|
using IdentityServer4.Test;
|
||||||
|
|
||||||
|
namespace OAuth2Integration.AuthServer.Controllers
|
||||||
|
{
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
[Route("account")]
|
||||||
|
public class AccountController : Controller
|
||||||
|
{
|
||||||
|
private readonly TestUserStore _userStore;
|
||||||
|
|
||||||
|
public AccountController()
|
||||||
|
{
|
||||||
|
_userStore = new TestUserStore(Config.TestUsers());
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("login")]
|
||||||
|
public IActionResult Login(string returnUrl)
|
||||||
|
{
|
||||||
|
var viewModel = new LoginViewModel { Username = "joebloggs", Password = "pass123", ReturnUrl = returnUrl };
|
||||||
|
|
||||||
|
return View("/AuthServer/Views/Login.cshtml", viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("login")]
|
||||||
|
public async Task<IActionResult> Login([FromForm]LoginViewModel viewModel)
|
||||||
|
{
|
||||||
|
if (!_userStore.ValidateCredentials(viewModel.Username, viewModel.Password))
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("", "Invalid username or password");
|
||||||
|
viewModel.Password = string.Empty;
|
||||||
|
return View("/AuthServer/Views/Login.cshtml", viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use an IdentityServer-compatible ClaimsPrincipal
|
||||||
|
var identityServerUser = new IdentityServerUser(viewModel.Username);
|
||||||
|
identityServerUser.DisplayName = viewModel.Username;
|
||||||
|
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, identityServerUser.CreatePrincipal());
|
||||||
|
|
||||||
|
return Redirect(viewModel.ReturnUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LoginViewModel
|
||||||
|
{
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using IdentityServer4.Stores;
|
||||||
|
using IdentityServer4.Services;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
|
||||||
|
namespace OAuth2Integration.AuthServer.Controllers
|
||||||
|
{
|
||||||
|
[ApiExplorerSettings(IgnoreApi = true)]
|
||||||
|
public class ConsentController : Controller
|
||||||
|
{
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
private readonly IClientStore _clientStore;
|
||||||
|
private readonly IResourceStore _resourceStore;
|
||||||
|
|
||||||
|
public ConsentController(
|
||||||
|
IIdentityServerInteractionService interaction,
|
||||||
|
IClientStore clientStore,
|
||||||
|
IResourceStore resourceStore)
|
||||||
|
{
|
||||||
|
_interaction = interaction;
|
||||||
|
_clientStore = clientStore;
|
||||||
|
_resourceStore = resourceStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("consent")]
|
||||||
|
public async Task<IActionResult> Consent(string returnUrl)
|
||||||
|
{
|
||||||
|
var request = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||||
|
var client = await _clientStore.FindEnabledClientByIdAsync(request.ClientId);
|
||||||
|
var resource = await _resourceStore.FindApiResourceAsync("api");
|
||||||
|
|
||||||
|
var viewModel = new ConsentViewModel
|
||||||
|
{
|
||||||
|
ReturnUrl = returnUrl,
|
||||||
|
ClientName = client.ClientName,
|
||||||
|
ScopesRequested = resource.Scopes.Where(s => request.ScopesRequested.Contains(s.Name))
|
||||||
|
};
|
||||||
|
|
||||||
|
return View("/AuthServer/Views/Consent.cshtml", viewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost("consent")]
|
||||||
|
public async Task<IActionResult> Consent([FromForm]ConsentViewModel viewModel)
|
||||||
|
{
|
||||||
|
var request = await _interaction.GetAuthorizationContextAsync(viewModel.ReturnUrl);
|
||||||
|
|
||||||
|
// Communicate outcome of consent back to identityserver
|
||||||
|
var consentResponse = new ConsentResponse
|
||||||
|
{
|
||||||
|
ScopesConsented = viewModel.ScopesConsented
|
||||||
|
};
|
||||||
|
await _interaction.GrantConsentAsync(request, consentResponse);
|
||||||
|
|
||||||
|
return Redirect(viewModel.ReturnUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConsentViewModel
|
||||||
|
{
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
public string ClientName { get; set; }
|
||||||
|
public IEnumerable<Scope> ScopesRequested { get; set; }
|
||||||
|
public string[] ScopesConsented { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
@model OAuth2Integration.AuthServer.Controllers.ConsentViewModel
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h1>Consent</h1>
|
||||||
|
<p>
|
||||||
|
@Model.ClientName is requesting your permission to ...
|
||||||
|
</p>
|
||||||
|
<form asp-controller="Consent" asp-action="Consent">
|
||||||
|
<input type="hidden" asp-for="ReturnUrl" />
|
||||||
|
<fieldset>
|
||||||
|
<ul>
|
||||||
|
@foreach (var scope in Model.ScopesRequested)
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<input name="ScopesConsented" type="checkbox" value="@scope.Name" checked />@scope.DisplayName
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</fieldset>
|
||||||
|
<button type="submit">Grant Access</button>
|
||||||
|
</form>
|
@ -0,0 +1,23 @@
|
|||||||
|
@model OAuth2Integration.AuthServer.Controllers.LoginViewModel
|
||||||
|
|
||||||
|
<style>
|
||||||
|
input {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<h1>Login</h1>
|
||||||
|
<form asp-controller="Account" asp-action="Login">
|
||||||
|
<input type="hidden" asp-for="ReturnUrl" />
|
||||||
|
<fieldset>
|
||||||
|
<label>
|
||||||
|
Username:
|
||||||
|
<input type="text" asp-for="Username" />
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Password:
|
||||||
|
<input type="text" asp-for="Password" />
|
||||||
|
</label>
|
||||||
|
</fieldset>
|
||||||
|
<button type="submit">Login</button>
|
||||||
|
</form>
|
@ -0,0 +1 @@
|
|||||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
17
test/WebSites/OAuth2Integration/OAuth2Integration.csproj
Normal file
17
test/WebSites/OAuth2Integration/OAuth2Integration.csproj
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="IdentityServer4" Version="3.0.2" />
|
||||||
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.Swagger" Version="5.5.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="5.5.1" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUi" Version="5.5.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
26
test/WebSites/OAuth2Integration/Program.cs
Normal file
26
test/WebSites/OAuth2Integration/Program.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace OAuth2Integration
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
CreateHostBuilder(args).Build().Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||||
|
Host.CreateDefaultBuilder(args)
|
||||||
|
.ConfigureWebHostDefaults(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder.UseStartup<Startup>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "http://localhost:55202",
|
||||||
|
"sslPort": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "resource-server/swagger",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"OAuth2Integration": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"launchUrl": "resource-server/swagger",
|
||||||
|
"applicationUrl": "http://localhost:5000",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
|
||||||
|
namespace OAuth2Integration.ResourceServer.Controllers
|
||||||
|
{
|
||||||
|
[Route("products")]
|
||||||
|
[Authorize(AuthenticationSchemes = "Bearer")]
|
||||||
|
public class ProductsController : Controller
|
||||||
|
{
|
||||||
|
[HttpGet]
|
||||||
|
[Authorize("readAccess")]
|
||||||
|
public IEnumerable<Product> GetProducts()
|
||||||
|
{
|
||||||
|
yield return new Product
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
SerialNo = "ABC123",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("{id}")]
|
||||||
|
[Authorize("readAccess")]
|
||||||
|
public Product GetProduct(int id)
|
||||||
|
{
|
||||||
|
return new Product
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
SerialNo = "ABC123",
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[Authorize("writeAccess")]
|
||||||
|
public void CreateProduct([FromBody]Product product)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("{id}")]
|
||||||
|
[Authorize("writeAccess")]
|
||||||
|
public void DeleteProduct(int id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Product
|
||||||
|
{
|
||||||
|
public int Id { get; internal set; }
|
||||||
|
public string SerialNo { get; set; }
|
||||||
|
public ProductStatus Status { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ProductStatus
|
||||||
|
{
|
||||||
|
InStock, ComingSoon
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,40 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||||
|
|
||||||
|
namespace OAuth2Integration.ResourceServer.Swagger
|
||||||
|
{
|
||||||
|
public class SecurityRequirementsOperationFilter : IOperationFilter
|
||||||
|
{
|
||||||
|
public void Apply(OpenApiOperation operation, OperationFilterContext context)
|
||||||
|
{
|
||||||
|
// Policy names map to scopes
|
||||||
|
var requiredScopes = context.MethodInfo
|
||||||
|
.GetCustomAttributes(true)
|
||||||
|
.OfType<AuthorizeAttribute>()
|
||||||
|
.Select(attr => attr.Policy)
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
if (requiredScopes.Any())
|
||||||
|
{
|
||||||
|
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
|
||||||
|
operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
|
||||||
|
|
||||||
|
var oAuthScheme = new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
||||||
|
};
|
||||||
|
|
||||||
|
operation.Security = new List<OpenApiSecurityRequirement>
|
||||||
|
{
|
||||||
|
new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
[ oAuthScheme ] = requiredScopes.ToList()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
144
test/WebSites/OAuth2Integration/Startup.cs
Normal file
144
test/WebSites/OAuth2Integration/Startup.cs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.OpenApi.Models;
|
||||||
|
|
||||||
|
namespace OAuth2Integration
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
// Register IdentityServer services to power OAuth2.0 flows
|
||||||
|
services.AddIdentityServer()
|
||||||
|
.AddDeveloperSigningCredential()
|
||||||
|
.AddInMemoryClients(AuthServer.Config.Clients())
|
||||||
|
.AddInMemoryApiResources(AuthServer.Config.ApiResources())
|
||||||
|
.AddTestUsers(AuthServer.Config.TestUsers());
|
||||||
|
|
||||||
|
// The auth setup is a little nuanced because this app provides the auth-server & the resource-server
|
||||||
|
// Use the "Cookies" scheme by default & explicitly require "Bearer" in the resource-server controllers
|
||||||
|
// See https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?tabs=aspnetcore2x
|
||||||
|
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
|
||||||
|
.AddCookie()
|
||||||
|
.AddIdentityServerAuthentication(c =>
|
||||||
|
{
|
||||||
|
c.Authority = "http://localhost:55202/auth-server/";
|
||||||
|
c.RequireHttpsMetadata = false;
|
||||||
|
c.ApiName = "api";
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure named auth policies that map directly to OAuth2.0 scopes
|
||||||
|
services.AddAuthorization(c =>
|
||||||
|
{
|
||||||
|
c.AddPolicy("readAccess", p => p.RequireClaim("scope", "readAccess"));
|
||||||
|
c.AddPolicy("writeAccess", p => p.RequireClaim("scope", "writeAccess"));
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddControllersWithViews();
|
||||||
|
|
||||||
|
services.AddSwaggerGen(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerDoc("v1", new OpenApiInfo { Version = "v1", Title = "Test API V1" });
|
||||||
|
|
||||||
|
// Define the OAuth2.0 scheme that's in use (i.e. Implicit Flow)
|
||||||
|
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Type = SecuritySchemeType.OAuth2,
|
||||||
|
Flows = new OpenApiOAuthFlows
|
||||||
|
{
|
||||||
|
AuthorizationCode = new OpenApiOAuthFlow
|
||||||
|
{
|
||||||
|
AuthorizationUrl = new Uri("/auth-server/connect/authorize", UriKind.Relative),
|
||||||
|
TokenUrl = new Uri("/auth-server/connect/token", UriKind.Relative),
|
||||||
|
Scopes = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "readAccess", "Access read operations" },
|
||||||
|
{ "writeAccess", "Access write operations" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
|
{
|
||||||
|
{
|
||||||
|
new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }
|
||||||
|
},
|
||||||
|
new[] { "readAccess", "writeAccess" }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assign scope requirements to operations based on AuthorizeAttribute
|
||||||
|
//c.OperationFilter<SecurityRequirementsOperationFilter>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||||
|
{
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Map("/auth-server", authServer =>
|
||||||
|
{
|
||||||
|
authServer.UseRouting();
|
||||||
|
|
||||||
|
authServer.UseAuthentication();
|
||||||
|
|
||||||
|
authServer.UseIdentityServer();
|
||||||
|
|
||||||
|
authServer.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapControllers();
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.Map("/resource-server", resourceServer =>
|
||||||
|
{
|
||||||
|
resourceServer.UseRouting();
|
||||||
|
|
||||||
|
resourceServer.UseAuthentication();
|
||||||
|
|
||||||
|
resourceServer.UseAuthorization();
|
||||||
|
|
||||||
|
resourceServer.UseEndpoints(endpoints =>
|
||||||
|
{
|
||||||
|
endpoints.MapControllers();
|
||||||
|
});
|
||||||
|
|
||||||
|
resourceServer.UseSwagger();
|
||||||
|
resourceServer.UseSwaggerUI(c =>
|
||||||
|
{
|
||||||
|
c.SwaggerEndpoint("/resource-server/swagger/v1/swagger.json", "My API V1");
|
||||||
|
c.EnableDeepLinking();
|
||||||
|
|
||||||
|
// Additional OAuth settings (See https://github.com/swagger-api/swagger-ui/blob/v3.10.0/docs/usage/oauth2.md)
|
||||||
|
c.OAuthClientId("test-id");
|
||||||
|
c.OAuthClientSecret("test-secret");
|
||||||
|
c.OAuthAppName("test-app");
|
||||||
|
c.OAuthScopeSeparator(" ");
|
||||||
|
c.OAuthUsePkce();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
test/WebSites/OAuth2Integration/appsettings.json
Normal file
10
test/WebSites/OAuth2Integration/appsettings.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft": "Warning",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
1
test/WebSites/OAuth2Integration/tempkey.rsa
Normal file
1
test/WebSites/OAuth2Integration/tempkey.rsa
Normal file
@ -0,0 +1 @@
|
|||||||
|
{"KeyId":"08433185bdf583112d2df84b15adc5fe","Parameters":{"D":"G5yB2Ha3ktEmpXH+1qIuJ/dJ8TJKFAj2j1qMLl/Zu0jI1GZJlCyeWHEM0B0IFlbkUq2W82ym9dJUaSGXC3FxSZ/JHwNzgonpv9ELQZDnAwvO0kpnJTw+SQWwDxUxItGNT8giqqm5LcSYWhv+QexepyEOwKr6Efh6hHrTsjxk/2t3xPhZJbutifcBrAaHb0wjV/mw442wXol1cjWmG5qVOj54n16kU2Pxr1i9oukZBJMYLltg8NB99KgvZSVvOoctXiPszEHel4kNdSv0R3yt2lBad+OEZykCgzgbZYmA2aIvUmB8r+yxFUiXEDuM9Wjp3gxBLdAWAdutPhtcXqPXGw==","DP":"fVcFyftVuEEFERT6UzfnIhDW/2A1ckzwutVQAYOjRwC+FyRIGyN6kzc1SSIhpuMECcibvPBp0wbyx0JsUbcUVlo6uKXaYx5h6Qcv4fIrqfFOUfVNuQ2W8mltwpDdFuquwOAEz08zMtkVavZxfghmJgYNeqqQvvuvetjAuL6Tdws=","DQ":"whD6E1yg5fe3FDcq+cVQnj3xeSWB7pYURNIYAI2Oo1y2fCfH7/TDaQbGpIr0nO/RiH2SxH1vJ5O5foKG0JES3wCe7qflJOviOzb3SNlXm3gCdvNZyAruA2k9wyro5hauDMU51saed6xAy3dL+Q5fKE+0fmKOBxVZr2RPMnglTc0=","Exponent":"AQAB","InverseQ":"tQt+mEXZQq9YpAU6Q7/8gpPBy+F3B52KmP3e5bcnvilSDIXOUjye8NMTd8WeX+NmOje+g+5aYW5kVyiXhwoVM1Bz3Iwyq51Kix3JMmaiFBPaC/r9jF/2JveiT3iKlzgi0RJcOM4rnA1pHakr3rz6nVoxpBGe8OMUTrg4cVkM4HU=","Modulus":"y6F+tcOmGz6DAYfooFDMtkeG5/7TqOpW3U3eSu4KmuoCMdc8hiJwspcwu6Yu6OGVbYKW1nZTEP2SizpTixy6W/TYFMIQZC08iBxR8mDlp2HXeGiOTMFJQFdgT+Qe0xgZ9yPdOSbBmih6dATK61k4QvDbrhAf8j7mv7JW4s5T6WCjmx0UOyo9medzP/YhCL6lnaxP3hWA61ncVK38BGHFzo/sZSyA79EXo/091CKm1V0VpZMhpZIt6wyXPg8bLyJh1edXFCqEYDXvVcfuFFhmILfB/g5CUz/YCJu2Z1VDG4OOXPtg6xJMAbcl/67yH0wu/HSNoUcLN1cJkYXAUsg1yQ==","P":"6Tl+cOLjTwUiQbTiVtC4v2uO4FcjsuKbWlqKiqJ7veMJQHdP/AOPNtLq9eKrpKfc6f0eRQHYLf//o1ntweFFBhZ3lgCAdD3gQw02meMCxSPt+BMgylFkNZXW0TfISx/a3q4XGBSLTmu0d5uN3FNxRDNmAKeL8xgvb/ikKQSHnGM=","Q":"34Qr0kjiZkXvxHY6pxfBmzwYitC/bBwxwOrLq8S30FN5G2QQ/KWc7EJnOJz3C+yX9Vep2Fslu5xYAoBjbEofVMRlvsu2T4c+d/KfUQLaualXNlQiR4TKqW13Dau+ZEDEh71NFdJwqhKzJb8ojiRMcMHD1M5AmDApDUfqAOoybuM="}}
|
Loading…
x
Reference in New Issue
Block a user