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
31 changes: 19 additions & 12 deletions src/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,21 +460,26 @@ public class Config
{
EntityId = "https://saml-sp1.example.com",
DisplayName = "Simple SAML SP",
AssertionConsumerServiceUrls = [new Uri("https://saml-sp1.example.com/acs")],
AssertionConsumerServiceBinding = SamlBinding.HttpPost,
AssertionConsumerServiceUrls = new List<IndexedEndpoint>
{
new IndexedEndpoint { Location = "https://saml-sp1.example.com/acs", Binding = SamlBinding.HttpPost, Index = 0, IsDefault = true }
},
AllowedScopes = { "openid", "profile" },
},

// SP with Single Logout
new SamlServiceProvider
{
EntityId = "https://saml-sp2.example.com",
DisplayName = "SAML SP with SLO",
AssertionConsumerServiceUrls = [new Uri("https://saml-sp2.example.com/acs")],
AssertionConsumerServiceBinding = SamlBinding.HttpPost,
SingleLogoutServiceUrl = new SamlEndpointType
AssertionConsumerServiceUrls = new List<IndexedEndpoint>
{
new IndexedEndpoint { Location = "https://saml-sp2.example.com/acs", Binding = SamlBinding.HttpPost, Index = 0, IsDefault = true }
},
AllowedScopes = { "openid", "profile" },
SingleLogoutServiceUrls = new List<SamlEndpointType>
{
Location = new Uri("https://saml-sp2.example.com/saml/slo"),
Binding = SamlBinding.HttpPost,
new SamlEndpointType { Location = "https://saml-sp2.example.com/saml/slo", Binding = SamlBinding.HttpPost }
},
},

Expand All @@ -483,13 +488,15 @@ public class Config
{
EntityId = "https://saml-sp3.example.com",
DisplayName = "SAML SP (IdP-initiated)",
AssertionConsumerServiceUrls = [new Uri("https://saml-sp3.example.com/acs")],
AssertionConsumerServiceBinding = SamlBinding.HttpPost,
AssertionConsumerServiceUrls = new List<IndexedEndpoint>
{
new IndexedEndpoint { Location = "https://saml-sp3.example.com/acs", Binding = SamlBinding.HttpPost, Index = 0, IsDefault = true }
},
AllowIdpInitiated = true,
SingleLogoutServiceUrl = new SamlEndpointType
AllowedScopes = { "openid", "profile" },
SingleLogoutServiceUrls = new List<SamlEndpointType>
{
Location = new Uri("https://saml-sp3.example.com/saml/slo"),
Binding = SamlBinding.HttpPost,
new SamlEndpointType { Location = "https://saml-sp3.example.com/saml/slo", Binding = SamlBinding.HttpPost }
},
},
};
Expand Down
8 changes: 4 additions & 4 deletions src/Duende.IdentityServer.Demo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Duende.IdentityModel" Version="8.0.1" />
<PackageReference Include="Duende.IdentityServer" Version="8.0.0-alpha.1" />
<PackageReference Include="Duende.IdentityServer.ConformanceReport" Version="0.1.0-alpha.0.3488" />
<PackageReference Include="Duende.AspNetCore.Authentication.JwtBearer" Version="1.0.1" />
<PackageReference Include="Duende.IdentityModel" Version="8.1.0" />
<PackageReference Include="Duende.IdentityServer" Version="8.0.0" />
<PackageReference Include="Duende.IdentityServer.ConformanceReport" Version="8.0.0" />
<PackageReference Include="Duende.AspNetCore.Authentication.JwtBearer" Version="1.0.2" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
</ItemGroup>

Expand Down
2 changes: 0 additions & 2 deletions src/HostingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
UseX509Certificate = true
});
options.KeyManagement.KeyPath = "/tmp/keys";

options.Endpoints.EnableSamlIdpInitiatedEndpoint = true;
})
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryIdentityResources(Config.IdentityResources)
Expand Down
2 changes: 1 addition & 1 deletion src/Pages/Account/Create/Index.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@

