mirror of
https://git.teknik.io/Teknikode/Teknik.git
synced 2023-08-02 14:16:22 +02:00
Added Identity Server for authentication
This commit is contained in:
parent
7ec30d1e39
commit
62e4476897
5
.gitignore
vendored
5
.gitignore
vendored
@ -262,6 +262,7 @@ __pycache__/
|
|||||||
/Teknik/App_Data/MachineKey.config
|
/Teknik/App_Data/MachineKey.config
|
||||||
/Teknik/App_Data/ConnectionStrings.config
|
/Teknik/App_Data/ConnectionStrings.config
|
||||||
/Teknik/App_Data/Config.json
|
/Teknik/App_Data/Config.json
|
||||||
/Teknik/appsettings.Production.json
|
|
||||||
/Teknik/appsettings.Development.json
|
|
||||||
/Teknik/App_Data/version.json
|
/Teknik/App_Data/version.json
|
||||||
|
|
||||||
|
**/appsettings.*.json
|
||||||
|
**/tempkey.rsa
|
@ -5,6 +5,7 @@
|
|||||||
<RootNamespace>Teknik.Configuration</RootNamespace>
|
<RootNamespace>Teknik.Configuration</RootNamespace>
|
||||||
<AssemblyName>Teknik.Configuration</AssemblyName>
|
<AssemblyName>Teknik.Configuration</AssemblyName>
|
||||||
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
||||||
|
<Configurations>Debug;Release;Test</Configurations>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
30
Configuration/IdentityServerConfig.cs
Normal file
30
Configuration/IdentityServerConfig.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace Teknik.Configuration
|
||||||
|
{
|
||||||
|
public class IdentityServerConfig
|
||||||
|
{
|
||||||
|
public string Authority { get; set; }
|
||||||
|
|
||||||
|
public string ClientId { get; set; }
|
||||||
|
public string ClientSecret { get; set; }
|
||||||
|
public List<string> RedirectUris { get; set; }
|
||||||
|
public List<string> PostLogoutRedirectUris { get; set; }
|
||||||
|
|
||||||
|
public string APIName { get; set; }
|
||||||
|
public string APISecret { get; set; }
|
||||||
|
|
||||||
|
public IdentityServerConfig()
|
||||||
|
{
|
||||||
|
Authority = "https://localhost:5002";
|
||||||
|
ClientId = "mvc.client";
|
||||||
|
ClientSecret = "mysecret";
|
||||||
|
RedirectUris = new List<string>();
|
||||||
|
PostLogoutRedirectUris = new List<string>();
|
||||||
|
APIName = "api";
|
||||||
|
APISecret = "secret";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ namespace Teknik.Configuration
|
|||||||
public decimal PremiumAccountPrice { get; set; }
|
public decimal PremiumAccountPrice { get; set; }
|
||||||
public string PaymentType { get; set; }
|
public string PaymentType { get; set; }
|
||||||
public bool InviteCodeRequired { get; set; }
|
public bool InviteCodeRequired { get; set; }
|
||||||
|
public IdentityServerConfig IdentityServerConfig { get; set; }
|
||||||
|
|
||||||
public UserConfig()
|
public UserConfig()
|
||||||
{
|
{
|
||||||
@ -27,6 +28,7 @@ namespace Teknik.Configuration
|
|||||||
PremiumAccountPrice = 0;
|
PremiumAccountPrice = 0;
|
||||||
PaymentType = "Donation";
|
PaymentType = "Donation";
|
||||||
InviteCodeRequired = false;
|
InviteCodeRequired = false;
|
||||||
|
IdentityServerConfig = new IdentityServerConfig();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="MySql.Data" Version="8.0.11" />
|
<PackageReference Include="MySql.Data" Version="8.0.12" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
11
IdentityServer/ApplicationDbContext.cs
Normal file
11
IdentityServer/ApplicationDbContext.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer
|
||||||
|
{
|
||||||
|
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
|
||||||
|
{
|
||||||
|
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
|
||||||
|
}
|
||||||
|
}
|
143
IdentityServer/Configuration.cs
Normal file
143
IdentityServer/Configuration.cs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using IdentityModel;
|
||||||
|
using IdentityServer4;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
using IdentityServer4.Test;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Configuration
|
||||||
|
{
|
||||||
|
internal class Clients
|
||||||
|
{
|
||||||
|
public static IEnumerable<Client> Get(Config config)
|
||||||
|
{
|
||||||
|
return new List<Client> {
|
||||||
|
new Client
|
||||||
|
{
|
||||||
|
ClientId = config.UserConfig.IdentityServerConfig.ClientId,
|
||||||
|
ClientName = "Teknik Web Services",
|
||||||
|
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
|
||||||
|
|
||||||
|
ClientSecrets =
|
||||||
|
{
|
||||||
|
new Secret(config.UserConfig.IdentityServerConfig.ClientSecret.Sha256())
|
||||||
|
},
|
||||||
|
|
||||||
|
RequireConsent = false,
|
||||||
|
|
||||||
|
AllowedScopes =
|
||||||
|
{
|
||||||
|
IdentityServerConstants.StandardScopes.OpenId,
|
||||||
|
"role",
|
||||||
|
"account-info",
|
||||||
|
"security-info",
|
||||||
|
"teknik-api.read",
|
||||||
|
"teknik-api.write",
|
||||||
|
"auth-api"
|
||||||
|
},
|
||||||
|
AllowOfflineAccess = true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Resources
|
||||||
|
{
|
||||||
|
public static IEnumerable<IdentityResource> GetIdentityResources()
|
||||||
|
{
|
||||||
|
return new List<IdentityResource> {
|
||||||
|
new IdentityResources.OpenId(),
|
||||||
|
new IdentityResource
|
||||||
|
{
|
||||||
|
Name = "account-info",
|
||||||
|
DisplayName = "Account Info",
|
||||||
|
UserClaims = new List<string>
|
||||||
|
{
|
||||||
|
"username",
|
||||||
|
"email",
|
||||||
|
"creation-date",
|
||||||
|
"last-seen",
|
||||||
|
"account-type",
|
||||||
|
"account-status"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new IdentityResource
|
||||||
|
{
|
||||||
|
Name = "security-info",
|
||||||
|
DisplayName = "Security Info",
|
||||||
|
UserClaims = new List<string>
|
||||||
|
{
|
||||||
|
"recovery-email",
|
||||||
|
"recovery-verified",
|
||||||
|
"pgp-public-key"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new IdentityResource {
|
||||||
|
Name = "role",
|
||||||
|
DisplayName = "Role",
|
||||||
|
UserClaims = new List<string> {"role"}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<ApiResource> GetApiResources(Config config)
|
||||||
|
{
|
||||||
|
return new List<ApiResource> {
|
||||||
|
new ApiResource {
|
||||||
|
Name = config.UserConfig.IdentityServerConfig.APIName,
|
||||||
|
DisplayName = "Teknik API",
|
||||||
|
Description = "Teknik API Access for end users",
|
||||||
|
UserClaims = new List<string> {"role"},
|
||||||
|
ApiSecrets = new List<Secret> {new Secret(config.UserConfig.IdentityServerConfig.APISecret.Sha256()) },
|
||||||
|
Scopes = new List<Scope> {
|
||||||
|
new Scope("teknik-api.read", "Teknik API Read Access"),
|
||||||
|
new Scope("teknik-api.write", "Teknik API Write Access")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new ApiResource {
|
||||||
|
Name = "auth-api",
|
||||||
|
DisplayName = "Auth Server API",
|
||||||
|
Description = "Auth Server API Access for managing the Auth Server",
|
||||||
|
Scopes = new List<Scope> {
|
||||||
|
new Scope()
|
||||||
|
{
|
||||||
|
Name = "auth-api",
|
||||||
|
ShowInDiscoveryDocument = false,
|
||||||
|
Required = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Policies
|
||||||
|
{
|
||||||
|
public static IEnumerable<Policy> Get()
|
||||||
|
{
|
||||||
|
return new List<Policy>
|
||||||
|
{
|
||||||
|
new Policy
|
||||||
|
{
|
||||||
|
Name = "Internal",
|
||||||
|
Scopes = { "auth-api" }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Policy
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public ICollection<string> Scopes { get; set; }
|
||||||
|
|
||||||
|
public Policy()
|
||||||
|
{
|
||||||
|
Name = string.Empty;
|
||||||
|
Scopes = new List<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
IdentityServer/Content/common.css
Normal file
85
IdentityServer/Content/common.css
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
body {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
.navbar-header {
|
||||||
|
position: relative;
|
||||||
|
top: -4px;
|
||||||
|
}
|
||||||
|
.navbar-brand > .icon-banner {
|
||||||
|
position: relative;
|
||||||
|
top: -2px;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
label {
|
||||||
|
font-weight: normal !important;
|
||||||
|
}
|
||||||
|
.icon {
|
||||||
|
position: relative;
|
||||||
|
top: -10px;
|
||||||
|
}
|
||||||
|
.logged-out iframe {
|
||||||
|
display: none;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.page-consent .client-logo {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.page-consent .client-logo img {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
.page-consent .consent-buttons {
|
||||||
|
margin-top: 25px;
|
||||||
|
}
|
||||||
|
.page-consent .consent-form .consent-scopecheck {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
.page-consent .consent-form .consent-description {
|
||||||
|
margin-left: 25px;
|
||||||
|
}
|
||||||
|
.page-consent .consent-form .consent-description label {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
.page-consent .consent-form .consent-remember {
|
||||||
|
padding-left: 16px;
|
||||||
|
}
|
||||||
|
.grants .page-header {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.grants .grant {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding-bottom: 20px;
|
||||||
|
border-bottom: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
.grants .grant img {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
.grants .grant .clientname {
|
||||||
|
font-size: 140%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.grants .grant .granttype {
|
||||||
|
font-size: 120%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.grants .grant .created {
|
||||||
|
font-size: 120%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.grants .grant .expires {
|
||||||
|
font-size: 120%;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.grants .grant li {
|
||||||
|
list-style-type: none;
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
.grants .grant li:after {
|
||||||
|
content: ', ';
|
||||||
|
}
|
||||||
|
.grants .grant li:last-child:after {
|
||||||
|
content: '';
|
||||||
|
}
|
307
IdentityServer/Controllers/AccountController.cs
Normal file
307
IdentityServer/Controllers/AccountController.cs
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
using IdentityModel;
|
||||||
|
using IdentityServer4.Services;
|
||||||
|
using IdentityServer4.Stores;
|
||||||
|
using IdentityServer4.Test;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Security.Principal;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using IdentityServer4.Events;
|
||||||
|
using IdentityServer4.Extensions;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Teknik.IdentityServer.Security;
|
||||||
|
using Teknik.IdentityServer.Services;
|
||||||
|
using Teknik.IdentityServer.ViewModels;
|
||||||
|
using Teknik.IdentityServer.Options;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Teknik.Logging;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Controllers
|
||||||
|
{
|
||||||
|
public class AccountController : DefaultController
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
private readonly IEventService _events;
|
||||||
|
private readonly AccountService _account;
|
||||||
|
|
||||||
|
public AccountController(
|
||||||
|
ILogger<Logger> logger,
|
||||||
|
Config config,
|
||||||
|
IIdentityServerInteractionService interaction,
|
||||||
|
IClientStore clientStore,
|
||||||
|
IHttpContextAccessor httpContextAccessor,
|
||||||
|
IAuthenticationSchemeProvider schemeProvider,
|
||||||
|
IEventService events,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager) : base(logger, config)
|
||||||
|
{
|
||||||
|
// if the TestUserStore is not in DI, then we'll just use the global users collection
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
_interaction = interaction;
|
||||||
|
_events = events;
|
||||||
|
_account = new AccountService(interaction, httpContextAccessor, schemeProvider, clientStore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show login page
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Login(string returnUrl)
|
||||||
|
{
|
||||||
|
// build a model so we know what to show on the login page
|
||||||
|
var vm = await _account.BuildLoginViewModelAsync(returnUrl);
|
||||||
|
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle postback from username/password login
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Login(LoginViewModel model, string button, string returnUrl = null)
|
||||||
|
{
|
||||||
|
if (button != "login")
|
||||||
|
{
|
||||||
|
// the user clicked the "cancel" button
|
||||||
|
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||||
|
if (context != null)
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// since we don't have a valid context, then we just go back to the home page
|
||||||
|
return Redirect("~/");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ModelState.IsValid)
|
||||||
|
{
|
||||||
|
// Check to see if the user is banned
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
if (foundUser.AccountStatus == Utilities.AccountStatus.Banned)
|
||||||
|
{
|
||||||
|
// Redirect to banned page
|
||||||
|
return RedirectToAction(nameof(Banned));
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberMe, false);
|
||||||
|
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
// make sure the returnUrl is still valid, and if so redirect back to authorize endpoint or a local page
|
||||||
|
if (_interaction.IsValidReturnUrl(returnUrl) || Url.IsLocalUrl(returnUrl))
|
||||||
|
{
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Redirect("~/");
|
||||||
|
}
|
||||||
|
if (result.RequiresTwoFactor)
|
||||||
|
{
|
||||||
|
// Redirect to 2FA page
|
||||||
|
return RedirectToAction(nameof(LoginWith2fa), new { returnUrl, model.RememberMe });
|
||||||
|
}
|
||||||
|
if (result.IsLockedOut)
|
||||||
|
{
|
||||||
|
// Redirect to locked out page
|
||||||
|
return RedirectToAction(nameof(Lockout));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials"));
|
||||||
|
|
||||||
|
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// something went wrong, show form with error
|
||||||
|
var vm = await _account.BuildLoginViewModelAsync(model);
|
||||||
|
return View(vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
|
||||||
|
{
|
||||||
|
// Ensure the user has gone through the username & password screen first
|
||||||
|
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||||
|
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var model = new LoginWith2faViewModel { RememberMe = rememberMe };
|
||||||
|
ViewData["ReturnUrl"] = returnUrl;
|
||||||
|
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
|
||||||
|
|
||||||
|
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, model.RememberMachine);
|
||||||
|
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return RedirectToLocal(returnUrl);
|
||||||
|
}
|
||||||
|
else if (result.IsLockedOut)
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(Lockout));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null)
|
||||||
|
{
|
||||||
|
// Ensure the user has gone through the username & password screen first
|
||||||
|
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewData["ReturnUrl"] = returnUrl;
|
||||||
|
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null)
|
||||||
|
{
|
||||||
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
return View(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty);
|
||||||
|
|
||||||
|
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
|
||||||
|
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return RedirectToLocal(returnUrl);
|
||||||
|
}
|
||||||
|
if (result.IsLockedOut)
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(Lockout));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ModelState.AddModelError(string.Empty, "Invalid recovery code entered.");
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Lockout()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public IActionResult Banned()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show logout page
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Logout(string logoutId)
|
||||||
|
{
|
||||||
|
// build a model so the logout page knows what to display
|
||||||
|
var vm = await _account.BuildLogoutViewModelAsync(logoutId);
|
||||||
|
|
||||||
|
if (vm.ShowLogoutPrompt == false)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle logout page postback
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Logout(LogoutInputModel model)
|
||||||
|
{
|
||||||
|
// get context information (client name, post logout redirect URI and iframe for federated signout)
|
||||||
|
var vm = await _account.BuildLoggedOutViewModelAsync(model.LogoutId);
|
||||||
|
|
||||||
|
if (User?.Identity.IsAuthenticated == true)
|
||||||
|
{
|
||||||
|
await _signInManager.SignOutAsync();
|
||||||
|
|
||||||
|
// raise the logout event
|
||||||
|
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("LoggedOut", vm);
|
||||||
|
return RedirectToLocal(model.ReturnURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IActionResult RedirectToLocal(string returnUrl)
|
||||||
|
{
|
||||||
|
if (Url.IsLocalUrl(returnUrl))
|
||||||
|
{
|
||||||
|
return Redirect(returnUrl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return RedirectToAction(nameof(HomeController.Index), "Home");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
IdentityServer/Controllers/ConsentController.cs
Normal file
75
IdentityServer/Controllers/ConsentController.cs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
using IdentityServer4.Services;
|
||||||
|
using IdentityServer4.Stores;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
using Teknik.IdentityServer.Security;
|
||||||
|
using Teknik.IdentityServer.Services;
|
||||||
|
using Teknik.Logging;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Controllers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This controller processes the consent UI
|
||||||
|
/// </summary>
|
||||||
|
public class ConsentController : DefaultController
|
||||||
|
{
|
||||||
|
private readonly ConsentService _consent;
|
||||||
|
|
||||||
|
public ConsentController(
|
||||||
|
ILogger<Logger> logger,
|
||||||
|
Config config,
|
||||||
|
IIdentityServerInteractionService interaction,
|
||||||
|
IClientStore clientStore,
|
||||||
|
IResourceStore resourceStore) : base(logger, config)
|
||||||
|
{
|
||||||
|
_consent = new ConsentService(interaction, clientStore, resourceStore, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the consent screen
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="returnUrl"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Index(string returnUrl)
|
||||||
|
{
|
||||||
|
var vm = await _consent.BuildViewModelAsync(returnUrl);
|
||||||
|
if (vm != null)
|
||||||
|
{
|
||||||
|
return View("Index", vm);
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles the consent screen postback
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Index(ConsentInputModel model)
|
||||||
|
{
|
||||||
|
var result = await _consent.ProcessConsent(model);
|
||||||
|
|
||||||
|
if (result.IsRedirect)
|
||||||
|
{
|
||||||
|
return Redirect(result.RedirectUri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.HasValidationError)
|
||||||
|
{
|
||||||
|
ModelState.AddModelError("", result.ValidationError);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.ShowView)
|
||||||
|
{
|
||||||
|
return View("Index", result.ViewModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
return View("Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
64
IdentityServer/Controllers/DefaultController.cs
Normal file
64
IdentityServer/Controllers/DefaultController.cs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.Logging;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Controllers
|
||||||
|
{
|
||||||
|
public class DefaultController : Controller
|
||||||
|
{
|
||||||
|
protected readonly ILogger<Logger> _logger;
|
||||||
|
protected readonly Config _config;
|
||||||
|
|
||||||
|
public DefaultController(ILogger<Logger> logger, Config config)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_config = config;
|
||||||
|
|
||||||
|
ViewBag.Title = "Teknik Authentication";
|
||||||
|
ViewBag.Description = "Teknik Authentication Service";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Favicon
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ResponseCache(Duration = 31536000, Location = ResponseCacheLocation.Any)]
|
||||||
|
public IActionResult Favicon([FromServices] IHostingEnvironment env)
|
||||||
|
{
|
||||||
|
string imageFile = FileHelper.MapPath(env, Constants.FAVICON_PATH);
|
||||||
|
FileStream fs = new FileStream(imageFile, FileMode.Open, FileAccess.Read);
|
||||||
|
return File(fs, "image/x-icon");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the Robots.txt
|
||||||
|
[HttpGet]
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult Robots([FromServices] IHostingEnvironment env)
|
||||||
|
{
|
||||||
|
//string file = FileHelper.MapPath(env, Constants.ROBOTS_PATH);
|
||||||
|
return File(Constants.ROBOTS_PATH, "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IActionResult GenerateActionResult(object json)
|
||||||
|
{
|
||||||
|
return GenerateActionResult(json, View());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected IActionResult GenerateActionResult(object json, IActionResult result)
|
||||||
|
{
|
||||||
|
if (Request.IsAjaxRequest())
|
||||||
|
{
|
||||||
|
return Json(json);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
237
IdentityServer/Controllers/ErrorController.cs
Normal file
237
IdentityServer/Controllers/ErrorController.cs
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
using IdentityServer4.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Mail;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.IdentityServer.ViewModels;
|
||||||
|
using Teknik.Logging;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Controllers
|
||||||
|
{
|
||||||
|
public class ErrorController : DefaultController
|
||||||
|
{
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
|
||||||
|
public ErrorController(ILogger<Logger> logger, Config config, IIdentityServerInteractionService interaction) : base(logger, config)
|
||||||
|
{
|
||||||
|
_interaction = interaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult HttpError(int statusCode)
|
||||||
|
{
|
||||||
|
switch (statusCode)
|
||||||
|
{
|
||||||
|
case 401:
|
||||||
|
return Http401();
|
||||||
|
case 403:
|
||||||
|
return Http403();
|
||||||
|
case 404:
|
||||||
|
return Http404();
|
||||||
|
default:
|
||||||
|
return HttpGeneral(statusCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult HttpGeneral(int statusCode)
|
||||||
|
{
|
||||||
|
ViewBag.Title = statusCode + " - " + _config.Title;
|
||||||
|
|
||||||
|
LogError(LogLevel.Error, "HTTP Error Code: " + statusCode);
|
||||||
|
|
||||||
|
ErrorViewModel model = new ErrorViewModel();
|
||||||
|
model.StatusCode = statusCode;
|
||||||
|
|
||||||
|
return GenerateActionResult(CreateErrorObj("Http", statusCode, "Invalid HTTP Response"), View("~/Views/Error/HttpGeneral.cshtml", model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult Http401()
|
||||||
|
{
|
||||||
|
Response.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
|
|
||||||
|
ViewBag.Title = "401 - " + _config.Title;
|
||||||
|
ViewBag.Description = "Unauthorized";
|
||||||
|
|
||||||
|
LogError(LogLevel.Error, "Unauthorized");
|
||||||
|
|
||||||
|
ErrorViewModel model = new ErrorViewModel();
|
||||||
|
model.StatusCode = StatusCodes.Status401Unauthorized;
|
||||||
|
|
||||||
|
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status401Unauthorized, "Unauthorized"), View("~/Views/Error/Http401.cshtml", model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult Http403()
|
||||||
|
{
|
||||||
|
Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||||
|
|
||||||
|
ViewBag.Title = "403 - " + _config.Title;
|
||||||
|
ViewBag.Description = "Access Denied";
|
||||||
|
|
||||||
|
LogError(LogLevel.Error, "Access Denied");
|
||||||
|
|
||||||
|
ErrorViewModel model = new ErrorViewModel();
|
||||||
|
model.StatusCode = StatusCodes.Status403Forbidden;
|
||||||
|
|
||||||
|
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status403Forbidden, "Access Denied"), View("~/Views/Error/Http403.cshtml", model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult Http404()
|
||||||
|
{
|
||||||
|
Response.StatusCode = StatusCodes.Status404NotFound;
|
||||||
|
|
||||||
|
ViewBag.Title = "404 - " + _config.Title;
|
||||||
|
ViewBag.Description = "Uh Oh, can't find it!";
|
||||||
|
|
||||||
|
LogError(LogLevel.Warning, "Page Not Found");
|
||||||
|
|
||||||
|
ErrorViewModel model = new ErrorViewModel();
|
||||||
|
model.StatusCode = StatusCodes.Status404NotFound;
|
||||||
|
|
||||||
|
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status404NotFound, "Page Not Found"), View("~/Views/Error/Http404.cshtml", model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
public IActionResult Http500(Exception exception)
|
||||||
|
{
|
||||||
|
if (HttpContext != null)
|
||||||
|
{
|
||||||
|
var ex = HttpContext.Features.Get<IExceptionHandlerFeature>();
|
||||||
|
if (ex != null)
|
||||||
|
{
|
||||||
|
exception = ex.Error;
|
||||||
|
}
|
||||||
|
HttpContext.Session.Set("Exception", exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||||
|
|
||||||
|
ViewBag.Title = "500 - " + _config.Title;
|
||||||
|
ViewBag.Description = "Something Borked";
|
||||||
|
|
||||||
|
LogError(LogLevel.Error, "Server Error", exception);
|
||||||
|
|
||||||
|
ErrorViewModel model = new ErrorViewModel();
|
||||||
|
model.StatusCode = StatusCodes.Status500InternalServerError;
|
||||||
|
model.Exception = exception;
|
||||||
|
|
||||||
|
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status500InternalServerError, exception.Message), View("~/Views/Error/Http500.cshtml", model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[AllowAnonymous]
|
||||||
|
public async Task<IActionResult> IdentityError(string errorId)
|
||||||
|
{
|
||||||
|
var message = await _interaction.GetErrorContextAsync(errorId);
|
||||||
|
|
||||||
|
Response.StatusCode = StatusCodes.Status500InternalServerError;
|
||||||
|
|
||||||
|
ViewBag.Title = "Identity Error - " + _config.Title;
|
||||||
|
ViewBag.Description = "The Identity Service threw an error";
|
||||||
|
|
||||||
|
LogError(LogLevel.Error, "Identity Error: " + message.Error);
|
||||||
|
|
||||||
|
IdentityErrorViewModel model = new IdentityErrorViewModel();
|
||||||
|
model.Title = message.Error;
|
||||||
|
model.Description = message.ErrorDescription;
|
||||||
|
|
||||||
|
return GenerateActionResult(CreateErrorObj("Http", StatusCodes.Status500InternalServerError, message.Error), View("~/Views/Error/IdentityError.cshtml", model));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
[AllowAnonymous]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public IActionResult SubmitErrorReport(SubmitReportViewModel model)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string exceptionMsg = model.Exception;
|
||||||
|
|
||||||
|
// Try to grab the actual exception that occured
|
||||||
|
Exception ex = HttpContext.Session.Get<Exception>("Exception");
|
||||||
|
if (ex != null)
|
||||||
|
{
|
||||||
|
exceptionMsg = string.Format(@"
|
||||||
|
Exception: {0}
|
||||||
|
|
||||||
|
Source: {1}
|
||||||
|
|
||||||
|
Stack Trace:
|
||||||
|
|
||||||
|
{2}
|
||||||
|
", ex.GetFullMessage(true), ex.Source, ex.StackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let's also email the message to support
|
||||||
|
SmtpClient client = new SmtpClient();
|
||||||
|
client.Host = _config.ContactConfig.EmailAccount.Host;
|
||||||
|
client.Port = _config.ContactConfig.EmailAccount.Port;
|
||||||
|
client.EnableSsl = _config.ContactConfig.EmailAccount.SSL;
|
||||||
|
client.DeliveryMethod = SmtpDeliveryMethod.Network;
|
||||||
|
client.UseDefaultCredentials = true;
|
||||||
|
client.Credentials = new System.Net.NetworkCredential(_config.ContactConfig.EmailAccount.Username, _config.ContactConfig.EmailAccount.Password);
|
||||||
|
client.Timeout = 5000;
|
||||||
|
|
||||||
|
MailMessage mail = new MailMessage(new MailAddress(_config.NoReplyEmail, _config.NoReplyEmail), new MailAddress(_config.SupportEmail, "Teknik Support"));
|
||||||
|
mail.Sender = new MailAddress(_config.ContactConfig.EmailAccount.EmailAddress);
|
||||||
|
mail.Subject = "[Exception] Application Exception Occured";
|
||||||
|
mail.Body = @"
|
||||||
|
An exception has occured at: " + model.CurrentUrl + @"
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
User Message:
|
||||||
|
|
||||||
|
" + model.Message + @"
|
||||||
|
|
||||||
|
----------------------------------------
|
||||||
|
" + exceptionMsg;
|
||||||
|
mail.BodyEncoding = UTF8Encoding.UTF8;
|
||||||
|
mail.DeliveryNotificationOptions = DeliveryNotificationOptions.Never;
|
||||||
|
|
||||||
|
client.Send(mail);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Json(new { error = "Error submitting report. Exception: " + ex.Message });
|
||||||
|
}
|
||||||
|
|
||||||
|
return Json(new { result = "true" });
|
||||||
|
}
|
||||||
|
|
||||||
|
private object CreateErrorObj(string type, int statusCode, string message)
|
||||||
|
{
|
||||||
|
return new { error = new { type = type, status = statusCode, message = message } };
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogError(LogLevel level, string message)
|
||||||
|
{
|
||||||
|
LogError(level, message, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LogError(LogLevel level, string message, Exception exception)
|
||||||
|
{
|
||||||
|
if (Request != null)
|
||||||
|
{
|
||||||
|
message += " | Url: " + Request.GetDisplayUrl();
|
||||||
|
|
||||||
|
message += " | Referred Url: " + Request.Headers["Referer"].ToString();
|
||||||
|
|
||||||
|
message += " | Method: " + Request.Method;
|
||||||
|
|
||||||
|
message += " | User Agent: " + Request.Headers["User-Agent"].ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Log(level, message, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
92
IdentityServer/Controllers/GrantsController.cs
Normal file
92
IdentityServer/Controllers/GrantsController.cs
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
using IdentityServer4.Services;
|
||||||
|
using IdentityServer4.Stores;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Teknik.IdentityServer.Security;
|
||||||
|
using Teknik.IdentityServer.ViewModels;
|
||||||
|
using Teknik.Logging;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Controllers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This sample controller allows a user to revoke grants given to clients
|
||||||
|
/// </summary>
|
||||||
|
[Authorize(AuthenticationSchemes = "Identity.Application")]
|
||||||
|
public class GrantsController : DefaultController
|
||||||
|
{
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
private readonly IClientStore _clients;
|
||||||
|
private readonly IResourceStore _resources;
|
||||||
|
|
||||||
|
public GrantsController(
|
||||||
|
ILogger<Logger> logger,
|
||||||
|
Config config,
|
||||||
|
IIdentityServerInteractionService interaction,
|
||||||
|
IClientStore clients,
|
||||||
|
IResourceStore resources) : base(logger, config)
|
||||||
|
{
|
||||||
|
_interaction = interaction;
|
||||||
|
_clients = clients;
|
||||||
|
_resources = resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Show list of grants
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Index()
|
||||||
|
{
|
||||||
|
return View("Index", await BuildViewModelAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handle postback to revoke a client
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost]
|
||||||
|
[ValidateAntiForgeryToken]
|
||||||
|
public async Task<IActionResult> Revoke(string clientId)
|
||||||
|
{
|
||||||
|
await _interaction.RevokeUserConsentAsync(clientId);
|
||||||
|
return RedirectToAction("Index");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<GrantsViewModel> BuildViewModelAsync()
|
||||||
|
{
|
||||||
|
var grants = await _interaction.GetAllUserConsentsAsync();
|
||||||
|
|
||||||
|
var list = new List<GrantViewModel>();
|
||||||
|
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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
IdentityServer/Controllers/HomeController.cs
Normal file
29
IdentityServer/Controllers/HomeController.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using IdentityServer4.Services;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.IdentityServer.Security;
|
||||||
|
using Teknik.IdentityServer.ViewModels;
|
||||||
|
using Teknik.Logging;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Controllers
|
||||||
|
{
|
||||||
|
public class HomeController : DefaultController
|
||||||
|
{
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
|
||||||
|
public HomeController(
|
||||||
|
ILogger<Logger> logger,
|
||||||
|
Config config,
|
||||||
|
IIdentityServerInteractionService interaction) : base(logger, config)
|
||||||
|
{
|
||||||
|
_interaction = interaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IActionResult Index()
|
||||||
|
{
|
||||||
|
return View();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
420
IdentityServer/Controllers/ManageController.cs
Normal file
420
IdentityServer/Controllers/ManageController.cs
Normal file
@ -0,0 +1,420 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
using Teknik.IdentityServer.Models.Manage;
|
||||||
|
using Teknik.Logging;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Controllers
|
||||||
|
{
|
||||||
|
[Authorize(Policy = "Internal", AuthenticationSchemes = "Bearer")]
|
||||||
|
[Route("[controller]/[action]")]
|
||||||
|
[ApiController]
|
||||||
|
public class ManageController : DefaultController
|
||||||
|
{
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||||
|
|
||||||
|
public ManageController(
|
||||||
|
ILogger<Logger> logger,
|
||||||
|
Config config,
|
||||||
|
UserManager<ApplicationUser> userManager,
|
||||||
|
SignInManager<ApplicationUser> signInManager) : base(logger, config)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_signInManager = signInManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> CreateUser(NewUserModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.Password))
|
||||||
|
return new JsonResult(new { success = false, message = "Password is required" });
|
||||||
|
|
||||||
|
var identityUser = new ApplicationUser(model.Username)
|
||||||
|
{
|
||||||
|
Id = Guid.NewGuid().ToString(),
|
||||||
|
UserName = model.Username,
|
||||||
|
AccountStatus = model.AccountStatus,
|
||||||
|
AccountType = model.AccountType,
|
||||||
|
Email = model.RecoveryEmail,
|
||||||
|
EmailConfirmed = model.RecoveryVerified,
|
||||||
|
PGPPublicKey = model.PGPPublicKey
|
||||||
|
};
|
||||||
|
var result = await _userManager.CreateAsync(identityUser, model.Password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to create user.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> DeleteUser(DeleteUserModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
var result = await _userManager.DeleteAsync(foundUser);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to delete user.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> UserExists(string username)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(username);
|
||||||
|
return new JsonResult(new { success = true, data = foundUser != null });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> GetUserInfo(string username)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
return new JsonResult(new { success = true, data = foundUser.ToJson() });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> CheckPassword(CheckPasswordModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.Password))
|
||||||
|
return new JsonResult(new { success = false, message = "Password is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
bool valid = await _userManager.CheckPasswordAsync(foundUser, model.Password);
|
||||||
|
return new JsonResult(new { success = true, data = valid });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> GeneratePasswordResetToken(GeneratePasswordResetTokenModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
string token = await _userManager.GeneratePasswordResetTokenAsync(foundUser);
|
||||||
|
return new JsonResult(new { success = true, data = token });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> ResetPassword(ResetPasswordModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.Token))
|
||||||
|
return new JsonResult(new { success = false, message = "Token is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.Password))
|
||||||
|
return new JsonResult(new { success = false, message = "Password is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
var result = await _userManager.ResetPasswordAsync(foundUser, model.Token, model.Password);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to reset password.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> UpdatePassword(UpdatePasswordModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.CurrentPassword))
|
||||||
|
return new JsonResult(new { success = false, message = "Current Password is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.NewPassword))
|
||||||
|
return new JsonResult(new { success = false, message = "New Password is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
var result = await _userManager.ChangePasswordAsync(foundUser, model.CurrentPassword, model.NewPassword);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to update password.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> UpdateEmail(UpdateEmailModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
var result = await _userManager.SetEmailAsync(foundUser, model.Email);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
var token = await _userManager.GenerateEmailConfirmationTokenAsync(foundUser);
|
||||||
|
return new JsonResult(new { success = true, data = token });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to update email address.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> VerifyEmail(VerifyEmailModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.Token))
|
||||||
|
return new JsonResult(new { success = false, message = "Token is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
var result = await _userManager.ConfirmEmailAsync(foundUser, model.Token);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to verify email address.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> UpdateAccountStatus(UpdateAccountStatusModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
foundUser.AccountStatus = model.AccountStatus;
|
||||||
|
|
||||||
|
var result = await _userManager.UpdateAsync(foundUser);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to update account status.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> UpdateAccountType(UpdateAccountTypeModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
foundUser.AccountType = model.AccountType;
|
||||||
|
|
||||||
|
var result = await _userManager.UpdateAsync(foundUser);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to update account type.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> UpdatePGPPublicKey(UpdatePGPPublicKeyModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
foundUser.PGPPublicKey = model.PGPPublicKey;
|
||||||
|
|
||||||
|
var result = await _userManager.UpdateAsync(foundUser);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to update pgp public key.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
public async Task<IActionResult> Get2FAKey(string username)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser);
|
||||||
|
|
||||||
|
return new JsonResult(new { success = true, data = FormatKey(unformattedKey) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Reset2FAKey(Reset2FAKeyModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
await _userManager.ResetAuthenticatorKeyAsync(foundUser);
|
||||||
|
string unformattedKey = await _userManager.GetAuthenticatorKeyAsync(foundUser);
|
||||||
|
|
||||||
|
return new JsonResult(new { success = true, data = FormatKey(unformattedKey) });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Enable2FA(Enable2FAModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
if (string.IsNullOrEmpty(model.Code))
|
||||||
|
return new JsonResult(new { success = false, message = "Code is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
// Strip spaces and hypens
|
||||||
|
var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
|
||||||
|
|
||||||
|
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
|
||||||
|
foundUser, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);
|
||||||
|
|
||||||
|
if (is2faTokenValid)
|
||||||
|
{
|
||||||
|
var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, true);
|
||||||
|
if (result.Succeeded)
|
||||||
|
{
|
||||||
|
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10);
|
||||||
|
return new JsonResult(new { success = true, data = recoveryCodes.ToArray() });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to set Two-Factor Authentication.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "Verification code is invalid." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> Disable2FA(Disable2FAModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
var result = await _userManager.SetTwoFactorEnabledAsync(foundUser, false);
|
||||||
|
if (result.Succeeded)
|
||||||
|
return new JsonResult(new { success = true });
|
||||||
|
else
|
||||||
|
return new JsonResult(new { success = false, message = "Unable to disable Two-Factor Authentication.", identityErrors = result.Errors });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpPost]
|
||||||
|
public async Task<IActionResult> GenerateRecoveryCodes(GenerateRecoveryCodesModel model)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(model.Username))
|
||||||
|
return new JsonResult(new { success = false, message = "Username is required" });
|
||||||
|
|
||||||
|
var foundUser = await _userManager.FindByNameAsync(model.Username);
|
||||||
|
if (foundUser != null)
|
||||||
|
{
|
||||||
|
if (foundUser.TwoFactorEnabled)
|
||||||
|
{
|
||||||
|
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(foundUser, 10);
|
||||||
|
|
||||||
|
return new JsonResult(new { success = true, data = recoveryCodes.ToArray() });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "Two-Factor Authentication is not enabled." });
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonResult(new { success = false, message = "User does not exist." });
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatKey(string unformattedKey)
|
||||||
|
{
|
||||||
|
var result = new StringBuilder();
|
||||||
|
int currentPosition = 0;
|
||||||
|
while (currentPosition + 4 < unformattedKey.Length)
|
||||||
|
{
|
||||||
|
result.Append(unformattedKey.Substring(currentPosition, 4)).Append(" ");
|
||||||
|
currentPosition += 4;
|
||||||
|
}
|
||||||
|
if (currentPosition < unformattedKey.Length)
|
||||||
|
{
|
||||||
|
result.Append(unformattedKey.Substring(currentPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString().ToLowerInvariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,242 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Teknik.IdentityServer;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20181015060219_InitialApplicationDbContextMigration")]
|
||||||
|
partial class InitialApplicationDbContextMigration
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Teknik.IdentityServer.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<int>("AccountStatus");
|
||||||
|
|
||||||
|
b.Property<int>("AccountType");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationDate");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeen");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PGPPublicKey");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,225 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
|
||||||
|
{
|
||||||
|
public partial class InitialApplicationDbContextMigration : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(nullable: false),
|
||||||
|
Name = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUsers",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(nullable: false),
|
||||||
|
UserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
Email = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
|
||||||
|
EmailConfirmed = table.Column<bool>(nullable: false),
|
||||||
|
PasswordHash = table.Column<string>(nullable: true),
|
||||||
|
SecurityStamp = table.Column<string>(nullable: true),
|
||||||
|
ConcurrencyStamp = table.Column<string>(nullable: true),
|
||||||
|
PhoneNumber = table.Column<string>(nullable: true),
|
||||||
|
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
|
||||||
|
TwoFactorEnabled = table.Column<bool>(nullable: false),
|
||||||
|
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
|
||||||
|
LockoutEnabled = table.Column<bool>(nullable: false),
|
||||||
|
AccessFailedCount = table.Column<int>(nullable: false),
|
||||||
|
CreationDate = table.Column<DateTime>(nullable: false),
|
||||||
|
LastSeen = table.Column<DateTime>(nullable: false),
|
||||||
|
AccountType = table.Column<int>(nullable: false),
|
||||||
|
AccountStatus = table.Column<int>(nullable: false),
|
||||||
|
PGPPublicKey = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetRoleClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
RoleId = table.Column<string>(nullable: false),
|
||||||
|
ClaimType = table.Column<string>(nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
UserId = table.Column<string>(nullable: false),
|
||||||
|
ClaimType = table.Column<string>(nullable: true),
|
||||||
|
ClaimValue = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserLogins",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
LoginProvider = table.Column<string>(nullable: false),
|
||||||
|
ProviderKey = table.Column<string>(nullable: false),
|
||||||
|
ProviderDisplayName = table.Column<string>(nullable: true),
|
||||||
|
UserId = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserRoles",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(nullable: false),
|
||||||
|
RoleId = table.Column<string>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
|
||||||
|
column: x => x.RoleId,
|
||||||
|
principalTable: "AspNetRoles",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "AspNetUserTokens",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
UserId = table.Column<string>(nullable: false),
|
||||||
|
LoginProvider = table.Column<string>(nullable: false),
|
||||||
|
Name = table.Column<string>(nullable: false),
|
||||||
|
Value = table.Column<string>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalTable: "AspNetUsers",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetRoleClaims_RoleId",
|
||||||
|
table: "AspNetRoleClaims",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "RoleNameIndex",
|
||||||
|
table: "AspNetRoles",
|
||||||
|
column: "NormalizedName",
|
||||||
|
unique: true,
|
||||||
|
filter: "[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserClaims_UserId",
|
||||||
|
table: "AspNetUserClaims",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserLogins_UserId",
|
||||||
|
table: "AspNetUserLogins",
|
||||||
|
column: "UserId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_AspNetUserRoles_RoleId",
|
||||||
|
table: "AspNetUserRoles",
|
||||||
|
column: "RoleId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "EmailIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedEmail");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "UserNameIndex",
|
||||||
|
table: "AspNetUsers",
|
||||||
|
column: "NormalizedUserName",
|
||||||
|
unique: true,
|
||||||
|
filter: "[NormalizedUserName] IS NOT NULL");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoleClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserLogins");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUserTokens");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetRoles");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "AspNetUsers");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,240 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Teknik.IdentityServer;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.ApplicationDb
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex")
|
||||||
|
.HasFilter("[NormalizedName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Teknik.IdentityServer.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<int>("AccountStatus");
|
||||||
|
|
||||||
|
b.Property<int>("AccountType");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationDate");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeen");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PGPPublicKey");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex")
|
||||||
|
.HasFilter("[NormalizedUserName] IS NOT NULL");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Teknik.IdentityServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,679 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using IdentityServer4.EntityFramework.DbContexts;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ConfigurationDbContext))]
|
||||||
|
[Migration("20180930040544_InitialIdentityServerConfigurationDbMigration")]
|
||||||
|
partial class InitialIdentityServerConfigurationDbMigration
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastAccessed");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Updated");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ApiResources");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.ToTable("ApiClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.ToTable("ApiProperties");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Emphasize");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Required");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowInDiscoveryDocument");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ApiScopes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiScopeId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiScopeId");
|
||||||
|
|
||||||
|
b.ToTable("ApiScopeClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(4000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.ToTable("ApiSecrets");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("AbsoluteRefreshTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<int>("AccessTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<int>("AccessTokenType");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowAccessTokensViaBrowser");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowOfflineAccess");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowPlainTextPkce");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowRememberConsent");
|
||||||
|
|
||||||
|
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken");
|
||||||
|
|
||||||
|
b.Property<bool>("AlwaysSendClientClaims");
|
||||||
|
|
||||||
|
b.Property<int>("AuthorizationCodeLifetime");
|
||||||
|
|
||||||
|
b.Property<bool>("BackChannelLogoutSessionRequired");
|
||||||
|
|
||||||
|
b.Property<string>("BackChannelLogoutUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<string>("ClientClaimsPrefix")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<int?>("ConsentLifetime");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<bool>("EnableLocalLogin");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<bool>("FrontChannelLogoutSessionRequired");
|
||||||
|
|
||||||
|
b.Property<string>("FrontChannelLogoutUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<int>("IdentityTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<bool>("IncludeJwtId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastAccessed");
|
||||||
|
|
||||||
|
b.Property<string>("LogoUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<string>("PairWiseSubjectSalt")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ProtocolType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<int>("RefreshTokenExpiration");
|
||||||
|
|
||||||
|
b.Property<int>("RefreshTokenUsage");
|
||||||
|
|
||||||
|
b.Property<bool>("RequireClientSecret");
|
||||||
|
|
||||||
|
b.Property<bool>("RequireConsent");
|
||||||
|
|
||||||
|
b.Property<bool>("RequirePkce");
|
||||||
|
|
||||||
|
b.Property<int>("SlidingRefreshTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Updated");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Clients");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Origin")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(150);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientCorsOrigins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("GrantType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientGrantTypes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientIdPRestrictions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("PostLogoutRedirectUri")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientPostLogoutRedirectUris");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientProperties");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("RedirectUri")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientRedirectUris");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Scope")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientScopes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(4000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientSecrets");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("IdentityResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IdentityResourceId");
|
||||||
|
|
||||||
|
b.ToTable("IdentityClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Emphasize");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Required");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowInDiscoveryDocument");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Updated");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("IdentityResources");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("IdentityResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IdentityResourceId");
|
||||||
|
|
||||||
|
b.ToTable("IdentityProperties");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("Properties")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("Scopes")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("ApiScopeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("Secrets")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("Claims")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("AllowedCorsOrigins")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("AllowedGrantTypes")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("IdentityProviderRestrictions")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("PostLogoutRedirectUris")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("Properties")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("RedirectUris")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("AllowedScopes")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("ClientSecrets")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("IdentityResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||||
|
.WithMany("Properties")
|
||||||
|
.HasForeignKey("IdentityResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,602 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
|
||||||
|
{
|
||||||
|
public partial class InitialIdentityServerConfigurationDbMigration : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApiResources",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Enabled = table.Column<bool>(nullable: false),
|
||||||
|
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||||
|
Created = table.Column<DateTime>(nullable: false),
|
||||||
|
Updated = table.Column<DateTime>(nullable: true),
|
||||||
|
LastAccessed = table.Column<DateTime>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApiResources", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Clients",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Enabled = table.Column<bool>(nullable: false),
|
||||||
|
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
ProtocolType = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
RequireClientSecret = table.Column<bool>(nullable: false),
|
||||||
|
ClientName = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||||
|
ClientUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||||
|
LogoUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||||
|
RequireConsent = table.Column<bool>(nullable: false),
|
||||||
|
AllowRememberConsent = table.Column<bool>(nullable: false),
|
||||||
|
AlwaysIncludeUserClaimsInIdToken = table.Column<bool>(nullable: false),
|
||||||
|
RequirePkce = table.Column<bool>(nullable: false),
|
||||||
|
AllowPlainTextPkce = table.Column<bool>(nullable: false),
|
||||||
|
AllowAccessTokensViaBrowser = table.Column<bool>(nullable: false),
|
||||||
|
FrontChannelLogoutUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||||
|
FrontChannelLogoutSessionRequired = table.Column<bool>(nullable: false),
|
||||||
|
BackChannelLogoutUri = table.Column<string>(maxLength: 2000, nullable: true),
|
||||||
|
BackChannelLogoutSessionRequired = table.Column<bool>(nullable: false),
|
||||||
|
AllowOfflineAccess = table.Column<bool>(nullable: false),
|
||||||
|
IdentityTokenLifetime = table.Column<int>(nullable: false),
|
||||||
|
AccessTokenLifetime = table.Column<int>(nullable: false),
|
||||||
|
AuthorizationCodeLifetime = table.Column<int>(nullable: false),
|
||||||
|
ConsentLifetime = table.Column<int>(nullable: true),
|
||||||
|
AbsoluteRefreshTokenLifetime = table.Column<int>(nullable: false),
|
||||||
|
SlidingRefreshTokenLifetime = table.Column<int>(nullable: false),
|
||||||
|
RefreshTokenUsage = table.Column<int>(nullable: false),
|
||||||
|
UpdateAccessTokenClaimsOnRefresh = table.Column<bool>(nullable: false),
|
||||||
|
RefreshTokenExpiration = table.Column<int>(nullable: false),
|
||||||
|
AccessTokenType = table.Column<int>(nullable: false),
|
||||||
|
EnableLocalLogin = table.Column<bool>(nullable: false),
|
||||||
|
IncludeJwtId = table.Column<bool>(nullable: false),
|
||||||
|
AlwaysSendClientClaims = table.Column<bool>(nullable: false),
|
||||||
|
ClientClaimsPrefix = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
PairWiseSubjectSalt = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
Created = table.Column<DateTime>(nullable: false),
|
||||||
|
Updated = table.Column<DateTime>(nullable: true),
|
||||||
|
LastAccessed = table.Column<DateTime>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Clients", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "IdentityResources",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Enabled = table.Column<bool>(nullable: false),
|
||||||
|
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||||
|
Required = table.Column<bool>(nullable: false),
|
||||||
|
Emphasize = table.Column<bool>(nullable: false),
|
||||||
|
ShowInDiscoveryDocument = table.Column<bool>(nullable: false),
|
||||||
|
Created = table.Column<DateTime>(nullable: false),
|
||||||
|
Updated = table.Column<DateTime>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityResources", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApiClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
ApiResourceId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApiClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ApiClaims_ApiResources_ApiResourceId",
|
||||||
|
column: x => x.ApiResourceId,
|
||||||
|
principalTable: "ApiResources",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApiProperties",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||||
|
ApiResourceId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApiProperties", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ApiProperties_ApiResources_ApiResourceId",
|
||||||
|
column: x => x.ApiResourceId,
|
||||||
|
principalTable: "ApiResources",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApiScopes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Name = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
DisplayName = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||||
|
Required = table.Column<bool>(nullable: false),
|
||||||
|
Emphasize = table.Column<bool>(nullable: false),
|
||||||
|
ShowInDiscoveryDocument = table.Column<bool>(nullable: false),
|
||||||
|
ApiResourceId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApiScopes", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ApiScopes_ApiResources_ApiResourceId",
|
||||||
|
column: x => x.ApiResourceId,
|
||||||
|
principalTable: "ApiResources",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApiSecrets",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Description = table.Column<string>(maxLength: 1000, nullable: true),
|
||||||
|
Value = table.Column<string>(maxLength: 4000, nullable: false),
|
||||||
|
Expiration = table.Column<DateTime>(nullable: true),
|
||||||
|
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
Created = table.Column<DateTime>(nullable: false),
|
||||||
|
ApiResourceId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApiSecrets", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ApiSecrets_ApiResources_ApiResourceId",
|
||||||
|
column: x => x.ApiResourceId,
|
||||||
|
principalTable: "ApiResources",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
Value = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientClaims_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientCorsOrigins",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Origin = table.Column<string>(maxLength: 150, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientCorsOrigins_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientGrantTypes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
GrantType = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientGrantTypes", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientGrantTypes_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientIdPRestrictions",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Provider = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientIdPRestrictions_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientPostLogoutRedirectUris",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
PostLogoutRedirectUri = table.Column<string>(maxLength: 2000, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientProperties",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientProperties", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientProperties_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientRedirectUris",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
RedirectUri = table.Column<string>(maxLength: 2000, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientRedirectUris", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientRedirectUris_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientScopes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Scope = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientScopes", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientScopes_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ClientSecrets",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Description = table.Column<string>(maxLength: 2000, nullable: true),
|
||||||
|
Value = table.Column<string>(maxLength: 4000, nullable: false),
|
||||||
|
Expiration = table.Column<DateTime>(nullable: true),
|
||||||
|
Type = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
Created = table.Column<DateTime>(nullable: false),
|
||||||
|
ClientId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ClientSecrets", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ClientSecrets_Clients_ClientId",
|
||||||
|
column: x => x.ClientId,
|
||||||
|
principalTable: "Clients",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "IdentityClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
IdentityResourceId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_IdentityClaims_IdentityResources_IdentityResourceId",
|
||||||
|
column: x => x.IdentityResourceId,
|
||||||
|
principalTable: "IdentityResources",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "IdentityProperties",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Key = table.Column<string>(maxLength: 250, nullable: false),
|
||||||
|
Value = table.Column<string>(maxLength: 2000, nullable: false),
|
||||||
|
IdentityResourceId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_IdentityProperties", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_IdentityProperties_IdentityResources_IdentityResourceId",
|
||||||
|
column: x => x.IdentityResourceId,
|
||||||
|
principalTable: "IdentityResources",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "ApiScopeClaims",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
|
||||||
|
Type = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
ApiScopeId = table.Column<int>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ApiScopeClaims", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_ApiScopeClaims_ApiScopes_ApiScopeId",
|
||||||
|
column: x => x.ApiScopeId,
|
||||||
|
principalTable: "ApiScopes",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApiClaims_ApiResourceId",
|
||||||
|
table: "ApiClaims",
|
||||||
|
column: "ApiResourceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApiProperties_ApiResourceId",
|
||||||
|
table: "ApiProperties",
|
||||||
|
column: "ApiResourceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApiResources_Name",
|
||||||
|
table: "ApiResources",
|
||||||
|
column: "Name",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApiScopeClaims_ApiScopeId",
|
||||||
|
table: "ApiScopeClaims",
|
||||||
|
column: "ApiScopeId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApiScopes_ApiResourceId",
|
||||||
|
table: "ApiScopes",
|
||||||
|
column: "ApiResourceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApiScopes_Name",
|
||||||
|
table: "ApiScopes",
|
||||||
|
column: "Name",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ApiSecrets_ApiResourceId",
|
||||||
|
table: "ApiSecrets",
|
||||||
|
column: "ApiResourceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientClaims_ClientId",
|
||||||
|
table: "ClientClaims",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientCorsOrigins_ClientId",
|
||||||
|
table: "ClientCorsOrigins",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientGrantTypes_ClientId",
|
||||||
|
table: "ClientGrantTypes",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientIdPRestrictions_ClientId",
|
||||||
|
table: "ClientIdPRestrictions",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientPostLogoutRedirectUris_ClientId",
|
||||||
|
table: "ClientPostLogoutRedirectUris",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientProperties_ClientId",
|
||||||
|
table: "ClientProperties",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientRedirectUris_ClientId",
|
||||||
|
table: "ClientRedirectUris",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Clients_ClientId",
|
||||||
|
table: "Clients",
|
||||||
|
column: "ClientId",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientScopes_ClientId",
|
||||||
|
table: "ClientScopes",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_ClientSecrets_ClientId",
|
||||||
|
table: "ClientSecrets",
|
||||||
|
column: "ClientId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_IdentityClaims_IdentityResourceId",
|
||||||
|
table: "IdentityClaims",
|
||||||
|
column: "IdentityResourceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_IdentityProperties_IdentityResourceId",
|
||||||
|
table: "IdentityProperties",
|
||||||
|
column: "IdentityResourceId");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_IdentityResources_Name",
|
||||||
|
table: "IdentityResources",
|
||||||
|
column: "Name",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ApiClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ApiProperties");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ApiScopeClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ApiSecrets");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientCorsOrigins");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientGrantTypes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientIdPRestrictions");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientPostLogoutRedirectUris");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientProperties");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientRedirectUris");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientScopes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ClientSecrets");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "IdentityClaims");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "IdentityProperties");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ApiScopes");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Clients");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "IdentityResources");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ApiResources");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,677 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using IdentityServer4.EntityFramework.DbContexts;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ConfigurationDbContext))]
|
||||||
|
partial class ConfigurationDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResource", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastAccessed");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Updated");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ApiResources");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.ToTable("ApiClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.ToTable("ApiProperties");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Emphasize");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Required");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowInDiscoveryDocument");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("ApiScopes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiScopeId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiScopeId");
|
||||||
|
|
||||||
|
b.ToTable("ApiScopeClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ApiResourceId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(4000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ApiResourceId");
|
||||||
|
|
||||||
|
b.ToTable("ApiSecrets");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.Client", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("AbsoluteRefreshTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<int>("AccessTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<int>("AccessTokenType");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowAccessTokensViaBrowser");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowOfflineAccess");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowPlainTextPkce");
|
||||||
|
|
||||||
|
b.Property<bool>("AllowRememberConsent");
|
||||||
|
|
||||||
|
b.Property<bool>("AlwaysIncludeUserClaimsInIdToken");
|
||||||
|
|
||||||
|
b.Property<bool>("AlwaysSendClientClaims");
|
||||||
|
|
||||||
|
b.Property<int>("AuthorizationCodeLifetime");
|
||||||
|
|
||||||
|
b.Property<bool>("BackChannelLogoutSessionRequired");
|
||||||
|
|
||||||
|
b.Property<string>("BackChannelLogoutUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<string>("ClientClaimsPrefix")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<int?>("ConsentLifetime");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<bool>("EnableLocalLogin");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<bool>("FrontChannelLogoutSessionRequired");
|
||||||
|
|
||||||
|
b.Property<string>("FrontChannelLogoutUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<int>("IdentityTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<bool>("IncludeJwtId");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastAccessed");
|
||||||
|
|
||||||
|
b.Property<string>("LogoUri")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<string>("PairWiseSubjectSalt")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ProtocolType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<int>("RefreshTokenExpiration");
|
||||||
|
|
||||||
|
b.Property<int>("RefreshTokenUsage");
|
||||||
|
|
||||||
|
b.Property<bool>("RequireClientSecret");
|
||||||
|
|
||||||
|
b.Property<bool>("RequireConsent");
|
||||||
|
|
||||||
|
b.Property<bool>("RequirePkce");
|
||||||
|
|
||||||
|
b.Property<int>("SlidingRefreshTokenLifetime");
|
||||||
|
|
||||||
|
b.Property<bool>("UpdateAccessTokenClaimsOnRefresh");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Updated");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("Clients");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Origin")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(150);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientCorsOrigins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("GrantType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientGrantTypes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Provider")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientIdPRestrictions");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("PostLogoutRedirectUri")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientPostLogoutRedirectUris");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientProperties");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("RedirectUri")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientRedirectUris");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<string>("Scope")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientScopes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("ClientId");
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(4000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("ClientId");
|
||||||
|
|
||||||
|
b.ToTable("ClientSecrets");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("IdentityResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IdentityResourceId");
|
||||||
|
|
||||||
|
b.ToTable("IdentityClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResource", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<DateTime>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("Description")
|
||||||
|
.HasMaxLength(1000);
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Emphasize");
|
||||||
|
|
||||||
|
b.Property<bool>("Enabled");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<bool>("Required");
|
||||||
|
|
||||||
|
b.Property<bool>("ShowInDiscoveryDocument");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Updated");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Name")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("IdentityResources");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
b.Property<int>("IdentityResourceId");
|
||||||
|
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(250);
|
||||||
|
|
||||||
|
b.Property<string>("Value")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2000);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("IdentityResourceId");
|
||||||
|
|
||||||
|
b.ToTable("IdentityProperties");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("Properties")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScope", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("Scopes")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiScopeClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiScope", "ApiScope")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("ApiScopeId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ApiSecret", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.ApiResource", "ApiResource")
|
||||||
|
.WithMany("Secrets")
|
||||||
|
.HasForeignKey("ApiResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("Claims")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientCorsOrigin", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("AllowedCorsOrigins")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientGrantType", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("AllowedGrantTypes")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientIdPRestriction", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("IdentityProviderRestrictions")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientPostLogoutRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("PostLogoutRedirectUris")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientProperty", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("Properties")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientRedirectUri", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("RedirectUris")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientScope", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("AllowedScopes")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.ClientSecret", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.Client", "Client")
|
||||||
|
.WithMany("ClientSecrets")
|
||||||
|
.HasForeignKey("ClientId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityClaim", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||||
|
.WithMany("UserClaims")
|
||||||
|
.HasForeignKey("IdentityResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.IdentityResourceProperty", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("IdentityServer4.EntityFramework.Entities.IdentityResource", "IdentityResource")
|
||||||
|
.WithMany("Properties")
|
||||||
|
.HasForeignKey("IdentityResourceId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using IdentityServer4.EntityFramework.DbContexts;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PersistedGrantDbContext))]
|
||||||
|
[Migration("20180930040529_InitialIdentityServerPersistedGrantDbMigration")]
|
||||||
|
partial class InitialIdentityServerPersistedGrantDbMigration
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationTime");
|
||||||
|
|
||||||
|
b.Property<string>("Data")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50000);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("SubjectId")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.HasKey("Key");
|
||||||
|
|
||||||
|
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||||
|
|
||||||
|
b.ToTable("PersistedGrants");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
|
||||||
|
{
|
||||||
|
public partial class InitialIdentityServerPersistedGrantDbMigration : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PersistedGrants",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Key = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
Type = table.Column<string>(maxLength: 50, nullable: false),
|
||||||
|
SubjectId = table.Column<string>(maxLength: 200, nullable: true),
|
||||||
|
ClientId = table.Column<string>(maxLength: 200, nullable: false),
|
||||||
|
CreationTime = table.Column<DateTime>(nullable: false),
|
||||||
|
Expiration = table.Column<DateTime>(nullable: true),
|
||||||
|
Data = table.Column<string>(maxLength: 50000, nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PersistedGrants", x => x.Key);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PersistedGrants_SubjectId_ClientId_Type",
|
||||||
|
table: "PersistedGrants",
|
||||||
|
columns: new[] { "SubjectId", "ClientId", "Type" });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PersistedGrants");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using IdentityServer4.EntityFramework.DbContexts;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PersistedGrantDbContext))]
|
||||||
|
partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.2.0-preview2-35157")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||||
|
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||||
|
|
||||||
|
modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Key")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("ClientId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreationTime");
|
||||||
|
|
||||||
|
b.Property<string>("Data")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50000);
|
||||||
|
|
||||||
|
b.Property<DateTime?>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("SubjectId")
|
||||||
|
.HasMaxLength(200);
|
||||||
|
|
||||||
|
b.Property<string>("Type")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(50);
|
||||||
|
|
||||||
|
b.HasKey("Key");
|
||||||
|
|
||||||
|
b.HasIndex("SubjectId", "ClientId", "Type");
|
||||||
|
|
||||||
|
b.ToTable("PersistedGrants");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
IdentityServer/IdentityServer.csproj
Normal file
34
IdentityServer/IdentityServer.csproj
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<AssemblyName>Teknik.IdentityServer</AssemblyName>
|
||||||
|
<RootNamespace>Teknik.IdentityServer</RootNamespace>
|
||||||
|
<RuntimeIdentifiers>win-x86;win-x64;linux-x64;linux-arm;osx-x64</RuntimeIdentifiers>
|
||||||
|
<Configurations>Debug;Release;Test</Configurations>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Middleware\" />
|
||||||
|
<Folder Include="wwwroot\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="IdentityServer4" Version="2.3.0-preview1-update2" />
|
||||||
|
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="2.7.0-preview1" />
|
||||||
|
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="2.3.0-preview1-update2" />
|
||||||
|
<PackageReference Include="IdentityServer4.EntityFramework" Version="2.3.0-preview1-update1" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.All" Version="2.1.5" />
|
||||||
|
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.5" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Configuration\Configuration.csproj" />
|
||||||
|
<ProjectReference Include="..\Logging\Logging.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
BIN
IdentityServer/Images/favicon.ico
Normal file
BIN
IdentityServer/Images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
23
IdentityServer/Images/logo-black.svg
Normal file
23
IdentityServer/Images/logo-black.svg
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="263.292px" height="72.13px" viewBox="0 0 263.292 72.13" enable-background="new 0 0 263.292 72.13" xml:space="preserve">
|
||||||
|
<path d="M6.386,37.689c0-3.666-2.722-6.7-6.386-7.538V22.3h6.386V10.784h11.62V22.3h18.217v10.363h-22.09v3.561
|
||||||
|
c2.513,0.732,3.873,2.93,3.873,5.863v19.574h18.217V72.13H11.515c-2.616,0-5.129-2.197-5.129-5.234V37.689z"/>
|
||||||
|
<path d="M130.21,61.662l-15.599-16.121l12.04-12.877h7.118V22.3h-8.689c-2.512,0-4.083,0.942-5.129,1.989l-15.39,16.854h-2.407V5.13
|
||||||
|
c0-2.512-2.199-5.13-5.13-5.13H84.147v10.47h6.386v51.192h-5.895h-0.596H50.317V49.728h4.188c0.523,2.721,2.513,3.664,5.024,3.664
|
||||||
|
h20.31c3.036,0,5.235-2.199,5.235-5.236V27.638c0-3.035-2.199-5.338-5.235-5.338H44.036c-2.722,0-5.34,2.303-5.34,5.338v39.258
|
||||||
|
c0,3.037,2.618,5.234,5.34,5.234h40.007h1.03h11.951c2.931,0,5.13-2.197,5.13-5.234V50.984h3.035l17.798,19.264
|
||||||
|
c1.569,1.465,3.036,1.883,5.024,1.883h5.758V61.662H130.21z M50.317,32.663h23.135v10.366H50.317V32.663z"/>
|
||||||
|
<polygon points="203.416,0 196.745,0 191.794,0 191.794,12.772 203.416,12.772 "/>
|
||||||
|
<path d="M199.955,61.662v-3.453c2.617-0.629,3.873-2.723,3.873-4.816V27.638c0-3.035-2.304-5.338-5.13-5.338h-12.98v10.363h6.489
|
||||||
|
v24.184c0,3.559-2.512,3.635-6.489,4.682V72.13h24.602H223.3c2.932,0,5.13-2.197,5.13-5.234V50.984h3.035l17.798,19.264
|
||||||
|
c1.57,1.465,3.036,1.883,5.024,1.883h9.004V61.662h-6.805l-15.6-16.121l12.04-12.877h7.119V22.3h-8.69
|
||||||
|
c-2.512,0-4.082,0.942-5.129,1.989l-15.39,16.854h-2.407V5.13c0-2.512-2.198-5.13-5.13-5.13h-12.877v10.47h6.386v51.192"/>
|
||||||
|
<path d="M183.249,61.662V27.638c0-3.035-2.721-5.338-5.548-5.338h-25.754l-4.71,7.747h-0.209v-2.514
|
||||||
|
c0-2.093-1.676-5.233-5.444-5.233l-5.34-0.135v10.498v21.59v2.594V72.13h18.217V61.662h-10.469v-3.453
|
||||||
|
c2.617-0.629,3.873-2.723,3.873-4.816V40.201c0-4.396,3.455-7.538,7.852-7.538h15.912v34.233c0,1.289,0.435,2.428,1.151,3.311
|
||||||
|
c0.97,1.197,2.458,1.924,4.084,1.924h6.381L183.249,61.662L183.249,61.662z"/>
|
||||||
|
<rect x="171.628" y="54.419" width="11.62" height="6.25"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
23
IdentityServer/Images/logo-blue.svg
Normal file
23
IdentityServer/Images/logo-blue.svg
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
width="263.292px" height="72.13px" viewBox="0 0 263.292 72.13" enable-background="new 0 0 263.292 72.13" xml:space="preserve">
|
||||||
|
<path fill="#418BCA" d="M6.386,37.689c0-3.666-2.722-6.7-6.386-7.538V22.3h6.386V10.784h11.62V22.3h18.217v10.363h-22.09v3.561
|
||||||
|
c2.513,0.732,3.873,2.93,3.873,5.863v19.574h18.217V72.13H11.515c-2.616,0-5.129-2.197-5.129-5.234V37.689z"/>
|
||||||
|
<path fill="#418BCA" d="M130.21,61.662l-15.599-16.121l12.04-12.877h7.118V22.3h-8.689c-2.512,0-4.083,0.942-5.129,1.989
|
||||||
|
l-15.39,16.854h-2.407V5.13c0-2.512-2.199-5.13-5.13-5.13H84.147v10.47h6.386v51.192h-5.895h-0.596H50.317V49.728h4.188
|
||||||
|
c0.523,2.721,2.513,3.664,5.024,3.664h20.31c3.036,0,5.235-2.199,5.235-5.236V27.638c0-3.035-2.199-5.338-5.235-5.338H44.036
|
||||||
|
c-2.722,0-5.34,2.303-5.34,5.338v39.258c0,3.037,2.618,5.234,5.34,5.234h40.007h1.03h11.951c2.931,0,5.13-2.197,5.13-5.234V50.984
|
||||||
|
h3.035l17.798,19.264c1.569,1.465,3.036,1.883,5.024,1.883h5.758V61.662H130.21z M50.317,32.663h23.135v10.366H50.317V32.663z"/>
|
||||||
|
<polygon fill="#418BCA" points="203.416,0 196.745,0 191.794,0 191.794,12.772 203.416,12.772 "/>
|
||||||
|
<path fill="#418BCA" d="M199.955,61.662v-3.453c2.617-0.629,3.873-2.723,3.873-4.816V27.638c0-3.035-2.304-5.338-5.13-5.338h-12.98
|
||||||
|
v10.363h6.489v24.184c0,3.559-2.512,3.635-6.489,4.682V72.13h24.602H223.3c2.932,0,5.13-2.197,5.13-5.234V50.984h3.035
|
||||||
|
l17.798,19.264c1.57,1.465,3.036,1.883,5.024,1.883h9.004V61.662h-6.805l-15.6-16.121l12.04-12.877h7.119V22.3h-8.69
|
||||||
|
c-2.512,0-4.082,0.942-5.129,1.989l-15.39,16.854h-2.407V5.13c0-2.512-2.198-5.13-5.13-5.13h-12.877v10.47h6.386v51.192"/>
|
||||||
|
<path fill="#418BCA" d="M183.249,61.662V27.638c0-3.035-2.721-5.338-5.548-5.338h-25.754l-4.71,7.747h-0.209v-2.514
|
||||||
|
c0-2.093-1.676-5.233-5.444-5.233l-5.34-0.135v10.498v21.59v2.594V72.13h18.217V61.662h-10.469v-3.453
|
||||||
|
c2.617-0.629,3.873-2.723,3.873-4.816V40.201c0-4.396,3.455-7.538,7.852-7.538h15.912v34.233c0,1.289,0.435,2.428,1.151,3.311
|
||||||
|
c0.97,1.197,2.458,1.924,4.084,1.924h6.381L183.249,61.662L183.249,61.662z"/>
|
||||||
|
<rect x="171.628" y="54.419" fill="#418BCA" width="11.62" height="6.25"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
118
IdentityServer/Middleware/BlacklistMiddleware.cs
Normal file
118
IdentityServer/Middleware/BlacklistMiddleware.cs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Specialized;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Middleware
|
||||||
|
{
|
||||||
|
public class BlacklistMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IMemoryCache _cache;
|
||||||
|
|
||||||
|
public BlacklistMiddleware(RequestDelegate next, IMemoryCache cache)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext context, Config config)
|
||||||
|
{
|
||||||
|
// Beggining of Request
|
||||||
|
bool blocked = false;
|
||||||
|
string blockReason = string.Empty;
|
||||||
|
|
||||||
|
#region Detect Blacklisted IPs
|
||||||
|
if (!blocked)
|
||||||
|
{
|
||||||
|
string IPAddr = context.Request.HttpContext.Connection.RemoteIpAddress.ToString();
|
||||||
|
if (!string.IsNullOrEmpty(IPAddr))
|
||||||
|
{
|
||||||
|
StringDictionary badIPs = GetFileData(context, "BlockedIPs", config.IPBlacklistFile);
|
||||||
|
|
||||||
|
blocked |= (badIPs != null && badIPs.ContainsKey(IPAddr));
|
||||||
|
blockReason = $"This IP address ({IPAddr}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Detect Blacklisted Referrers
|
||||||
|
if (!blocked)
|
||||||
|
{
|
||||||
|
string referrer = context.Request.Headers["Referer"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(referrer))
|
||||||
|
{
|
||||||
|
StringDictionary badReferrers = GetFileData(context, "BlockedReferrers", config.ReferrerBlacklistFile);
|
||||||
|
|
||||||
|
blocked |= (badReferrers != null && badReferrers.ContainsKey(referrer));
|
||||||
|
blockReason = $"This referrer ({referrer}) has been blacklisted. If you feel this is in error, please contact support@teknik.io for assistance.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
if (blocked)
|
||||||
|
{
|
||||||
|
// Clear the response
|
||||||
|
context.Response.Clear();
|
||||||
|
|
||||||
|
string jsonResult = JsonConvert.SerializeObject(new { error = new { type = "Blacklist", message = blockReason } });
|
||||||
|
await context.Response.WriteAsync(jsonResult);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await _next.Invoke(context);
|
||||||
|
|
||||||
|
// End of request
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringDictionary GetFileData(HttpContext context, string key, string filePath)
|
||||||
|
{
|
||||||
|
StringDictionary data;
|
||||||
|
if (!_cache.TryGetValue(key, out data))
|
||||||
|
{
|
||||||
|
data = GetFileLines(filePath);
|
||||||
|
_cache.Set(key, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringDictionary GetFileLines(string configPath)
|
||||||
|
{
|
||||||
|
StringDictionary retval = new StringDictionary();
|
||||||
|
if (File.Exists(configPath))
|
||||||
|
{
|
||||||
|
using (StreamReader sr = new StreamReader(configPath))
|
||||||
|
{
|
||||||
|
String line;
|
||||||
|
while ((line = sr.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
line = line.Trim();
|
||||||
|
if (line.Length != 0)
|
||||||
|
{
|
||||||
|
retval.Add(line, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class BlacklistMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseBlacklist(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<BlacklistMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
IdentityServer/Middleware/CORSMiddleware.cs
Normal file
72
IdentityServer/Middleware/CORSMiddleware.cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Middleware
|
||||||
|
{
|
||||||
|
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||||
|
public class CORSMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public CORSMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InvokeAsync(HttpContext httpContext, Config config)
|
||||||
|
{
|
||||||
|
// Allow this domain, or everything if local
|
||||||
|
string origin = (httpContext.Request.IsLocal()) ? "*" : httpContext.Request.Headers["Origin"].ToString();
|
||||||
|
|
||||||
|
// Is the referrer set to the CDN and we are using a CDN?
|
||||||
|
if (config.UseCdn && !string.IsNullOrEmpty(config.CdnHost))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string host = httpContext.Request.Headers["Host"];
|
||||||
|
Uri uri = new Uri(config.CdnHost);
|
||||||
|
if (host == uri.Host)
|
||||||
|
origin = host;
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
string domain = (string.IsNullOrEmpty(origin)) ? string.Empty : origin.GetDomain();
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(origin))
|
||||||
|
{
|
||||||
|
string host = httpContext.Request.Headers["Host"];
|
||||||
|
string sub = host.GetSubdomain();
|
||||||
|
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (domain != config.Host)
|
||||||
|
{
|
||||||
|
string sub = origin.GetSubdomain();
|
||||||
|
origin = (string.IsNullOrEmpty(sub)) ? config.Host : sub + "." + config.Host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
httpContext.Response.Headers.Append("Access-Control-Allow-Origin", origin);
|
||||||
|
httpContext.Response.Headers.Append("Vary", "Origin");
|
||||||
|
|
||||||
|
return _next(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||||
|
public static class CORSMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseCORS(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<CORSMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
66
IdentityServer/Middleware/CSPMiddleware.cs
Normal file
66
IdentityServer/Middleware/CSPMiddleware.cs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Middleware
|
||||||
|
{
|
||||||
|
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||||
|
public class CSPMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public CSPMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Invoke(HttpContext httpContext, Config config)
|
||||||
|
{
|
||||||
|
if (!httpContext.Request.IsLocal())
|
||||||
|
{
|
||||||
|
// Default to nothing allowed
|
||||||
|
string allowedDomain = "'none'";
|
||||||
|
|
||||||
|
// Allow this domain
|
||||||
|
string host = httpContext.Request.Headers["Host"];
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(host))
|
||||||
|
{
|
||||||
|
allowedDomain = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
var csp = "default-src 'self';" +
|
||||||
|
"img-src * 'self' data: https:;" +
|
||||||
|
$"style-src 'self' {allowedDomain};" +
|
||||||
|
$"font-src 'self' {allowedDomain};" +
|
||||||
|
$"script-src 'self' 'unsafe-inline' {allowedDomain};";
|
||||||
|
|
||||||
|
if (!httpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
|
||||||
|
{
|
||||||
|
httpContext.Response.Headers.Add("Content-Security-Policy", csp);
|
||||||
|
}
|
||||||
|
// and once again for IE
|
||||||
|
if (!httpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
|
||||||
|
{
|
||||||
|
httpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _next(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||||
|
public static class CSPMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseCSP(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<CSPMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
105
IdentityServer/Middleware/ErrorHandlerMiddleware.cs
Normal file
105
IdentityServer/Middleware/ErrorHandlerMiddleware.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
using IdentityServer4.Services;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Internal;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.IdentityServer.Controllers;
|
||||||
|
using Teknik.Logging;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Middleware
|
||||||
|
{
|
||||||
|
public class ErrorHandlerMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
private readonly IRouter _router;
|
||||||
|
|
||||||
|
public ErrorHandlerMiddleware(RequestDelegate next, IRouter router)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
_router = router;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext httpContext, ILogger<Logger> logger, Config config, IIdentityServerInteractionService interaction)
|
||||||
|
{
|
||||||
|
var statusCodeFeature = new StatusCodePagesFeature();
|
||||||
|
httpContext.Features.Set<IStatusCodePagesFeature>(statusCodeFeature);
|
||||||
|
|
||||||
|
Exception exception = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _next(httpContext);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
httpContext.Response.StatusCode = 500;
|
||||||
|
exception = ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!statusCodeFeature.Enabled)
|
||||||
|
{
|
||||||
|
// Check if the feature is still available because other middleware (such as a web API written in MVC) could
|
||||||
|
// have disabled the feature to prevent HTML status code responses from showing up to an API client.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do nothing if a response body has already been provided or not 404 response
|
||||||
|
if (httpContext.Response.HasStarted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect if there is a response code or exception occured
|
||||||
|
if ((httpContext.Response.StatusCode >= 400 && httpContext.Response.StatusCode <= 600) || exception != null)
|
||||||
|
{
|
||||||
|
RouteData routeData = new RouteData();
|
||||||
|
routeData.Values.Add("controller", "Error");
|
||||||
|
routeData.Routers.Add(_router);
|
||||||
|
|
||||||
|
var context = new ControllerContext();
|
||||||
|
context.HttpContext = httpContext;
|
||||||
|
context.RouteData = routeData;
|
||||||
|
context.ActionDescriptor = new Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor();
|
||||||
|
|
||||||
|
ErrorController errorController = new ErrorController(logger, config, interaction);
|
||||||
|
errorController.ControllerContext = context;
|
||||||
|
|
||||||
|
if (httpContext.Response.StatusCode == 500)
|
||||||
|
{
|
||||||
|
await errorController.Http500(exception).ExecuteResultAsync(context);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await errorController.HttpError(httpContext.Response.StatusCode).ExecuteResultAsync(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||||
|
public static class SetupErrorHandlerMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseErrorHandler(this IApplicationBuilder builder, Config config)
|
||||||
|
{
|
||||||
|
var routes = new RouteBuilder(builder)
|
||||||
|
{
|
||||||
|
DefaultHandler = builder.ApplicationServices.GetRequiredService<MvcRouteHandler>(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return builder.UseMiddleware<ErrorHandlerMiddleware>(routes.Build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
IdentityServer/Middleware/PerformanceMonitorMiddleware.cs
Normal file
68
IdentityServer/Middleware/PerformanceMonitorMiddleware.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Middleware
|
||||||
|
{
|
||||||
|
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
|
||||||
|
public class PerformanceMonitorMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public PerformanceMonitorMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext httpContext, Config config)
|
||||||
|
{
|
||||||
|
Stopwatch timer = new Stopwatch();
|
||||||
|
timer.Start();
|
||||||
|
|
||||||
|
httpContext.Response.OnStarting(state =>
|
||||||
|
{
|
||||||
|
var context = (HttpContext)state;
|
||||||
|
|
||||||
|
timer.Stop();
|
||||||
|
|
||||||
|
double ms = (double)timer.ElapsedMilliseconds;
|
||||||
|
string result = string.Format("{0:F0}", ms);
|
||||||
|
|
||||||
|
if (!httpContext.Response.Headers.IsReadOnly)
|
||||||
|
httpContext.Response.Headers.Add("GenerationTime", result);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}, httpContext);
|
||||||
|
|
||||||
|
await _next(httpContext);
|
||||||
|
|
||||||
|
// Don't interfere with non-HTML responses
|
||||||
|
if (httpContext.Response.ContentType != null && httpContext.Response.ContentType.StartsWith("text/html") && httpContext.Response.StatusCode == 200 && !httpContext.Request.IsAjaxRequest())
|
||||||
|
{
|
||||||
|
double ms = (double)timer.ElapsedMilliseconds;
|
||||||
|
string result = string.Format("{0:F0}", ms);
|
||||||
|
|
||||||
|
await httpContext.Response.WriteAsync(
|
||||||
|
"<script nonce=\"" + httpContext.Items[Constants.NONCE_KEY] + "\">" +
|
||||||
|
"var pageGenerationTime = '" + result + "';" +
|
||||||
|
"pageloadStopTimer();" +
|
||||||
|
"</script >");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||||
|
public static class PerformanceMonitorMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UsePerformanceMonitor(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<PerformanceMonitorMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
IdentityServer/Middleware/SecurityHeadersMiddleware.cs
Normal file
61
IdentityServer/Middleware/SecurityHeadersMiddleware.cs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Middleware
|
||||||
|
{
|
||||||
|
public class SecurityHeadersMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public SecurityHeadersMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task Invoke(HttpContext httpContext, Config config)
|
||||||
|
{
|
||||||
|
IHeaderDictionary headers = httpContext.Response.Headers;
|
||||||
|
|
||||||
|
// Access Control
|
||||||
|
headers.Append("Access-Control-Allow-Credentials", "true");
|
||||||
|
headers.Append("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS");
|
||||||
|
headers.Append("Access-Control-Allow-Headers", "Authorization, Accept, Origin, Content-Type, X-Requested-With, Connection, Transfer-Encoding");
|
||||||
|
|
||||||
|
// HSTS
|
||||||
|
headers.Append("strict-transport-security", "max-age=31536000; includeSubdomains; preload");
|
||||||
|
|
||||||
|
// XSS Protection
|
||||||
|
headers.Append("X-XSS-Protection", "1; mode=block");
|
||||||
|
|
||||||
|
// Content Type Options
|
||||||
|
headers.Append("X-Content-Type-Options", "nosniff");
|
||||||
|
|
||||||
|
// Public Key Pinning
|
||||||
|
string keys = string.Empty;
|
||||||
|
foreach (string key in config.PublicKeys)
|
||||||
|
{
|
||||||
|
keys += $"pin-sha256=\"{key}\";";
|
||||||
|
}
|
||||||
|
headers.Append("Public-Key-Pins", $"max-age=300; includeSubDomains; {keys}");
|
||||||
|
|
||||||
|
// Referrer Policy
|
||||||
|
headers.Append("Referrer-Policy", "no-referrer, strict-origin-when-cross-origin");
|
||||||
|
|
||||||
|
return _next(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||||
|
public static class SecurityHeadersMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseSecurityHeaders(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<SecurityHeadersMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
40
IdentityServer/Middleware/SetupHttpContextMiddleware.cs
Normal file
40
IdentityServer/Middleware/SetupHttpContextMiddleware.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Middleware
|
||||||
|
{
|
||||||
|
public class SetupHttpContextMiddleware
|
||||||
|
{
|
||||||
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
public SetupHttpContextMiddleware(RequestDelegate next)
|
||||||
|
{
|
||||||
|
_next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Invoke(HttpContext httpContext)
|
||||||
|
{
|
||||||
|
// Generate the NONCE used for this request
|
||||||
|
string nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(StringHelper.RandomString(24)));
|
||||||
|
httpContext.Items[Constants.NONCE_KEY] = nonce;
|
||||||
|
|
||||||
|
await _next(httpContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extension method used to add the middleware to the HTTP request pipeline.
|
||||||
|
public static class SetupHttpContextMiddlewareExtensions
|
||||||
|
{
|
||||||
|
public static IApplicationBuilder UseHttpContextSetup(this IApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.UseMiddleware<SetupHttpContextMiddleware>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
74
IdentityServer/Models/ApplicationUser.cs
Normal file
74
IdentityServer/Models/ApplicationUser.cs
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models
|
||||||
|
{
|
||||||
|
public class ApplicationUser : IdentityUser
|
||||||
|
{
|
||||||
|
public DateTime CreationDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime LastSeen { get; set; }
|
||||||
|
|
||||||
|
public AccountType AccountType { get; set; }
|
||||||
|
|
||||||
|
public AccountStatus AccountStatus { get; set; }
|
||||||
|
|
||||||
|
public string PGPPublicKey { get; set; }
|
||||||
|
|
||||||
|
public ApplicationUser() : base()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApplicationUser(string userName) : base(userName)
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Init()
|
||||||
|
{
|
||||||
|
CreationDate = DateTime.Now;
|
||||||
|
LastSeen = DateTime.Now;
|
||||||
|
AccountType = AccountType.Basic;
|
||||||
|
AccountStatus = AccountStatus.Active;
|
||||||
|
PGPPublicKey = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Claim> ToClaims()
|
||||||
|
{
|
||||||
|
var claims = new List<Claim>();
|
||||||
|
claims.Add(new Claim("username", UserName));
|
||||||
|
claims.Add(new Claim("creation-date", CreationDate.ToString("o")));
|
||||||
|
claims.Add(new Claim("last-seen", LastSeen.ToString("o")));
|
||||||
|
claims.Add(new Claim("account-type", AccountType.ToString()));
|
||||||
|
claims.Add(new Claim("account-status", AccountStatus.ToString()));
|
||||||
|
claims.Add(new Claim("recovery-email", Email ?? string.Empty));
|
||||||
|
claims.Add(new Claim("recovery-verified", EmailConfirmed.ToString()));
|
||||||
|
claims.Add(new Claim("2fa-enabled", TwoFactorEnabled.ToString()));
|
||||||
|
claims.Add(new Claim("pgp-public-key", PGPPublicKey ?? string.Empty));
|
||||||
|
return claims;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JObject ToJson()
|
||||||
|
{
|
||||||
|
return new JObject()
|
||||||
|
{
|
||||||
|
new JProperty("username", UserName),
|
||||||
|
new JProperty("creation-date", CreationDate),
|
||||||
|
new JProperty("last-seen", LastSeen),
|
||||||
|
new JProperty("account-type", AccountType),
|
||||||
|
new JProperty("account-status", AccountStatus),
|
||||||
|
new JProperty("recovery-email", Email),
|
||||||
|
new JProperty("recovery-verified", EmailConfirmed),
|
||||||
|
new JProperty("2fa-enabled", TwoFactorEnabled),
|
||||||
|
new JProperty("pgp-public-key", PGPPublicKey)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/Models/ConsentInputModel.cs
Normal file
12
IdentityServer/Models/ConsentInputModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models
|
||||||
|
{
|
||||||
|
public class ConsentInputModel
|
||||||
|
{
|
||||||
|
public string Button { get; set; }
|
||||||
|
public IEnumerable<string> ScopesConsented { get; set; }
|
||||||
|
public bool RememberConsent { get; set; }
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
IdentityServer/Models/LoginInputModel.cs
Normal file
14
IdentityServer/Models/LoginInputModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models
|
||||||
|
{
|
||||||
|
public class LoginInputModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
public string Username { get; set; }
|
||||||
|
[Required]
|
||||||
|
public string Password { get; set; }
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
public string ReturnUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
8
IdentityServer/Models/LogoutInputModel.cs
Normal file
8
IdentityServer/Models/LogoutInputModel.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace Teknik.IdentityServer.Models
|
||||||
|
{
|
||||||
|
public class LogoutInputModel
|
||||||
|
{
|
||||||
|
public string LogoutId { get; set; }
|
||||||
|
public string ReturnURL { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
IdentityServer/Models/Manage/CheckPasswordModel.cs
Normal file
13
IdentityServer/Models/Manage/CheckPasswordModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class CheckPasswordModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
11
IdentityServer/Models/Manage/CreateClientModel.cs
Normal file
11
IdentityServer/Models/Manage/CreateClientModel.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class CreateClientModel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/Models/Manage/DeleteUserModel.cs
Normal file
12
IdentityServer/Models/Manage/DeleteUserModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class DeleteUserModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/Models/Manage/Disable2FAModel.cs
Normal file
12
IdentityServer/Models/Manage/Disable2FAModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class Disable2FAModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
IdentityServer/Models/Manage/Enable2FAModel.cs
Normal file
13
IdentityServer/Models/Manage/Enable2FAModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class Enable2FAModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Code { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class GeneratePasswordResetTokenModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/Models/Manage/GenerateRecoveryCodesModel.cs
Normal file
12
IdentityServer/Models/Manage/GenerateRecoveryCodesModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class GenerateRecoveryCodesModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
25
IdentityServer/Models/Manage/NewUserModel.cs
Normal file
25
IdentityServer/Models/Manage/NewUserModel.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class NewUserModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
public AccountType AccountType { get; set; }
|
||||||
|
public AccountStatus AccountStatus { get; set; }
|
||||||
|
public string RecoveryEmail { get; set; }
|
||||||
|
public bool RecoveryVerified { get; set; }
|
||||||
|
public string PGPPublicKey { get; set; }
|
||||||
|
|
||||||
|
public NewUserModel()
|
||||||
|
{
|
||||||
|
AccountType = AccountType.Basic;
|
||||||
|
AccountStatus = AccountStatus.Active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/Models/Manage/Reset2FAKeyModel.cs
Normal file
12
IdentityServer/Models/Manage/Reset2FAKeyModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class Reset2FAKeyModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
IdentityServer/Models/Manage/ResetPasswordModel.cs
Normal file
14
IdentityServer/Models/Manage/ResetPasswordModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class ResetPasswordModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
public string Password { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
IdentityServer/Models/Manage/UpdateAccountStatusModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdateAccountStatusModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class UpdateAccountStatusModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public AccountStatus AccountStatus { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
IdentityServer/Models/Manage/UpdateAccountTypeModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdateAccountTypeModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class UpdateAccountTypeModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public AccountType AccountType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
IdentityServer/Models/Manage/UpdateEmailModel.cs
Normal file
13
IdentityServer/Models/Manage/UpdateEmailModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class UpdateEmailModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
IdentityServer/Models/Manage/UpdateEmailVerifiedModel.cs
Normal file
13
IdentityServer/Models/Manage/UpdateEmailVerifiedModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class UpdateEmailVerifiedModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public bool Verified { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
IdentityServer/Models/Manage/UpdatePGPPublicKeyModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdatePGPPublicKeyModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class UpdatePGPPublicKeyModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string PGPPublicKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
IdentityServer/Models/Manage/UpdatePasswordModel.cs
Normal file
14
IdentityServer/Models/Manage/UpdatePasswordModel.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class UpdatePasswordModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string CurrentPassword { get; set; }
|
||||||
|
public string NewPassword { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
IdentityServer/Models/Manage/VerifyEmailModel.cs
Normal file
13
IdentityServer/Models/Manage/VerifyEmailModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models.Manage
|
||||||
|
{
|
||||||
|
public class VerifyEmailModel
|
||||||
|
{
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
}
|
||||||
|
}
|
16
IdentityServer/Models/ProcessConsentResult.cs
Normal file
16
IdentityServer/Models/ProcessConsentResult.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using Teknik.IdentityServer.ViewModels;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Models
|
||||||
|
{
|
||||||
|
public class ProcessConsentResult
|
||||||
|
{
|
||||||
|
public bool IsRedirect => RedirectUri != null;
|
||||||
|
public string RedirectUri { get; set; }
|
||||||
|
|
||||||
|
public bool ShowView => ViewModel != null;
|
||||||
|
public ConsentViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
|
public bool HasValidationError => ValidationError != null;
|
||||||
|
public string ValidationError { get; set; }
|
||||||
|
}
|
||||||
|
}
|
16
IdentityServer/Options/AccountOptions.cs
Normal file
16
IdentityServer/Options/AccountOptions.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Options
|
||||||
|
{
|
||||||
|
public class AccountOptions
|
||||||
|
{
|
||||||
|
public static bool AllowLocalLogin = true;
|
||||||
|
public static bool AllowRememberLogin = true;
|
||||||
|
public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30);
|
||||||
|
|
||||||
|
public static bool ShowLogoutPrompt = true;
|
||||||
|
public static bool AutomaticRedirectAfterSignOut = true;
|
||||||
|
|
||||||
|
public static string InvalidCredentialsErrorMessage = "Invalid username or password";
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/Options/ConsentOptions.cs
Normal file
12
IdentityServer/Options/ConsentOptions.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Teknik.IdentityServer.Options
|
||||||
|
{
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
}
|
27
IdentityServer/Program.cs
Normal file
27
IdentityServer/Program.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using Microsoft.AspNetCore;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer
|
||||||
|
{
|
||||||
|
public class Program
|
||||||
|
{
|
||||||
|
public static void Main(string[] args)
|
||||||
|
{
|
||||||
|
BuildWebHost(args).Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IWebHost BuildWebHost(string[] args)
|
||||||
|
{
|
||||||
|
var config = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.json", optional: true)
|
||||||
|
.AddCommandLine(args)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
return WebHost.CreateDefaultBuilder(args)
|
||||||
|
.UseConfiguration(config)
|
||||||
|
.UseStartup<Startup>()
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
This file is used by the publish/package process of your Web project. You can customize the behavior of this process
|
||||||
|
by editing this MSBuild file. In order to learn more about this please visit https://go.microsoft.com/fwlink/?LinkID=208121.
|
||||||
|
-->
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<WebPublishMethod>MSDeploy</WebPublishMethod>
|
||||||
|
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
|
||||||
|
<LastUsedPlatform>Any CPU</LastUsedPlatform>
|
||||||
|
<SiteUrlToLaunchAfterPublish>https://authdev.teknik.io</SiteUrlToLaunchAfterPublish>
|
||||||
|
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
|
||||||
|
<ExcludeApp_Data>False</ExcludeApp_Data>
|
||||||
|
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||||
|
<ProjectGuid>05842e03-223a-4f43-9e81-d968a9475a97</ProjectGuid>
|
||||||
|
<SelfContained>false</SelfContained>
|
||||||
|
<_IsPortable>true</_IsPortable>
|
||||||
|
<MSDeployServiceURL>ams1.teknik.io</MSDeployServiceURL>
|
||||||
|
<DeployIisAppPath>TeknikIdentityDev</DeployIisAppPath>
|
||||||
|
<RemoteSitePhysicalPath />
|
||||||
|
<SkipExtraFilesOnServer>True</SkipExtraFilesOnServer>
|
||||||
|
<MSDeployPublishMethod>WMSVC</MSDeployPublishMethod>
|
||||||
|
<EnableMSDeployBackup>True</EnableMSDeployBackup>
|
||||||
|
<UserName>Administrator</UserName>
|
||||||
|
<_SavePWD>True</_SavePWD>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
35
IdentityServer/Properties/launchSettings.json
Normal file
35
IdentityServer/Properties/launchSettings.json
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"iisSettings": {
|
||||||
|
"windowsAuthentication": false,
|
||||||
|
"anonymousAuthentication": true,
|
||||||
|
"iisExpress": {
|
||||||
|
"applicationUrl": "https://localhost:44350/",
|
||||||
|
"sslPort": 44350
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"profiles": {
|
||||||
|
"IIS Express": {
|
||||||
|
"commandName": "IISExpress",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"IdentityServer": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:6002/"
|
||||||
|
},
|
||||||
|
"IdentityServer - Prod": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"launchBrowser": true,
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Production"
|
||||||
|
},
|
||||||
|
"applicationUrl": "https://localhost:6002/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
IdentityServer/Scripts/Error.js
Normal file
49
IdentityServer/Scripts/Error.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
$(document).ready(function () {
|
||||||
|
$('#submitErrorReport').click(function () {
|
||||||
|
bootbox.prompt({
|
||||||
|
title: "Please enter any additional information that could help us",
|
||||||
|
inputType: 'textarea',
|
||||||
|
callback: function (result) {
|
||||||
|
if (result) {
|
||||||
|
errorMsg = $("#errorMsg").html();
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: submitErrorReportURL,
|
||||||
|
data: AddAntiForgeryToken({
|
||||||
|
Message: result,
|
||||||
|
Exception: errorMsg,
|
||||||
|
CurrentUrl: window.location.href
|
||||||
|
}),
|
||||||
|
success: function (response) {
|
||||||
|
if (response.result) {
|
||||||
|
$("#top_msg").css('display', 'inline', 'important');
|
||||||
|
$("#top_msg").html(
|
||||||
|
'<div class="alert alert-info alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>Thank you for your help! Feedback has been submitted.</div>');
|
||||||
|
} else {
|
||||||
|
$("#top_msg").css('display', 'inline', 'important');
|
||||||
|
$("#top_msg")
|
||||||
|
.html(
|
||||||
|
'<div class="alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' +
|
||||||
|
parseErrorMessage(response) +
|
||||||
|
'</div>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".view-details-button").on("click",
|
||||||
|
function () {
|
||||||
|
var link = $(this);
|
||||||
|
var linkText = link.text().toUpperCase();
|
||||||
|
|
||||||
|
if (linkText === "SHOW DETAILS") {
|
||||||
|
link.text("Hide Details");
|
||||||
|
} else {
|
||||||
|
link.text("Show Details");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
6
IdentityServer/Scripts/signout-redirect.js
Normal file
6
IdentityServer/Scripts/signout-redirect.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
window.addEventListener("load", function () {
|
||||||
|
var a = document.querySelector("a.PostLogoutRedirectUri");
|
||||||
|
if (a) {
|
||||||
|
window.location = a.href;
|
||||||
|
}
|
||||||
|
});
|
62
IdentityServer/Security/PasswordHasher.cs
Normal file
62
IdentityServer/Security/PasswordHasher.cs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Microsoft.Extensions.Identity.Core;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Teknik.Utilities.Cryptography;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
using System.Text;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Security
|
||||||
|
{
|
||||||
|
public class TeknikPasswordHasher : PasswordHasher<ApplicationUser>
|
||||||
|
{
|
||||||
|
private readonly Config _config;
|
||||||
|
|
||||||
|
public TeknikPasswordHasher(Config config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override PasswordVerificationResult VerifyHashedPassword(ApplicationUser user, string hashedPassword, string providedPassword)
|
||||||
|
{
|
||||||
|
if (hashedPassword == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(hashedPassword));
|
||||||
|
}
|
||||||
|
if (providedPassword == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(providedPassword));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test legacy password hashes
|
||||||
|
#region Legacy Checks
|
||||||
|
byte[] hashBytes = SHA384.Hash(user.UserName, providedPassword);
|
||||||
|
string hash = hashBytes.ToHex();
|
||||||
|
if (hashedPassword == hash)
|
||||||
|
{
|
||||||
|
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = Encoding.ASCII.GetString(hashBytes);
|
||||||
|
if (hashedPassword == hash)
|
||||||
|
{
|
||||||
|
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = SHA256.Hash(providedPassword, _config.Salt1, _config.Salt2);
|
||||||
|
if (hashedPassword == hash)
|
||||||
|
{
|
||||||
|
return PasswordVerificationResult.SuccessRehashNeeded;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
// Test Latest
|
||||||
|
return base.VerifyHashedPassword(user, hashedPassword, providedPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
IdentityServer/Security/SecurityHeadersAttribute.cs
Normal file
39
IdentityServer/Security/SecurityHeadersAttribute.cs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Security
|
||||||
|
{
|
||||||
|
public class SecurityHeadersAttribute : ActionFilterAttribute
|
||||||
|
{
|
||||||
|
public override void OnResultExecuting(ResultExecutingContext context)
|
||||||
|
{
|
||||||
|
var result = context.Result;
|
||||||
|
if (result is ViewResult)
|
||||||
|
{
|
||||||
|
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options"))
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff");
|
||||||
|
}
|
||||||
|
if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options"))
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
|
||||||
|
}
|
||||||
|
|
||||||
|
var csp = "default-src 'self';";
|
||||||
|
// an example if you need client images to be displayed from twitter
|
||||||
|
//var csp = "default-src 'self'; img-src 'self' https://pbs.twimg.com";
|
||||||
|
|
||||||
|
// once for standards compliant browsers
|
||||||
|
if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp);
|
||||||
|
}
|
||||||
|
// and once again for IE
|
||||||
|
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
|
||||||
|
{
|
||||||
|
context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
60
IdentityServer/Security/TeknikRedirectUriValidator.cs
Normal file
60
IdentityServer/Security/TeknikRedirectUriValidator.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using IdentityServer4.Models;
|
||||||
|
using IdentityServer4.Validation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Security
|
||||||
|
{
|
||||||
|
public class TeknikRedirectUriValidator : IRedirectUriValidator
|
||||||
|
{
|
||||||
|
private readonly Config _config;
|
||||||
|
|
||||||
|
public TeknikRedirectUriValidator(Config config)
|
||||||
|
{
|
||||||
|
_config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client)
|
||||||
|
{
|
||||||
|
if (client.PostLogoutRedirectUris != null && client.PostLogoutRedirectUris.Any())
|
||||||
|
{
|
||||||
|
if (client.PostLogoutRedirectUris.Contains(requestedUri))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add special case for pre-configured redirect URIs
|
||||||
|
if (client.ClientId == _config.UserConfig.IdentityServerConfig.ClientId &&
|
||||||
|
_config.UserConfig.IdentityServerConfig.PostLogoutRedirectUris != null &&
|
||||||
|
_config.UserConfig.IdentityServerConfig.PostLogoutRedirectUris.Any())
|
||||||
|
{
|
||||||
|
if (_config.UserConfig.IdentityServerConfig.PostLogoutRedirectUris.Contains(requestedUri))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsRedirectUriValidAsync(string requestedUri, Client client)
|
||||||
|
{
|
||||||
|
if (client.RedirectUris != null && client.RedirectUris.Any())
|
||||||
|
{
|
||||||
|
if (client.RedirectUris.Contains(requestedUri))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add special case for pre-configured redirect URIs
|
||||||
|
if (client.ClientId == _config.UserConfig.IdentityServerConfig.ClientId &&
|
||||||
|
_config.UserConfig.IdentityServerConfig.RedirectUris != null &&
|
||||||
|
_config.UserConfig.IdentityServerConfig.RedirectUris.Any())
|
||||||
|
{
|
||||||
|
if (_config.UserConfig.IdentityServerConfig.RedirectUris.Contains(requestedUri))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
107
IdentityServer/Services/AccountService.cs
Normal file
107
IdentityServer/Services/AccountService.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using IdentityModel;
|
||||||
|
using IdentityServer4.Extensions;
|
||||||
|
using IdentityServer4.Services;
|
||||||
|
using IdentityServer4.Stores;
|
||||||
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
using Teknik.IdentityServer.Options;
|
||||||
|
using Teknik.IdentityServer.ViewModels;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Services
|
||||||
|
{
|
||||||
|
public class AccountService
|
||||||
|
{
|
||||||
|
private readonly IClientStore _clientStore;
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
private readonly IAuthenticationSchemeProvider _schemeProvider;
|
||||||
|
|
||||||
|
public AccountService(
|
||||||
|
IIdentityServerInteractionService interaction,
|
||||||
|
IHttpContextAccessor httpContextAccessor,
|
||||||
|
IAuthenticationSchemeProvider schemeProvider,
|
||||||
|
IClientStore clientStore)
|
||||||
|
{
|
||||||
|
_interaction = interaction;
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
_schemeProvider = schemeProvider;
|
||||||
|
_clientStore = clientStore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LoginViewModel> BuildLoginViewModelAsync(string returnUrl)
|
||||||
|
{
|
||||||
|
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||||
|
|
||||||
|
var allowLocal = true;
|
||||||
|
if (context?.ClientId != null)
|
||||||
|
{
|
||||||
|
var client = await _clientStore.FindEnabledClientByIdAsync(context.ClientId);
|
||||||
|
if (client != null)
|
||||||
|
{
|
||||||
|
allowLocal = client.EnableLocalLogin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LoginViewModel
|
||||||
|
{
|
||||||
|
AllowRememberLogin = AccountOptions.AllowRememberLogin,
|
||||||
|
EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin,
|
||||||
|
ReturnUrl = returnUrl,
|
||||||
|
Username = context?.LoginHint
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LoginViewModel> BuildLoginViewModelAsync(LoginInputModel model)
|
||||||
|
{
|
||||||
|
var vm = await BuildLoginViewModelAsync(model.ReturnUrl);
|
||||||
|
vm.Username = model.Username;
|
||||||
|
vm.RememberMe = model.RememberMe;
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LogoutViewModel> BuildLogoutViewModelAsync(string logoutId)
|
||||||
|
{
|
||||||
|
var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt };
|
||||||
|
|
||||||
|
var user = _httpContextAccessor.HttpContext.User;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<LoggedOutViewModel> 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 = logout?.ClientId,
|
||||||
|
SignOutIframeUrl = logout?.SignOutIFrameUrl,
|
||||||
|
LogoutId = logoutId
|
||||||
|
};
|
||||||
|
|
||||||
|
return vm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
190
IdentityServer/Services/ConsentService.cs
Normal file
190
IdentityServer/Services/ConsentService.cs
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
using IdentityServer4.Models;
|
||||||
|
using IdentityServer4.Services;
|
||||||
|
using IdentityServer4.Stores;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
using Teknik.IdentityServer.Options;
|
||||||
|
using Teknik.IdentityServer.ViewModels;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.Services
|
||||||
|
{
|
||||||
|
public class ConsentService
|
||||||
|
{
|
||||||
|
private readonly IClientStore _clientStore;
|
||||||
|
private readonly IResourceStore _resourceStore;
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
|
public ConsentService(
|
||||||
|
IIdentityServerInteractionService interaction,
|
||||||
|
IClientStore clientStore,
|
||||||
|
IResourceStore resourceStore,
|
||||||
|
ILogger logger)
|
||||||
|
{
|
||||||
|
_interaction = interaction;
|
||||||
|
_clientStore = clientStore;
|
||||||
|
_resourceStore = resourceStore;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ProcessConsentResult> ProcessConsent(ConsentInputModel model)
|
||||||
|
{
|
||||||
|
var result = new ProcessConsentResult();
|
||||||
|
|
||||||
|
ConsentResponse grantedConsent = null;
|
||||||
|
|
||||||
|
// user clicked 'no' - send back the standard 'access_denied' response
|
||||||
|
if (model.Button == "no")
|
||||||
|
{
|
||||||
|
grantedConsent = ConsentResponse.Denied;
|
||||||
|
}
|
||||||
|
// user clicked 'yes' - validate the data
|
||||||
|
else if (model.Button == "yes" && model != null)
|
||||||
|
{
|
||||||
|
// 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()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.ValidationError = ConsentOptions.MustChooseOneErrorMessage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (grantedConsent != null)
|
||||||
|
{
|
||||||
|
// validate return url is still valid
|
||||||
|
var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
|
||||||
|
if (request == null) return result;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we need to redisplay the consent UI
|
||||||
|
result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ConsentViewModel> 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();
|
||||||
|
vm.RememberConsent = model?.RememberConsent ?? true;
|
||||||
|
vm.ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty<string>();
|
||||||
|
|
||||||
|
vm.ReturnUrl = returnUrl;
|
||||||
|
|
||||||
|
vm.ClientName = client.ClientName ?? client.ClientId;
|
||||||
|
vm.ClientUrl = client.ClientUri;
|
||||||
|
vm.ClientLogoUrl = client.LogoUri;
|
||||||
|
vm.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
245
IdentityServer/Startup.cs
Normal file
245
IdentityServer/Startup.cs
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using IdentityServer4.EntityFramework.DbContexts;
|
||||||
|
using IdentityServer4.EntityFramework.Mappers;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
|
using Teknik.Configuration;
|
||||||
|
using Teknik.IdentityServer.Configuration;
|
||||||
|
using Teknik.IdentityServer.Security;
|
||||||
|
using Teknik.IdentityServer.Middleware;
|
||||||
|
using Teknik.Logging;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
using IdentityServer4.Services;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer
|
||||||
|
{
|
||||||
|
public class Startup
|
||||||
|
{
|
||||||
|
public Startup(IConfiguration configuration, IHostingEnvironment env)
|
||||||
|
{
|
||||||
|
Configuration = configuration;
|
||||||
|
Environment = env;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
public IHostingEnvironment Environment { get; }
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
string dataDir = Configuration["ConfigDirectory"];
|
||||||
|
AppDomain.CurrentDomain.SetData("DataDirectory", dataDir);
|
||||||
|
|
||||||
|
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
|
||||||
|
|
||||||
|
// Create Configuration Singleton
|
||||||
|
services.AddScoped<Config, Config>(opt => Config.Load(dataDir));
|
||||||
|
|
||||||
|
// Build an intermediate service provider
|
||||||
|
var sp = services.BuildServiceProvider();
|
||||||
|
|
||||||
|
// Resolve the services from the service provider
|
||||||
|
var config = sp.GetService<Config>();
|
||||||
|
|
||||||
|
services.ConfigureApplicationCookie(options =>
|
||||||
|
{
|
||||||
|
options.Cookie.Name = "TeknikAuth";
|
||||||
|
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||||
|
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
|
||||||
|
options.Cookie.Expiration = TimeSpan.FromDays(30);
|
||||||
|
options.ExpireTimeSpan = TimeSpan.FromDays(30);
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddHttpsRedirection(options =>
|
||||||
|
{
|
||||||
|
options.RedirectStatusCode = StatusCodes.Status301MovedPermanently;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sessions
|
||||||
|
services.AddResponseCaching();
|
||||||
|
services.AddMemoryCache();
|
||||||
|
services.AddSession();
|
||||||
|
|
||||||
|
// Set the anti-forgery cookie name
|
||||||
|
services.AddAntiforgery(options =>
|
||||||
|
{
|
||||||
|
options.Cookie.Name = "TeknikAuthAntiForgery";
|
||||||
|
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
|
||||||
|
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict;
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
|
||||||
|
|
||||||
|
services.AddDbContext<ApplicationDbContext>(builder =>
|
||||||
|
builder.UseSqlServer(Configuration.GetConnectionString("TeknikAuthEntities"), sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)));
|
||||||
|
|
||||||
|
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
|
||||||
|
{
|
||||||
|
options.Password = new PasswordOptions()
|
||||||
|
{
|
||||||
|
RequireDigit = false,
|
||||||
|
RequiredLength = 4,
|
||||||
|
RequiredUniqueChars = 1,
|
||||||
|
RequireLowercase = false,
|
||||||
|
RequireNonAlphanumeric = false,
|
||||||
|
RequireUppercase = false
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.AddEntityFrameworkStores<ApplicationDbContext>()
|
||||||
|
.AddDefaultTokenProviders();
|
||||||
|
|
||||||
|
services.AddIdentityServer(options =>
|
||||||
|
{
|
||||||
|
options.Events.RaiseErrorEvents = true;
|
||||||
|
options.Events.RaiseInformationEvents = true;
|
||||||
|
options.Events.RaiseFailureEvents = true;
|
||||||
|
options.Events.RaiseSuccessEvents = true;
|
||||||
|
options.UserInteraction.ErrorUrl = "/Error/IdentityError";
|
||||||
|
options.UserInteraction.ErrorIdParameter = "errorId";
|
||||||
|
})
|
||||||
|
.AddOperationalStore(options =>
|
||||||
|
options.ConfigureDbContext = builder =>
|
||||||
|
builder.UseSqlServer(Configuration.GetConnectionString("TeknikAuthEntities"), sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
|
||||||
|
.AddConfigurationStore(options =>
|
||||||
|
options.ConfigureDbContext = builder =>
|
||||||
|
builder.UseSqlServer(Configuration.GetConnectionString("TeknikAuthEntities"), sqlOptions => sqlOptions.MigrationsAssembly(migrationsAssembly)))
|
||||||
|
.AddAspNetIdentity<ApplicationUser>()
|
||||||
|
.AddRedirectUriValidator<TeknikRedirectUriValidator>()
|
||||||
|
.AddDeveloperSigningCredential();
|
||||||
|
|
||||||
|
services.AddAuthorization(options =>
|
||||||
|
{
|
||||||
|
foreach (var policy in Policies.Get())
|
||||||
|
{
|
||||||
|
options.AddPolicy(policy.Name, p =>
|
||||||
|
{
|
||||||
|
foreach (var scope in policy.Scopes)
|
||||||
|
{
|
||||||
|
p.RequireScope(scope);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddAuthentication("Bearer")
|
||||||
|
.AddIdentityServerAuthentication(options =>
|
||||||
|
{
|
||||||
|
options.Authority = config.UserConfig.IdentityServerConfig.Authority;
|
||||||
|
options.RequireHttpsMetadata = true;
|
||||||
|
|
||||||
|
options.ApiName = "auth-api";
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddTransient<IPasswordHasher<ApplicationUser>, TeknikPasswordHasher>();
|
||||||
|
services.AddTransient<IProfileService, TeknikProfileService>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, Config config)
|
||||||
|
{
|
||||||
|
// Initiate Logging
|
||||||
|
loggerFactory.AddLogger(config);
|
||||||
|
|
||||||
|
// Setup the HttpContext
|
||||||
|
app.UseHttpContextSetup();
|
||||||
|
|
||||||
|
// HttpContext Session
|
||||||
|
app.UseSession(new SessionOptions()
|
||||||
|
{
|
||||||
|
IdleTimeout = TimeSpan.FromMinutes(30),
|
||||||
|
Cookie = new CookieBuilder()
|
||||||
|
{
|
||||||
|
Domain = null,
|
||||||
|
Name = "TeknikAuthSession",
|
||||||
|
SecurePolicy = CookieSecurePolicy.Always,
|
||||||
|
SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Strict
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Use Exception Handling
|
||||||
|
app.UseErrorHandler(config);
|
||||||
|
|
||||||
|
if (env.IsDevelopment())
|
||||||
|
{
|
||||||
|
app.UseDeveloperExceptionPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Custom Middleware
|
||||||
|
app.UseBlacklist();
|
||||||
|
app.UseCORS();
|
||||||
|
app.UseCSP();
|
||||||
|
app.UseSecurityHeaders();
|
||||||
|
|
||||||
|
// Cache Responses
|
||||||
|
app.UseResponseCaching();
|
||||||
|
|
||||||
|
// Force a HTTPS redirection (301)
|
||||||
|
app.UseHttpsRedirection();
|
||||||
|
|
||||||
|
// Setup static files anc cache them client side
|
||||||
|
app.UseStaticFiles(new StaticFileOptions
|
||||||
|
{
|
||||||
|
OnPrepareResponse = ctx =>
|
||||||
|
{
|
||||||
|
ctx.Context.Response.Headers[HeaderNames.CacheControl] = "public,max-age=" + 31536000;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
InitializeDbTestDataAsync(app, config).Wait();
|
||||||
|
|
||||||
|
app.UseIdentityServer();
|
||||||
|
|
||||||
|
app.UseMvcWithDefaultRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async System.Threading.Tasks.Task InitializeDbTestDataAsync(IApplicationBuilder app, Config config)
|
||||||
|
{
|
||||||
|
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
|
||||||
|
{
|
||||||
|
scope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();
|
||||||
|
scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>().Database.Migrate();
|
||||||
|
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
|
||||||
|
|
||||||
|
var context = scope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
|
||||||
|
|
||||||
|
if (!context.Clients.Any())
|
||||||
|
{
|
||||||
|
foreach (var client in Clients.Get(config))
|
||||||
|
{
|
||||||
|
context.Clients.Add(client.ToEntity());
|
||||||
|
}
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.IdentityResources.Any())
|
||||||
|
{
|
||||||
|
foreach (var resource in Resources.GetIdentityResources())
|
||||||
|
{
|
||||||
|
context.IdentityResources.Add(resource.ToEntity());
|
||||||
|
}
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!context.ApiResources.Any())
|
||||||
|
{
|
||||||
|
foreach (var resource in Resources.GetApiResources(config))
|
||||||
|
{
|
||||||
|
context.ApiResources.Add(resource.ToEntity());
|
||||||
|
}
|
||||||
|
context.SaveChanges();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
49
IdentityServer/TeknikProfileService.cs
Normal file
49
IdentityServer/TeknikProfileService.cs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
using IdentityModel;
|
||||||
|
using IdentityServer4.Extensions;
|
||||||
|
using IdentityServer4.Models;
|
||||||
|
using IdentityServer4.Services;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
using Teknik.Utilities;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer
|
||||||
|
{
|
||||||
|
public class TeknikProfileService : IProfileService
|
||||||
|
{
|
||||||
|
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
|
||||||
|
private readonly UserManager<ApplicationUser> _userManager;
|
||||||
|
|
||||||
|
public TeknikProfileService(UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory)
|
||||||
|
{
|
||||||
|
_userManager = userManager;
|
||||||
|
_claimsFactory = claimsFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
|
||||||
|
{
|
||||||
|
var sub = context.Subject.GetSubjectId();
|
||||||
|
var user = await _userManager.FindByIdAsync(sub);
|
||||||
|
var principal = await _claimsFactory.CreateAsync(user);
|
||||||
|
|
||||||
|
var claims = principal.Claims.ToList();
|
||||||
|
|
||||||
|
claims.AddRange(user.ToClaims());
|
||||||
|
|
||||||
|
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
|
||||||
|
|
||||||
|
context.IssuedClaims = claims;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task IsActiveAsync(IsActiveContext context)
|
||||||
|
{
|
||||||
|
var sub = context.Subject.GetSubjectId();
|
||||||
|
var user = await _userManager.FindByIdAsync(sub);
|
||||||
|
context.IsActive = user != null && user.AccountStatus == Utilities.AccountStatus.Active;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
IdentityServer/ViewModels/ConsentViewModel.cs
Normal file
16
IdentityServer/ViewModels/ConsentViewModel.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
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<ScopeViewModel> IdentityScopes { get; set; }
|
||||||
|
public IEnumerable<ScopeViewModel> ResourceScopes { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/ViewModels/ErrorViewModel.cs
Normal file
12
IdentityServer/ViewModels/ErrorViewModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class ErrorViewModel
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
public int StatusCode { get; set; }
|
||||||
|
public Exception Exception { get; set; }
|
||||||
|
}
|
||||||
|
}
|
22
IdentityServer/ViewModels/GrantsViewModel.cs
Normal file
22
IdentityServer/ViewModels/GrantsViewModel.cs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class GrantsViewModel
|
||||||
|
{
|
||||||
|
public IEnumerable<GrantViewModel> 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<string> IdentityGrantNames { get; set; }
|
||||||
|
public IEnumerable<string> ApiGrantNames { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
IdentityServer/ViewModels/IdentityErrorViewModel.cs
Normal file
10
IdentityServer/ViewModels/IdentityErrorViewModel.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class IdentityErrorViewModel
|
||||||
|
{
|
||||||
|
public string Title { get; set; }
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
}
|
13
IdentityServer/ViewModels/LoggedOutViewModel.cs
Normal file
13
IdentityServer/ViewModels/LoggedOutViewModel.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class LoggedOutViewModel
|
||||||
|
{
|
||||||
|
public string PostLogoutRedirectUri { get; set; }
|
||||||
|
public string ClientName { get; set; }
|
||||||
|
public string SignOutIframeUrl { get; set; }
|
||||||
|
|
||||||
|
public bool AutomaticRedirectAfterSignOut { get; set; }
|
||||||
|
|
||||||
|
public string LogoutId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
10
IdentityServer/ViewModels/LoginViewModel.cs
Normal file
10
IdentityServer/ViewModels/LoginViewModel.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class LoginViewModel : LoginInputModel
|
||||||
|
{
|
||||||
|
public bool AllowRememberLogin { get; set; }
|
||||||
|
public bool EnableLocalLogin { get; set; }
|
||||||
|
}
|
||||||
|
}
|
18
IdentityServer/ViewModels/LoginWith2faViewModel.cs
Normal file
18
IdentityServer/ViewModels/LoginWith2faViewModel.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class LoginWith2faViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
|
||||||
|
[DataType(DataType.Text)]
|
||||||
|
[Display(Name = "Authenticator code")]
|
||||||
|
public string TwoFactorCode { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Remember this machine")]
|
||||||
|
public bool RememberMachine { get; set; }
|
||||||
|
|
||||||
|
public bool RememberMe { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/ViewModels/LoginWithRecoveryCodeViewModel.cs
Normal file
12
IdentityServer/ViewModels/LoginWithRecoveryCodeViewModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class LoginWithRecoveryCodeViewModel
|
||||||
|
{
|
||||||
|
[Required]
|
||||||
|
[DataType(DataType.Text)]
|
||||||
|
[Display(Name = "Recovery Code")]
|
||||||
|
public string RecoveryCode { get; set; }
|
||||||
|
}
|
||||||
|
}
|
9
IdentityServer/ViewModels/LogoutViewModel.cs
Normal file
9
IdentityServer/ViewModels/LogoutViewModel.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
using Teknik.IdentityServer.Models;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class LogoutViewModel : LogoutInputModel
|
||||||
|
{
|
||||||
|
public bool ShowLogoutPrompt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
12
IdentityServer/ViewModels/ScopeViewModel.cs
Normal file
12
IdentityServer/ViewModels/ScopeViewModel.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
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; }
|
||||||
|
}
|
||||||
|
}
|
11
IdentityServer/ViewModels/SubmitReportViewModel.cs
Normal file
11
IdentityServer/ViewModels/SubmitReportViewModel.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class SubmitReportViewModel
|
||||||
|
{
|
||||||
|
public string Message { get; set; }
|
||||||
|
|
||||||
|
public string Exception { get; set; }
|
||||||
|
|
||||||
|
public string CurrentUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
20
IdentityServer/ViewModels/ViewModelBase.cs
Normal file
20
IdentityServer/ViewModels/ViewModelBase.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Teknik.IdentityServer.ViewModels
|
||||||
|
{
|
||||||
|
public class ViewModelBase
|
||||||
|
{
|
||||||
|
public bool Error { get; set; }
|
||||||
|
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public ViewModelBase()
|
||||||
|
{
|
||||||
|
Error = false;
|
||||||
|
ErrorMessage = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
IdentityServer/Views/Account/AccessDenied.cshtml
Normal file
14
IdentityServer/Views/Account/AccessDenied.cshtml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Access denied";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 text-center">
|
||||||
|
<header>
|
||||||
|
<h2 class="text-danger">@ViewData["Title"]</h2>
|
||||||
|
<p class="text-danger">You do not have access to this resource.</p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
14
IdentityServer/Views/Account/Banned.cshtml
Normal file
14
IdentityServer/Views/Account/Banned.cshtml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Account Banned";
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 text-center">
|
||||||
|
<header>
|
||||||
|
<h2 class="text-danger">@ViewData["Title"]</h2>
|
||||||
|
<p class="text-danger">This account has been banned.</p>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
8
IdentityServer/Views/Account/Lockout.cshtml
Normal file
8
IdentityServer/Views/Account/Lockout.cshtml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@{
|
||||||
|
ViewData["Title"] = "Locked out";
|
||||||
|
}
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<h2 class="text-danger">@ViewData["Title"]</h2>
|
||||||
|
<p class="text-danger">This account has been locked out, please try again later.</p>
|
||||||
|
</header>
|
35
IdentityServer/Views/Account/LoggedOut.cshtml
Normal file
35
IdentityServer/Views/Account/LoggedOut.cshtml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
@model LoggedOutViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
// set this so the layout rendering sees an anonymous user
|
||||||
|
ViewData["signed-out"] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 text-center">
|
||||||
|
<h1>
|
||||||
|
Logout
|
||||||
|
<small>You are now logged out</small>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
@if (Model.PostLogoutRedirectUri != null)
|
||||||
|
{
|
||||||
|
<div>
|
||||||
|
Click <a class="PostLogoutRedirectUri" href="@Model.PostLogoutRedirectUri">here</a> to return to the
|
||||||
|
<span>@Model.ClientName</span> application.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.SignOutIframeUrl != null)
|
||||||
|
{
|
||||||
|
<iframe width="0" height="0" class="signout" src="@Model.SignOutIframeUrl"></iframe>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (Model.AutomaticRedirectAfterSignOut)
|
||||||
|
{
|
||||||
|
<bundle src="js/signout-redirect.min.js" append-version="true"></bundle>
|
||||||
|
}
|
61
IdentityServer/Views/Account/Login.cshtml
Normal file
61
IdentityServer/Views/Account/Login.cshtml
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
@model LoginViewModel
|
||||||
|
|
||||||
|
@{
|
||||||
|
string logoPath = "/images/logo-blue.svg";
|
||||||
|
|
||||||
|
// If we are using a CDN, let's replace it
|
||||||
|
if (Config.UseCdn)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Config.CdnHost))
|
||||||
|
{
|
||||||
|
logoPath = Config.CdnHost.TrimEnd('/') + logoPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Config.UserConfig.LoginEnabled)
|
||||||
|
{
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12 text-center">
|
||||||
|
<div class="row">
|
||||||
|
<img src="@logoPath" class="img-responsive center-block" alt="Teknik">
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
|
||||||
|
@await Html.PartialAsync("_ValidationSummary")
|
||||||
|
|
||||||
|
|
||||||
|
@if (Model.EnableLocalLogin)
|
||||||
|
{
|
||||||
|
<form class="form-horizontal" asp-route="Login">
|
||||||
|
<input type="hidden" asp-for="ReturnUrl" />
|
||||||
|
<div class="form-group">
|
||||||
|
<input class="form-control" placeholder="Username" asp-for="Username" autofocus>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<input type="password" class="form-control" placeholder="Password" asp-for="Password" autocomplete="off">
|
||||||
|
</div>
|
||||||
|
@if (Model.AllowRememberLogin)
|
||||||
|
{
|
||||||
|
<div class="form-group abc-checkbox">
|
||||||
|
<input asp-for="RememberMe">
|
||||||
|
<label asp-for="RememberMe">Remember My Login</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-primary" name="button" value="login">Log In</button>
|
||||||
|
<button class="btn btn-default" name="button" value="cancel">Cancel</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<h3>Authentication is currently disabled.</h3>
|
||||||
|
}
|
46
IdentityServer/Views/Account/LoginWith2fa.cshtml
Normal file
46
IdentityServer/Views/Account/LoginWith2fa.cshtml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
@model LoginWith2faViewModel
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 col-md-offset-2 text-center">
|
||||||
|
<h1>Two-factor authentication</h1>
|
||||||
|
<hr />
|
||||||
|
<p>Your login is protected with an authenticator app. Enter your authenticator code below.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
<form method="post" asp-route-returnUrl="@ViewData["ReturnUrl"]">
|
||||||
|
<input asp-for="RememberMe" type="hidden" />
|
||||||
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="TwoFactorCode"></label>
|
||||||
|
<input asp-for="TwoFactorCode" class="form-control" autocomplete="off" />
|
||||||
|
<span asp-validation-for="TwoFactorCode" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="abc-checkbox">
|
||||||
|
<input asp-for="RememberMachine" />
|
||||||
|
<label asp-for="RememberMachine">@Html.DisplayNameFor(m => m.RememberMachine)</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group pull-right">
|
||||||
|
<button type="submit" class="btn btn-default">Log in</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 col-md-offset-2 text-center">
|
||||||
|
<p>
|
||||||
|
Don't have access to your authenticator device? You can
|
||||||
|
<a asp-action="LoginWithRecoveryCode" asp-route-returnUrl="@ViewData["ReturnUrl"]">log in with a recovery code</a>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
31
IdentityServer/Views/Account/LoginWithRecoveryCode.cshtml
Normal file
31
IdentityServer/Views/Account/LoginWithRecoveryCode.cshtml
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
@model LoginWithRecoveryCodeViewModel
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-8 col-md-offset-2 text-center">
|
||||||
|
<h1>Recovery code verification</h1>
|
||||||
|
<hr />
|
||||||
|
<p>
|
||||||
|
You have requested to login with a recovery code. This login will not be remembered until you provide
|
||||||
|
an authenticator app code at login or disable 2FA and login again.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-4 col-md-offset-4">
|
||||||
|
<form method="post">
|
||||||
|
<div asp-validation-summary="All" class="text-danger"></div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="RecoveryCode"></label>
|
||||||
|
<input asp-for="RecoveryCode" class="form-control" autocomplete="off" />
|
||||||
|
<span asp-validation-for="RecoveryCode" class="text-danger"></span>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-default pull-right">Log in</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
23
IdentityServer/Views/Account/Logout.cshtml
Normal file
23
IdentityServer/Views/Account/Logout.cshtml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
@model LogoutViewModel
|
||||||
|
|
||||||
|
<div class="logout-page">
|
||||||
|
<div class="page-header">
|
||||||
|
<h1>Logout</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<p>Would you like to logout of IdentityServer?</p>
|
||||||
|
<form asp-action="Logout">
|
||||||
|
<input type="hidden" name="logoutId" value="@Model.LogoutId" />
|
||||||
|
<fieldset>
|
||||||
|
<div class="form-group">
|
||||||
|
<button class="btn btn-primary">Yes</button>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<bundle src="js/signout-redirect.min.js" append-version="true"></bundle>
|
82
IdentityServer/Views/Consent/Index.cshtml
Normal file
82
IdentityServer/Views/Consent/Index.cshtml
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
@model ConsentViewModel
|
||||||
|
|
||||||
|
<div class="page-consent">
|
||||||
|
<div class="row page-header">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
@if (Model.ClientLogoUrl != null)
|
||||||
|
{
|
||||||
|
<div class="client-logo"><img src="@Model.ClientLogoUrl"></div>
|
||||||
|
}
|
||||||
|
<h1>
|
||||||
|
@Model.ClientName
|
||||||
|
<small>is requesting your permission</small>
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-8">
|
||||||
|
@Html.Partial("_ValidationSummary")
|
||||||
|
|
||||||
|
<form asp-action="Index" class="consent-form">
|
||||||
|
<input type="hidden" asp-for="ReturnUrl" />
|
||||||
|
|
||||||
|
<div>Uncheck the permissions you do not wish to grant.</div>
|
||||||
|
|
||||||
|
@if (Model.IdentityScopes.Any())
|
||||||
|
{
|
||||||
|
<div class="panel panel-default consent-buttons">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<span class="glyphicon glyphicon-user"></span>
|
||||||
|
Personal Information
|
||||||
|
</div>
|
||||||
|
<ul class="list-group">
|
||||||
|
@foreach (var scope in Model.IdentityScopes)
|
||||||
|
{
|
||||||
|
@Html.Partial("_ScopeListItem", scope)
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.ResourceScopes.Any())
|
||||||
|
{
|
||||||
|
<div class="panel panel-default">
|
||||||
|
<div class="panel-heading">
|
||||||
|
<span class="glyphicon glyphicon-tasks"></span>
|
||||||
|
Application Access
|
||||||
|
</div>
|
||||||
|
<ul class="list-group">
|
||||||
|
@foreach (var scope in Model.ResourceScopes)
|
||||||
|
{
|
||||||
|
@Html.Partial("_ScopeListItem", scope)
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@if (Model.AllowRememberConsent)
|
||||||
|
{
|
||||||
|
<div class="consent-remember">
|
||||||
|
<label>
|
||||||
|
<input class="consent-scopecheck" asp-for="RememberConsent" />
|
||||||
|
<strong>Remember My Decision</strong>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div class="consent-buttons">
|
||||||
|
<button name="button" value="yes" class="btn btn-primary" autofocus>Yes, Allow</button>
|
||||||
|
<button name="button" value="no" class="btn">No, Do Not Allow</button>
|
||||||
|
@if (Model.ClientUrl != null)
|
||||||
|
{
|
||||||
|
<a class="pull-right btn btn-default" target="_blank" href="@Model.ClientUrl">
|
||||||
|
<span class="glyphicon glyphicon-info-sign"></span>
|
||||||
|
<strong>@Model.ClientName</strong>
|
||||||
|
</a>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
34
IdentityServer/Views/Consent/_ScopeListItem.cshtml
Normal file
34
IdentityServer/Views/Consent/_ScopeListItem.cshtml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
@model ScopeViewModel
|
||||||
|
|
||||||
|
<li class="list-group-item">
|
||||||
|
<label>
|
||||||
|
<input class="consent-scopecheck"
|
||||||
|
type="checkbox"
|
||||||
|
name="ScopesConsented"
|
||||||
|
id="scopes_@Model.Name"
|
||||||
|
value="@Model.Name"
|
||||||
|
checked="@Model.Checked"
|
||||||
|
disabled="@Model.Required" />
|
||||||
|
@if (Model.Required)
|
||||||
|
{
|
||||||
|
<input type="hidden"
|
||||||
|
name="ScopesConsented"
|
||||||
|
value="@Model.Name" />
|
||||||
|
}
|
||||||
|
<strong>@Model.DisplayName</strong>
|
||||||
|
@if (Model.Emphasize)
|
||||||
|
{
|
||||||
|
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||||
|
}
|
||||||
|
</label>
|
||||||
|
@if (Model.Required)
|
||||||
|
{
|
||||||
|
<span><em>(required)</em></span>
|
||||||
|
}
|
||||||
|
@if (Model.Description != null)
|
||||||
|
{
|
||||||
|
<div class="consent-description">
|
||||||
|
<label for="scopes_@Model.Name">@Model.Description</label>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</li>
|
47
IdentityServer/Views/Error/Exception.cshtml
Normal file
47
IdentityServer/Views/Error/Exception.cshtml
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
@model Teknik.IdentityServer.ViewModels.ErrorViewModel
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var submitErrorReportURL = '@Url.RouteUrl("Error.Action", new { action = "SubmitErrorReport" })';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="error-template text-center">
|
||||||
|
<h1>Unexpected Error</h1>
|
||||||
|
<div class="error-details">
|
||||||
|
<p>Uh oh! You aren't supposed to be seeing this. Something went horribly wrong.</p>
|
||||||
|
<div class="error-actions">
|
||||||
|
@if (Model != null && Model.Exception != null)
|
||||||
|
{
|
||||||
|
<div class="btn btn-primary btn-lg" id="submitErrorReport">
|
||||||
|
<span class="fa fa-bug"></span>
|
||||||
|
Submit Bug Report
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (Model != null && Model.Exception != null)
|
||||||
|
{
|
||||||
|
<br />
|
||||||
|
<div class="show-more" id="view-details">
|
||||||
|
<button role="button" class="btn btn-default btn-sm view-details-button" data-toggle="collapse" data-target="#errorMsg">Show Details</button>
|
||||||
|
</div>
|
||||||
|
<div class="text-left collapse" id="errorMsg">
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
<b>Exception:</b> @Model.Exception.GetFullMessage(true)
|
||||||
|
<br/>
|
||||||
|
<b>Source:</b> @Model.Exception.Source
|
||||||
|
</p>
|
||||||
|
<div style="overflow: scroll">
|
||||||
|
<pre>@Model.Exception.StackTrace</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<bundle src="js/error.min.js" append-version="true"></bundle>
|
15
IdentityServer/Views/Error/Http401.cshtml
Normal file
15
IdentityServer/Views/Error/Http401.cshtml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@model Teknik.IdentityServer.ViewModels.ErrorViewModel
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="error-template text-center">
|
||||||
|
<h2>401 Unauthorized</h2>
|
||||||
|
<div class="error-details">
|
||||||
|
The request has not been applied because it lacks valid authentication credentials for the target resource.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
16
IdentityServer/Views/Error/Http403.cshtml
Normal file
16
IdentityServer/Views/Error/Http403.cshtml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@model Teknik.IdentityServer.ViewModels.ErrorViewModel
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="error-template text-center">
|
||||||
|
<h1>Sorry Bud!</h1>
|
||||||
|
<h2>403 Access Denied</h2>
|
||||||
|
<div class="error-details">
|
||||||
|
You do not have access to this resource. Please contact an Administrator if you think this is an error.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
16
IdentityServer/Views/Error/Http404.cshtml
Normal file
16
IdentityServer/Views/Error/Http404.cshtml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@model Teknik.IdentityServer.ViewModels.ErrorViewModel
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="error-template text-center">
|
||||||
|
<h1>Uh Oh!</h1>
|
||||||
|
<h2>404 Not Found</h2>
|
||||||
|
<div class="error-details">
|
||||||
|
Unable to find the resource you are looking for. Please contact an Administrator if you think this is in error.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
48
IdentityServer/Views/Error/Http500.cshtml
Normal file
48
IdentityServer/Views/Error/Http500.cshtml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
@model Teknik.IdentityServer.ViewModels.ErrorViewModel
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var submitErrorReportURL = '@Url.RouteUrl("Error.Action", new { action = "SubmitErrorReport" })';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="error-template text-center">
|
||||||
|
<h1>Whoops!</h1>
|
||||||
|
<h2>500 Server Error</h2>
|
||||||
|
<div class="error-details">
|
||||||
|
<p>Sorry, a server error occured. Please contact an Administrator about this error.</p>
|
||||||
|
<div class="error-actions">
|
||||||
|
@if (Model != null && Model.Exception != null)
|
||||||
|
{
|
||||||
|
<div class="btn btn-primary btn-lg" id="submitErrorReport">
|
||||||
|
<span class="fa fa-bug"></span>
|
||||||
|
Submit Bug Report
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
@if (Model != null && Model.Exception != null)
|
||||||
|
{
|
||||||
|
<br />
|
||||||
|
<div class="show-more" id="view-details">
|
||||||
|
<button role="button" class="btn btn-default btn-sm view-details-button" data-toggle="collapse" data-target="#errorMsg">Show Details</button>
|
||||||
|
</div>
|
||||||
|
<div class="text-left collapse" id="errorMsg">
|
||||||
|
<br />
|
||||||
|
<p>
|
||||||
|
<b>Exception:</b> @Model.Exception.GetFullMessage(true)
|
||||||
|
<br/>
|
||||||
|
<b>Source:</b> @Model.Exception.Source
|
||||||
|
</p>
|
||||||
|
<div style="overflow: scroll">
|
||||||
|
<pre>@Model.Exception.StackTrace</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<bundle src="error.min.js" append-version="true"></bundle>
|
16
IdentityServer/Views/Error/HttpGeneral.cshtml
Normal file
16
IdentityServer/Views/Error/HttpGeneral.cshtml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
@model Teknik.IdentityServer.ViewModels.ErrorViewModel
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="error-template text-center">
|
||||||
|
<h1>Http Error</h1>
|
||||||
|
<h2>Status Code: @Model.StatusCode</h2>
|
||||||
|
<div class="error-details">
|
||||||
|
Sorry, an error has occured: @Model.Description
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
15
IdentityServer/Views/Error/IdentityError.cshtml
Normal file
15
IdentityServer/Views/Error/IdentityError.cshtml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
@model Teknik.IdentityServer.ViewModels.IdentityErrorViewModel
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="error-template text-center">
|
||||||
|
<h1>Identity Error Occured</h1>
|
||||||
|
<h2>@Model.Title</h2>
|
||||||
|
<div class="error-details">
|
||||||
|
<p>@Model.Description</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user