امن سازی برنامههای ASP.NET Core توسط IdentityServer 4x - قسمت چهاردهم- آماده شدن برای انتشار برنامه
نویسنده: وحید نصیری
تاریخ: ۱۳۹۷/۰۶/۳۰ ۱۲:۱۵
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
namespace DNT.IDP
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddCustomUserStore()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients()); makecert.exe -r -pe -n "CN=DntIdpSigningCert" -b 01/01/2018 -e 01/01/2025 -eku 1.3.6.1.5.5.7.3.3 -sky signature -a sha256 -len 2048 -ss my -sr LocalMachine
namespace DNT.IDP
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddSigningCredential(loadCertificateFromStore())
.AddCustomUserStore()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients());
}
private X509Certificate2 loadCertificateFromStore()
{
var thumbPrint = Configuration["CertificateThumbPrint"];
using (var store = new X509Store(StoreName.My, StoreLocation.LocalMachine))
{
store.Open(OpenFlags.ReadOnly);
var certCollection = store.Certificates.Find(X509FindType.FindByThumbprint, thumbPrint, true);
if (certCollection.Count == 0)
{
throw new Exception("The specified certificate wasn't found.");
}
return certCollection[0];
}
}
private X509Certificate2 loadCertificateFromFile()
{
// NOTE:
// You should check out the identity of your application pool and make sure
// that the `Load user profile` option is turned on, otherwise the crypto susbsystem won't work.
var certificate = new X509Certificate2(
fileName: Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "app_data",
Configuration["X509Certificate:FileName"]),
password: Configuration["X509Certificate:Password"],
keyStorageFlags: X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
return certificate;
}
}
} https://localhost:6001/.well-known/openid-configuration https://localhost:6001/.well-known/openid-configuration/jwks
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ComponentModel.Annotations" Version="4.3.0" />
<PackageReference Include="IdentityServer4" Version="2.2.0" />
</ItemGroup>
</Project> namespace IdentityServer4.EntityFramework.Options
{
public class OperationalStoreOptions
{
public bool EnableTokenCleanup { get; set; } = false;
public int TokenCleanupInterval { get; set; } = 3600;
public int TokenCleanupBatchSize { get; set; } = 100;
}
} public TokenCleanup( IServiceProvider serviceProvider, ILogger<TokenCleanup> logger, IOptions<OperationalStoreOptions> options)
namespace DNT.IDP
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddSigningCredential(loadCertificateFromStore())
.AddCustomUserStore()
.AddConfigurationStore()
.AddOperationalStore(); public interface IConfigSeedDataService
{
void EnsureSeedDataForContext(
IEnumerable<IdentityServer4.Models.Client> clients,
IEnumerable<IdentityServer4.Models.ApiResource> apiResources,
IEnumerable<IdentityServer4.Models.IdentityResource> identityResources);
} namespace DNT.IDP
{
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// ...
initializeDb(app);
seedDb(app);
// ...
}
private static void seedDb(IApplicationBuilder app)
{
var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var configSeedDataService = scope.ServiceProvider.GetService<IConfigSeedDataService>();
configSeedDataService.EnsureSeedDataForContext(
Config.GetClients(),
Config.GetApiResources(),
Config.GetIdentityResources()
);
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
IdentityModelEventSource.ShowPII = true;
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
options.DefaultSignOutScheme = "oidc";
})
.AddCookie("Cookies", options =>
{
options.AccessDeniedPath = "/Authorization/AccessDenied";
// set session lifetime
options.ExpireTimeSpan = TimeSpan.FromHours(8);
// sliding or absolute
options.SlidingExpiration = false;
// host prefixed cookie name
options.Cookie.Name = "MVC";
// strict SameSite handling
options.Cookie.SameSite = SameSiteMode.Strict;
})
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = Configuration["IDPBaseAddress"];
options.ClientId = Configuration["ClientId"];
options.ClientSecret = Configuration["ClientSecret"];
options.ResponseType = "code id_token";
options.ResponseMode = "query";
options.RequireHttpsMetadata = false;
options.CallbackPath = new PathString("/Home/");
options.SignedOutCallbackPath = new PathString("/Home/");
options.MapInboundClaims = true;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("roles");
options.Scope.Add("PS.WebApi.Read");
options.Scope.Add("offline_access");
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
//options.UsePkce = true;
//options.ClaimActions.MapJsonKey(claimType: "role", jsonKey: "role"); // for having 2 or more roles
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = JwtClaimTypes.GivenName,
RoleClaimType = JwtClaimTypes.Role
};
});
//ServicePointManager.Expect100Continue = true;
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
// | SecurityProtocolType.Tls11
// | SecurityProtocolType.Tls12
// | SecurityProtocolType.Ssl3;
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "areas",
pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"
);
//.RequireAuthorization();
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
//.RequireAuthorization();
});
//[HttpPost]
//public IActionResult Logout()
//{
// return SignOut("Cookies", "oidc");
//}
} {
"IdentityServerData": {
"IdentityResources": [
{
"Name": "roles",
"Enabled": true,
"DisplayName": "Roles",
"UserClaims": [
"role"
]
},
{
"Name": "openid",
"Enabled": true,
"Required": true,
"DisplayName": "Your user identifier",
"UserClaims": [
"sub"
]
},
{
"Name": "profile",
"Enabled": true,
"DisplayName": "User profile",
"Description": "Your user profile information (first name, last name, etc.)",
"Emphasize": true,
"UserClaims": [
"name",
"family_name",
"given_name",
"middle_name",
"nickname",
"preferred_username",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"updated_at"
]
},
{
"Name": "email",
"Enabled": true,
"DisplayName": "Your email address",
"Emphasize": true,
"UserClaims": [
"email",
"email_verified"
]
},
{
"Name": "address",
"Enabled": true,
"DisplayName": "Your address",
"Emphasize": true,
"UserClaims": [
"address"
]
}
],
"ApiScopes": [
{
"Name": "Idp_Admin_ClientId_api",
"DisplayName": "Idp_Admin_ClientId_api",
"Required": true,
"UserClaims": [
"role",
"name"
]
},
{
"Name": "WebApi.Read",
"DisplayName": "WebApi Read",
"Required": true,
"UserClaims": [
"role",
"WebApi.Read"
]
},
{
"Name": "WebApi.Write",
"DisplayName": "WebApi Write",
"Required": true,
"UserClaims": [
"role",
"WebApi.Write"
]
}
],
"ApiResources": [
{
"Name": "Idp_Admin_ClientId_api",
"Scopes": [
"Idp_Admin_ClientId_api"
]
},
{
"Name": "WebApi",
"Scopes": [
"WebApi.Read",
"WebApi.Write"
]
}
],
"Clients": [
{
"ClientId": "Idp_Admin_ClientId",
"ClientName": "Idp_Admin_ClientId",
"ClientUri": "https://localhost:44303",
"AllowedGrantTypes": [
"authorization_code"
],
"RequirePkce": true,
"ClientSecrets": [
{
"Value": "Idp_Admin_ClientSecret"
}
],
"RedirectUris": [
"https://localhost:44303/signin-oidc"
],
"FrontChannelLogoutUri": "https://localhost:44303/signout-oidc",
"PostLogoutRedirectUris": [
"https://localhost:44303/signout-callback-oidc"
],
"AllowedCorsOrigins": [
"https://localhost:44303"
],
"AllowedScopes": [
"openid",
"email",
"profile",
"roles"
]
},
{
"ClientId": "Idp_Admin_ClientId_api_swaggerui",
"ClientName": "Idp_Admin_ClientId_api_swaggerui",
"AllowedGrantTypes": [
"authorization_code"
],
"RequireClientSecret": false,
"RequirePkce": true,
"RedirectUris": [
"https://localhost:44302/swagger/oauth2-redirect.html"
],
"AllowedScopes": [
"Idp_Admin_ClientId_api"
],
"AllowedCorsOrigins": [
"https://localhost:44302"
]
},
//WebApi
{
"ClientId": "WebApi_ClientId",
"ClientName": "WebApi_ClientId",
"ClientUri": "https://localhost:44365",
"AllowedGrantTypes": [
"authorization_code"
],
"RequirePkce": true,
"ClientSecrets": [
{
"Value": "WebApi"
}
],
"RedirectUris": [
"https://localhost:44303/signin-oidc"
],
"FrontChannelLogoutUri": "https://localhost:44303/signout-oidc",
"PostLogoutRedirectUris": [
"https://localhost:44303/signout-callback-oidc"
],
"AllowedCorsOrigins": [
"https://localhost:44303",
"https://localhost:44310"
],
"AllowedScopes": [
"openid",
"email",
"profile",
"roles"
]
},
//Mvc
{
"ClientId": "Mvc_ClientId",
"ClientName": "Mvc_ClientId",
"ClientUri": "https://localhost:44332",
"AllowedGrantTypes": [
"hybrid"
],
//"RequirePkce": true,
"AllowPlainTextPkce": false,
"ClientSecrets": [
{
"Value": "WebMvc"
}
],
"RedirectUris": [
"https://localhost:44332/signin-oidc"
],
"FrontChannelLogoutUri": "https://localhost:44332/signout-oidc",
"PostLogoutRedirectUris": [
"https://localhost:44332/signout-callback-oidc"
],
"AllowedCorsOrigins": [
"https://localhost:44332",
"https://localhost:44310"
],
"AllowedScopes": [
"openid",
"email",
"profile",
"roles",
"address",
"PS.webApi"
],
"AllowAccessTokensViaBrowser": true,
"RequireConsent": false,
"AllowOfflineAccess": true
//"UpdateAccessTokenClaimsOnRefresh": true
}
]
}
} public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Default()
{
return View();
}
public IActionResult Index()
{
return View();
}
[Authorize]
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
} [Authorize]میباشد با خطای زیر مواجه میشوم:
لازم به توضیح هست که پروپرتی RequireHttpsMetadata = false میباشد.
"Clients": [
{
"ClientId": "skoruba_identity_admin",
"ClientName": "skoruba_identity_admin",
"ClientUri": "https://localhost:44303",
"AllowedGrantTypes": [
"authorization_code"
],
"RequirePkce": true,
"ClientSecrets": [
{
"Value": "skoruba_admin_client_secret"
}
],
"RedirectUris": [
"https://localhost:44303/signin-oidc"
],
"FrontChannelLogoutUri": "https://localhost:44303/signout-oidc",
"PostLogoutRedirectUris": [
"https://localhost:44303/signout-callback-oidc"
],
"AllowedCorsOrigins": [
"https://localhost:44303"
],
"AllowedScopes": [
"openid",
"email",
"profile",
"roles"
]
},
{
"ClientId": "skoruba_identity_admin_api_swaggerui",
"ClientName": "skoruba_identity_admin_api_swaggerui",
"AllowedGrantTypes": [
"authorization_code"
],
"RequireClientSecret": false,
"RequirePkce": true,
"RedirectUris": [
"https://localhost:44302/swagger/oauth2-redirect.html"
],
"AllowedScopes": [
"skoruba_identity_admin_api"
],
"AllowedCorsOrigins": [
"https://localhost:44302"
]
}
] IDX20804: Unable to retrieve document from: 'https://localhost/.well-known/openid-configuration'. ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
.AddOpenIdConnect("oidc", options =>
{
options.RequireHttpsMetadata = false;
// ...
.AddIdentityServerAuthentication(options =>
{
options.RequireHttpsMetadata = false;
// ...