public Index(
IIdentityServerInteractionService interaction,
TestUserStore? users = null)

Check warning on line 27 in src/Pages/Account/Create/Index.cshtml.cs

View workflow job for this annotation

GitHub Actions / test

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
// this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity)
_users = users ?? throw new InvalidOperationException("Please call 'AddTestUsers(TestUsers.Users)' on the IIdentityServerBuilder in Startup or remove the TestUserStore from the AccountController.");
Expand All @@ -32,7 +32,7 @@
_interaction = interaction;
}

public IActionResult OnGet(string? returnUrl)

Check warning on line 35 in src/Pages/Account/Create/Index.cshtml.cs

View workflow job for this annotation

GitHub Actions / test

The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
{
Input = new InputModel { ReturnUrl = returnUrl };
return Page();
Expand All @@ -51,7 +51,7 @@
// 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.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied, HttpContext.RequestAborted);
await _interaction.DenyAuthorizationAsync(context, InteractionError.AccessDenied, HttpContext.RequestAborted);

// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
Expand Down
2 changes: 1 addition & 1 deletion src/Pages/Account/Login/Index.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public async Task<IActionResult> OnPost()
// 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.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied, HttpContext.RequestAborted);
await _interaction.DenyAuthorizationAsync(context, InteractionError.AccessDenied, HttpContext.RequestAborted);

// we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null
if (context.IsNativeClient())
Expand Down
2 changes: 1 addition & 1 deletion src/Pages/Consent/Index.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task<IActionResult> OnPost()
// user clicked 'no' - send back the standard 'access_denied' response
if (Input.Button == "no")
{
grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied };
grantedConsent = new ConsentResponse { Error = InteractionError.AccessDenied };

// emit event
await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues), HttpContext.RequestAborted);
Expand Down
2 changes: 1 addition & 1 deletion src/Pages/Device/Index.cshtml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public async Task<IActionResult> OnPost()
{
grantedConsent = new ConsentResponse
{
Error = AuthorizationError.AccessDenied
Error = InteractionError.AccessDenied
};

// emit event
Expand Down
19 changes: 12 additions & 7 deletions src/Pages/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
<section class="text-center container border-bottom pb-3 mb-5">
<div class="row py-lg-2">
<div class="col-lg-6 col-md-8 mx-auto">
<img src="duende-logo.svg" alt="Duende Software Logo" class="img-fluid w-75"/>
<h1 class="fw-light">
<a class="text-decoration-none" href="https://duendesoftware.com/products/identityserver">Welcome to Duende IdentityServer</a>
</h1>
Expand Down Expand Up @@ -72,7 +71,7 @@
</p>
</div>
<div class="card-footer p-0">
<a href="/saml/metadata" rel="nofollow" class="btn btn-primary w-100">
<a href="/Saml2" rel="nofollow" class="btn btn-primary w-100">
Go
<i class="glyphicon glyphicon-chevron-right"></i>
</a>
Expand Down Expand Up @@ -324,7 +323,7 @@
<h2 id="saml-service-providers" style="scroll-margin-top: 3em;">Demo SAML Service Providers (alpha)</h2>
<p>
This demo server also acts as a SAML 2.0 Identity Provider. The following service providers are configured for testing.
Use the <a href="/saml/metadata">SAML metadata endpoint</a> to retrieve the IdP metadata for configuring your SP.
Use the <a href="/Saml2">SAML metadata endpoint</a> to retrieve the IdP metadata for configuring your SP.
</p>

<div id="saml-accordion" class="mb-3">
Expand All @@ -339,14 +338,20 @@
<div class="card-body">
<div class="card-text">
Entity ID: <code>@sp.EntityId</code><br/>
ACS Binding: <code>@sp.AssertionConsumerServiceBinding</code><br/>
@foreach (var acs in sp.AssertionConsumerServiceUrls)
{
<text>ACS URL: <code>@acs</code><br/></text>
<text>ACS URL: <code>@acs.Location</code> (<code>@acs.Binding</code>)<br/></text>
}
@if (sp.SingleLogoutServiceUrl != null)
@if (sp.SingleLogoutServiceUrls.Any())
{
<text>SLO URL: <code>@sp.SingleLogoutServiceUrl.Location</code><br/></text>
@foreach (var slo in sp.SingleLogoutServiceUrls)
{
<text>SLO URL: <code>@slo.Location</code><br/></text>
}
}
@foreach (var slo in sp.SingleLogoutServiceUrls)
{
<text>SLO URL: <code>@slo.Location</code><br/></text>
}
@if (sp.AllowIdpInitiated)
{
Expand Down
7 changes: 0 additions & 7 deletions src/Pages/Index.cshtml.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.

using Duende.IdentityServer;
using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
Expand All @@ -11,17 +10,11 @@ namespace IdentityServerHost.Pages.Home;
[AllowAnonymous]
public class Index : PageModel
{
public Index(IdentityServerLicense? license = null)
{
License = license;
}

public string Version
{
get => typeof(Duende.IdentityServer.Hosting.IdentityServerMiddleware).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion.Split('+').First()
?? "unavailable";
}
public IdentityServerLicense? License { get; }
}
2 changes: 2 additions & 0 deletions src/Pages/Saml/IdpInitiated.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@page "/saml/idp-initiated"
@model Duende.IdentityServer.Demo.Pages.Saml.IdpInitiatedModel
56 changes: 56 additions & 0 deletions src/Pages/Saml/IdpInitiated.cshtml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Duende.IdentityServer.Saml;
using Duende.IdentityServer.Stores;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace Duende.IdentityServer.Demo.Pages.Saml;

[AllowAnonymous]
public class IdpInitiatedModel : PageModel
{
private readonly IIdpInitiatedSsoService _idpInitiatedSsoService;
private readonly ISamlServiceProviderStore _spStore;

public IdpInitiatedModel(
IIdpInitiatedSsoService idpInitiatedSsoService,
ISamlServiceProviderStore spStore)
{
_idpInitiatedSsoService = idpInitiatedSsoService;
_spStore = spStore;
}

public async Task<IActionResult> OnGetAsync(CancellationToken cancellationToken)
{
var spEntityId = HttpContext.Request.Query["spEntityId"].ToString();
if (string.IsNullOrEmpty(spEntityId))
{
return BadRequest("spEntityId is required");
}

// Validate SP exists and allows IdP-initiated before checking auth
var sp = await _spStore.FindByEntityIdAsync(spEntityId, cancellationToken);
if (sp == null)
{
return BadRequest("Unknown service provider");
}
if (!sp.AllowIdpInitiated)
{
return BadRequest("Service provider does not allow IdP-initiated SSO");
}

// If user is not authenticated, redirect to login
if (User.Identity?.IsAuthenticated != true)
{
return Challenge();
}

var result = await _idpInitiatedSsoService.CreateResponseAsync(HttpContext, spEntityId, cancellationToken);
if (result.IsError)
{
return BadRequest(result.Error);
}
await result.Response.ExecuteAsync(HttpContext);
return new EmptyResult();
}
}
11 changes: 1 addition & 10 deletions src/Pages/Shared/_Nav.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,7 @@
<a class="nav-link text-white" href="/#references">References</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/#demo-clients">OAuth/OIDC Clients</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/#saml-service-providers">SAML SPs</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/#sample-apis">APIs</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/#sample-keys">Keys</a>
<a class="nav-link text-white" href="/#demo-clients">Clients/SPs</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/jwt-decoder">JWT Decoder</a>
Expand Down
8 changes: 2 additions & 6 deletions src/wwwroot/css/site.css
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
:root {
--font-family-sans-serif: "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

.welcome-page .logo {
width: 64px;
--font-family-sans-serif: "Funnel Sans", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

/* List group */
Expand Down Expand Up @@ -74,7 +70,7 @@ a.navbar-brand span {
}
a.navbar-brand .icon-banner {
margin-top: -4px;
height: 2.5rem;
height: 1.5rem;
width: auto;
max-width: 100%;
object-fit: contain;
Expand Down
Loading
Loading