diff --git a/DuendeIdentityServer/DuendeDynamicProviders/Config.cs b/DuendeIdentityServer/DuendeDynamicProviders/Config.cs index 9d36347..74c9530 100644 --- a/DuendeIdentityServer/DuendeDynamicProviders/Config.cs +++ b/DuendeIdentityServer/DuendeDynamicProviders/Config.cs @@ -1,53 +1,52 @@ -// Copyright (c) Duende Software. All rights reserved. -// See LICENSE in the project root for license information. +using Duende.IdentityServer.Models; +namespace DuendeDynamicProviders; -using Duende.IdentityServer.Models; -using System.Collections.Generic; -using Duende.IdentityServer; - -namespace DuendeDynamicProviders +public static class Config { - public static class Config - { - public static IEnumerable GetIdentityResources() + public static IEnumerable IdentityResources => + new IdentityResource[] { - return new IdentityResource[] - { - new IdentityResources.OpenId(), - new IdentityResources.Profile(), - }; - } + new IdentityResources.OpenId(), + new IdentityResources.Profile(), + }; - public static IEnumerable GetApis() + public static IEnumerable ApiScopes => + new ApiScope[] { - return new ApiResource[] - { - new ApiResource("api1", "My API #1") - }; - } + new ApiScope("scope1"), + new ApiScope("scope2"), + }; - public static IEnumerable GetApiScopes() + public static IEnumerable Clients => + new Client[] { - return new ApiScope[] + // m2m client credentials flow client + new Client { - new ApiScope("scope1"), - new ApiScope("scope2"), - }; - } - - public static IEnumerable GetClients() - { - return new[] + ClientId = "m2m.client", + ClientName = "Client Credentials Client", + + AllowedGrantTypes = GrantTypes.ClientCredentials, + ClientSecrets = { new Secret("511536EF-F270-4058-80CA-1C89C192F69A".Sha256()) }, + + AllowedScopes = { "scope1" } + }, + + // interactive client using code flow + pkce + new Client { - new Client - { - ClientId = "https://localhost:5002", - ClientName = "client", - ProtocolType = IdentityServerConstants.ProtocolTypes.OpenIdConnect, - AllowedScopes = {"openid", "profile"} - } - }; - } - } -} \ No newline at end of file + ClientId = "interactive", + ClientSecrets = { new Secret("49C1A7E1-0C79-4A89-A3D6-A37998FB86B0".Sha256()) }, + + AllowedGrantTypes = GrantTypes.Code, + + RedirectUris = { "https://localhost:44300/signin-oidc" }, + FrontChannelLogoutUri = "https://localhost:44300/signout-oidc", + PostLogoutRedirectUris = { "https://localhost:44300/signout-callback-oidc" }, + + AllowOfflineAccess = true, + AllowedScopes = { "openid", "profile", "scope2" } + }, + }; +} diff --git a/DuendeIdentityServer/DuendeDynamicProviders/DuendeDynamicProviders.csproj b/DuendeIdentityServer/DuendeDynamicProviders/DuendeDynamicProviders.csproj index f9762d1..d6c5ec5 100644 --- a/DuendeIdentityServer/DuendeDynamicProviders/DuendeDynamicProviders.csproj +++ b/DuendeIdentityServer/DuendeDynamicProviders/DuendeDynamicProviders.csproj @@ -2,26 +2,26 @@ net6.0 + enable - + - + - + PreserveNewest - + PreserveNewest - - PreserveNewest - + + \ No newline at end of file diff --git a/DuendeIdentityServer/DuendeDynamicProviders/HostingExtensions.cs b/DuendeIdentityServer/DuendeDynamicProviders/HostingExtensions.cs new file mode 100644 index 0000000..e413222 --- /dev/null +++ b/DuendeIdentityServer/DuendeDynamicProviders/HostingExtensions.cs @@ -0,0 +1,99 @@ +using System.Security.Cryptography.X509Certificates; +using Duende.IdentityServer; +using Rsk.AspNetCore.Authentication.Saml2p; +using Rsk.Saml.DuendeIdentityServer.DynamicProviders; +using Serilog; + +namespace DuendeDynamicProviders; + +internal static class HostingExtensions +{ + public static WebApplication ConfigureServices(this WebApplicationBuilder builder) + { + builder.Services.AddRazorPages(); + + var isBuilder = builder.Services.AddIdentityServer(options => + { + options.Events.RaiseErrorEvents = true; + options.Events.RaiseInformationEvents = true; + options.Events.RaiseFailureEvents = true; + options.Events.RaiseSuccessEvents = true; + + // see https://docs.duendesoftware.com/identityserver/v6/fundamentals/resources/ + options.EmitStaticAudienceClaim = true; + }) + .AddTestUsers(TestUsers.Users); + + // in-memory, code config + isBuilder.AddInMemoryIdentityResources(Config.IdentityResources); + isBuilder.AddInMemoryApiScopes(Config.ApiScopes); + isBuilder.AddInMemoryClients(Config.Clients); + + // SP configuration - dynamic providers + isBuilder.AddSamlDynamicProvider(options => + { + // unstorable/reusable data, such as license information and events. This will override the data stored + options.Licensee = "/* your DEMO Licensee */"; + options.LicenseKey = "/* your DEMO LicenseKey */"; + }) + + // Use EntityFramework store for storing identity providers + //.AddIdentityProviderStore(); + + // use in memory store for storing identity providers + .AddInMemoryIdentityProviders(new List + { + new SamlDynamicIdentityProvider + { + SamlAuthenticationOptions = new Saml2pAuthenticationOptions + { + // The IdP you want to integrate with + IdentityProviderOptions = new IdpOptions + { + EntityId = "https://localhost:5000", + SigningCertificates = { new X509Certificate2("idsrv3test.cer") }, + SingleSignOnEndpoint = new SamlEndpoint("https://localhost:5000/saml/sso", SamlBindingTypes.HttpRedirect), + SingleLogoutEndpoint = new SamlEndpoint("https://localhost:5000/saml/slo", SamlBindingTypes.HttpRedirect) + }, + + // Details about yourself (the SP) + ServiceProviderOptions = new SpOptions + { + EntityId = "https://localhost:5004/saml", + MetadataPath = "/federation/saml/metadata", + SignAuthenticationRequests = false // OPTIONAL - use if you want to sign your auth requests + }, + + NameIdClaimType = "sub", + CallbackPath = "/federation/saml/signin-saml", // Duende prefixes "/federation/{scheme}" to all paths + SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme, + }, + + Scheme = "saml", + DisplayName = "saml", + Enabled = true, + } + }); + + builder.Services.AddAuthentication(); + + return builder.Build(); + } + + public static WebApplication ConfigurePipeline(this WebApplication app) + { + app.UseSerilogRequestLogging(); + + app.UseDeveloperExceptionPage(); + + app.UseStaticFiles(); + app.UseRouting(); + app.UseIdentityServer(); + app.UseAuthorization(); + + app.MapRazorPages() + .RequireAuthorization(); + + return app; + } +} \ No newline at end of file diff --git a/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/AccessDenied.cshtml b/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/AccessDenied.cshtml new file mode 100644 index 0000000..64cf7f9 --- /dev/null +++ b/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/AccessDenied.cshtml @@ -0,0 +1,10 @@ +@page +@model DuendeDynamicProviders.Pages.Account.AccessDeniedModel +@{ +} +
+
+

Access Denied

+

You do not have permission to access that resource.

+
+
\ No newline at end of file diff --git a/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/AccessDenied.cshtml.cs b/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/AccessDenied.cshtml.cs new file mode 100644 index 0000000..1eea763 --- /dev/null +++ b/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/AccessDenied.cshtml.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace DuendeDynamicProviders.Pages.Account +{ + public class AccessDeniedModel : PageModel + { + public void OnGet() + { + } + } +} diff --git a/DuendeIdentityServer/DuendeIdP/Views/Account/Login.cshtml b/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/Login/Index.cshtml similarity index 69% rename from DuendeIdentityServer/DuendeIdP/Views/Account/Login.cshtml rename to DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/Login/Index.cshtml index e4ccb1d..f58fe43 100644 --- a/DuendeIdentityServer/DuendeIdP/Views/Account/Login.cshtml +++ b/DuendeIdentityServer/DuendeDynamicProviders/Pages/Account/Login/Index.cshtml @@ -1,4 +1,5 @@ -@model LoginViewModel +@page +@model DuendeDynamicProviders.Pages.Login.Index