Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 42 additions & 43 deletions DuendeIdentityServer/DuendeDynamicProviders/Config.cs
Original file line number Diff line number Diff line change
@@ -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<IdentityResource> GetIdentityResources()
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
return new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
}
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};

public static IEnumerable<ApiResource> GetApis()
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
return new ApiResource[]
{
new ApiResource("api1", "My API #1")
};
}
new ApiScope("scope1"),
new ApiScope("scope2"),
};

public static IEnumerable<ApiScope> GetApiScopes()
public static IEnumerable<Client> Clients =>
new Client[]
{
return new ApiScope[]
// m2m client credentials flow client
new Client
{
new ApiScope("scope1"),
new ApiScope("scope2"),
};
}

public static IEnumerable<Client> 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"}
}
};
}
}
}
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" }
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<ItemGroup>
<PackageReference Include="Duende.IdentityServer" Version="6.0.0" />
<PackageReference Include="Rsk.Saml" Version="5.0.0" />
<PackageReference Include="Rsk.Saml.DuendeIdentityServer" Version="6.0.0" />

<PackageReference Include="Serilog.AspNetCore" Version="3.4.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
</ItemGroup>

<ItemGroup>
<None Update="testclient.cer">
<None Update="idsrv3test.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="testclient.pfx">
<None Update="testclient.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="idsrv3test.cer">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>



</Project>
99 changes: 99 additions & 0 deletions DuendeIdentityServer/DuendeDynamicProviders/HostingExtensions.cs
Original file line number Diff line number Diff line change
@@ -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<SamlIdentityProviderStore>();

// use in memory store for storing identity providers
.AddInMemoryIdentityProviders(new List<SamlDynamicIdentityProvider>
{
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
@page
@model DuendeDynamicProviders.Pages.Account.AccessDeniedModel
@{
}
<div class="row">
<div class="col">
<h1>Access Denied</h1>
<p>You do not have permission to access that resource.</p>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace DuendeDynamicProviders.Pages.Account
{
public class AccessDeniedModel : PageModel
{
public void OnGet()
{
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
@model LoginViewModel
@page
@model DuendeDynamicProviders.Pages.Login.Index

<div class="login-page">
<div class="lead">
Expand All @@ -10,7 +11,7 @@

<div class="row">

@if (Model.EnableLocalLogin)
@if (Model.View.EnableLocalLogin)
{
<div class="col-sm-6">
<div class="card">
Expand All @@ -19,37 +20,39 @@
</div>

<div class="card-body">
<form asp-route="Login">
<input type="hidden" asp-for="ReturnUrl" />
<form asp-page="/Account/Login/Index">
<input type="hidden" asp-for="Input.ReturnUrl" />

<div class="form-group">
<label asp-for="Username"></label>
<input class="form-control" placeholder="Username" asp-for="Username" autofocus>
<label asp-for="Input.Username"></label>
<input class="form-control" placeholder="Username" asp-for="Input.Username" autofocus>
</div>
<div class="form-group">
<label asp-for="Password"></label>
<input type="password" class="form-control" placeholder="Password" asp-for="Password" autocomplete="off">
<label asp-for="Input.Password"></label>
<input type="password" class="form-control" placeholder="Password" asp-for="Input.Password" autocomplete="off">
</div>
@if (Model.AllowRememberLogin)

@if (Model.View.AllowRememberLogin)
{
<div class="form-group">
<div class="form-check">
<input class="form-check-input" asp-for="RememberLogin">
<label class="form-check-label" asp-for="RememberLogin">
<input class="form-check-input" asp-for="Input.RememberLogin">
<label class="form-check-label" asp-for="Input.RememberLogin">
Remember My Login
</label>
</div>
</div>
}
<button class="btn btn-primary" name="button" value="login">Login</button>
<button class="btn btn-secondary" name="button" value="cancel">Cancel</button>

<button class="btn btn-primary" name="Input.Button" value="login">Login</button>
<button class="btn btn-secondary" name="Input.Button" value="cancel">Cancel</button>
</form>
</div>
</div>
</div>
}

@if (Model.VisibleExternalProviders.Any())
@if (Model.View.VisibleExternalProviders.Any())
{
<div class="col-sm-6">
<div class="card">
Expand All @@ -58,14 +61,13 @@
</div>
<div class="card-body">
<ul class="list-inline">
@foreach (var provider in Model.VisibleExternalProviders)
@foreach (var provider in Model.View.VisibleExternalProviders)
{
<li class="list-inline-item">
<a class="btn btn-secondary"
asp-controller="External"
asp-action="Challenge"
asp-page="/ExternalLogin/Challenge"
asp-route-scheme="@provider.AuthenticationScheme"
asp-route-returnUrl="@Model.ReturnUrl">
asp-route-returnUrl="@Model.Input.ReturnUrl">
@provider.DisplayName
</a>
</li>
Expand All @@ -76,7 +78,7 @@
</div>
}

@if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
@if (!Model.View.EnableLocalLogin && !Model.View.VisibleExternalProviders.Any())
{
<div class="alert alert-warning">
<strong>Invalid login request</strong>
Expand Down
Loading