@@ -88,7 +88,7 @@
@if (Model.View.ClientUrl != null)
{
-
+
@Model.View.ClientName
}
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Consent.cshtml.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Consent.cshtml.cs
index a059b38360..bf4e77e8ae 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Consent.cshtml.cs
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Consent.cshtml.cs
@@ -10,32 +10,21 @@
namespace IdentityServer.Pages.Ciba;
[Authorize]
-[SecurityHeadersAttribute]
-public class Consent : PageModel
+[SecurityHeaders]
+public class Consent(
+ IBackchannelAuthenticationInteractionService interaction,
+ IEventService events,
+ ILogger
logger)
+ : PageModel
{
- private readonly IBackchannelAuthenticationInteractionService _interaction;
- private readonly IEventService _events;
- private readonly ILogger _logger;
-
- public Consent(
- IBackchannelAuthenticationInteractionService interaction,
- IEventService events,
- ILogger logger)
- {
- _interaction = interaction;
- _events = events;
- _logger = logger;
- }
+ public ViewModel View { get; set; } = default!;
- public ViewModel View { get; set; }
-
[BindProperty]
- public InputModel Input { get; set; }
+ public InputModel Input { get; set; } = default!;
- public async Task OnGet(string id)
+ public async Task OnGetAsync(string? id, CancellationToken ct)
{
- View = await BuildViewModelAsync(id);
- if (View == null)
+ if (!await SetViewModelAsync(id, ct))
{
return RedirectToPage("/Home/Error/Index");
}
@@ -48,31 +37,32 @@ public async Task OnGet(string id)
return Page();
}
- public async Task OnPost()
+ public async Task OnPostAsync(CancellationToken ct)
{
// validate return url is still valid
- var request = await _interaction.GetLoginRequestByInternalIdAsync(Input.Id);
+ var request = await interaction.GetLoginRequestByInternalIdAsync(Input.Id ?? throw new ArgumentNullException(nameof(Input.Id)), ct);
if (request == null || request.Subject.GetSubjectId() != User.GetSubjectId())
{
- _logger.LogError("Invalid id {id}", Input.Id);
+ logger.InvalidId(Input.Id);
return RedirectToPage("/Home/Error/Index");
}
- CompleteBackchannelLoginRequest result = null;
+ CompleteBackchannelLoginRequest? result = null;
// user clicked 'no' - send back the standard 'access_denied' response
- if (Input?.Button == "no")
+ if (Input.Button == "no")
{
result = new CompleteBackchannelLoginRequest(Input.Id);
// emit event
- await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues));
+ await events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues), ct);
+ Telemetry.Metrics.ConsentDenied(request.Client.ClientId, request.ValidatedResources.ParsedScopes.Select(s => s.ParsedName));
}
// user clicked 'yes' - validate the data
- else if (Input?.Button == "yes")
+ else if (Input.Button == "yes")
{
// if the user consented to some scope, build the response model
- if (Input.ScopesConsented != null && Input.ScopesConsented.Any())
+ if (Input.ScopesConsented.Any())
{
var scopes = Input.ScopesConsented;
if (ConsentOptions.EnableOfflineAccess == false)
@@ -87,7 +77,10 @@ public async Task OnPost()
};
// emit event
- await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, result.ScopesValuesConsented, false));
+ await events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, result.ScopesValuesConsented, false), ct);
+ Telemetry.Metrics.ConsentGranted(request.Client.ClientId, result.ScopesValuesConsented, false);
+ var denied = request.ValidatedResources.ParsedScopes.Select(s => s.ParsedName).Except(result.ScopesValuesConsented);
+ Telemetry.Metrics.ConsentDenied(request.Client.ClientId, denied);
}
else
{
@@ -102,33 +95,37 @@ public async Task OnPost()
if (result != null)
{
// communicate outcome of consent back to identityserver
- await _interaction.CompleteLoginRequestAsync(result);
+ await interaction.CompleteLoginRequestAsync(result, ct);
return RedirectToPage("/Ciba/All");
}
// we need to redisplay the consent UI
- View = await BuildViewModelAsync(Input.Id, Input);
+ if (!await SetViewModelAsync(Input.Id, ct))
+ {
+ return RedirectToPage("/Home/Error/Index");
+ }
return Page();
}
- private async Task BuildViewModelAsync(string id, InputModel model = null)
+ private async Task SetViewModelAsync(string? id, CancellationToken ct)
{
- var request = await _interaction.GetLoginRequestByInternalIdAsync(id);
+ ArgumentNullException.ThrowIfNull(id);
+
+ var request = await interaction.GetLoginRequestByInternalIdAsync(id, ct);
if (request != null && request.Subject.GetSubjectId() == User.GetSubjectId())
{
- return CreateConsentViewModel(model, id, request);
+ View = CreateConsentViewModel(request);
+ return true;
}
else
{
- _logger.LogError("No backchannel login request matching id: {id}", id);
+ logger.NoMatchingBackchannelLoginRequest(id);
+ return false;
}
- return null;
}
- private ViewModel CreateConsentViewModel(
- InputModel model, string id,
- BackchannelUserLoginRequest request)
+ private ViewModel CreateConsentViewModel(BackchannelUserLoginRequest request)
{
var vm = new ViewModel
{
@@ -139,7 +136,7 @@ private ViewModel CreateConsentViewModel(
};
vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources
- .Select(x => CreateScopeViewModel(x, model?.ScopesConsented == null || model.ScopesConsented?.Contains(x.Name) == true))
+ .Select(x => CreateScopeViewModel(x, Input == null || Input.ScopesConsented.Contains(x.Name)))
.ToArray();
var resourceIndicators = request.RequestedResourceIndicators ?? Enumerable.Empty();
@@ -151,7 +148,7 @@ private ViewModel CreateConsentViewModel(
var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName);
if (apiScope != null)
{
- var scopeVm = CreateScopeViewModel(parsedScope, apiScope, model == null || model.ScopesConsented?.Contains(parsedScope.RawValue) == true);
+ var scopeVm = CreateScopeViewModel(parsedScope, apiScope, Input == null || Input.ScopesConsented.Contains(parsedScope.RawValue));
scopeVm.Resources = apiResources.Where(x => x.Scopes.Contains(parsedScope.ParsedName))
.Select(x => new ResourceViewModel
{
@@ -163,31 +160,28 @@ private ViewModel CreateConsentViewModel(
}
if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess)
{
- apiScopes.Add(GetOfflineAccessScope(model == null || model.ScopesConsented?.Contains(Duende.IdentityServer.IdentityServerConstants.StandardScopes.OfflineAccess) == true));
+ apiScopes.Add(GetOfflineAccessScope(Input == null || Input.ScopesConsented.Contains(Duende.IdentityServer.IdentityServerConstants.StandardScopes.OfflineAccess)));
}
vm.ApiScopes = apiScopes;
return vm;
}
- private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check)
+ private static ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) => new ScopeViewModel
{
- return new ScopeViewModel
- {
- Name = identity.Name,
- Value = identity.Name,
- DisplayName = identity.DisplayName ?? identity.Name,
- Description = identity.Description,
- Emphasize = identity.Emphasize,
- Required = identity.Required,
- Checked = check || identity.Required
- };
- }
-
- public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
+ Name = identity.Name,
+ Value = identity.Name,
+ DisplayName = identity.DisplayName ?? identity.Name,
+ Description = identity.Description,
+ Emphasize = identity.Emphasize,
+ Required = identity.Required,
+ Checked = check || identity.Required
+ };
+
+ private static ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check)
{
var displayName = apiScope.DisplayName ?? apiScope.Name;
- if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter))
+ if (!string.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter))
{
displayName += ":" + parsedScopeValue.ParsedParameter;
}
@@ -204,15 +198,12 @@ public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, Ap
};
}
- private ScopeViewModel GetOfflineAccessScope(bool check)
+ private static ScopeViewModel GetOfflineAccessScope(bool check) => new ScopeViewModel
{
- return new ScopeViewModel
- {
- Value = Duende.IdentityServer.IdentityServerConstants.StandardScopes.OfflineAccess,
- DisplayName = ConsentOptions.OfflineAccessDisplayName,
- Description = ConsentOptions.OfflineAccessDescription,
- Emphasize = true,
- Checked = check
- };
- }
-}
\ No newline at end of file
+ Value = Duende.IdentityServer.IdentityServerConstants.StandardScopes.OfflineAccess,
+ DisplayName = ConsentOptions.OfflineAccessDisplayName,
+ Description = ConsentOptions.OfflineAccessDescription,
+ Emphasize = true,
+ Checked = check
+ };
+}
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ConsentOptions.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ConsentOptions.cs
index 946a475bbd..4f268bb71b 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ConsentOptions.cs
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ConsentOptions.cs
@@ -1,15 +1,11 @@
-// Copyright (c) Duende Software. All rights reserved.
-// See LICENSE in the project root for license information.
-
-
namespace IdentityServer.Pages.Ciba;
-public class ConsentOptions
+public static 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 bool EnableOfflineAccess = true;
+ public static readonly string OfflineAccessDisplayName = "Offline Access";
+ public static readonly 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";
-}
\ No newline at end of file
+}
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Index.cshtml.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Index.cshtml.cs
index bfe6d00726..e023391e9b 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Index.cshtml.cs
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/Index.cshtml.cs
@@ -1,6 +1,3 @@
-// Copyright (c) Duende Software. All rights reserved.
-// See LICENSE in the project root for license information.
-
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using Microsoft.AspNetCore.Authorization;
@@ -11,28 +8,26 @@ namespace IdentityServer.Pages.Ciba;
[AllowAnonymous]
[SecurityHeaders]
-public class IndexModel : PageModel
+public class IndexModel(
+ IBackchannelAuthenticationInteractionService backchannelAuthenticationInteractionService,
+ ILogger logger)
+ : PageModel
{
- public BackchannelUserLoginRequest LoginRequest { get; set; }
-
- private readonly IBackchannelAuthenticationInteractionService _backchannelAuthenticationInteraction;
- private readonly ILogger _logger;
+ public BackchannelUserLoginRequest LoginRequest { get; set; } = default!;
- public IndexModel(IBackchannelAuthenticationInteractionService backchannelAuthenticationInteractionService, ILogger logger)
+ public async Task OnGetAsync(string id, CancellationToken ct)
{
- _backchannelAuthenticationInteraction = backchannelAuthenticationInteractionService;
- _logger = logger;
- }
-
- public async Task OnGet(string id)
- {
- LoginRequest = await _backchannelAuthenticationInteraction.GetLoginRequestByInternalIdAsync(id);
- if (LoginRequest == null)
+ var result = await backchannelAuthenticationInteractionService.GetLoginRequestByInternalIdAsync(id, ct);
+ if (result == null)
{
- _logger.LogWarning("Invalid backchannel login id {id}", id);
+ logger.InvalidBackchannelLoginId(id);
return RedirectToPage("/Home/Error/Index");
}
+ else
+ {
+ LoginRequest = result;
+ }
return Page();
}
-}
\ No newline at end of file
+}
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/InputModel.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/InputModel.cs
index c7f414d047..b90309b402 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/InputModel.cs
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/InputModel.cs
@@ -1,12 +1,9 @@
-// Copyright (c) Duende Software. All rights reserved.
-// See LICENSE in the project root for license information.
-
namespace IdentityServer.Pages.Ciba;
public class InputModel
{
- public string Button { get; set; }
- public IEnumerable ScopesConsented { get; set; }
- public string Id { get; set; }
- public string Description { get; set; }
-}
\ No newline at end of file
+ public string? Button { get; set; }
+ public IEnumerable ScopesConsented { get; set; } = new List();
+ public string? Id { get; set; }
+ public string? Description { get; set; }
+}
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ViewModel.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ViewModel.cs
index 4c2436d263..3f5b361c9c 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ViewModel.cs
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/ViewModel.cs
@@ -1,34 +1,31 @@
-// Copyright (c) Duende Software. All rights reserved.
-// See LICENSE in the project root for license information.
-
namespace IdentityServer.Pages.Ciba;
public class ViewModel
{
- public string ClientName { get; set; }
- public string ClientUrl { get; set; }
- public string ClientLogoUrl { get; set; }
-
- public string BindingMessage { get; set; }
+ public string? ClientName { get; set; }
+ public string? ClientUrl { get; set; }
+ public string? ClientLogoUrl { get; set; }
+
+ public string? BindingMessage { get; set; }
- public IEnumerable IdentityScopes { get; set; }
- public IEnumerable ApiScopes { get; set; }
+ public IEnumerable IdentityScopes { get; set; } = Enumerable.Empty();
+ public IEnumerable ApiScopes { get; set; } = Enumerable.Empty();
}
public class ScopeViewModel
{
- public string Name { get; set; }
- public string Value { get; set; }
- public string DisplayName { get; set; }
- public string Description { get; set; }
+ public string? Name { get; set; }
+ public string? Value { 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; }
- public IEnumerable Resources { get; set; }
+ public IEnumerable Resources { get; set; } = Enumerable.Empty();
}
public class ResourceViewModel
{
- public string Name { get; set; }
- public string DisplayName { get; set; }
-}
\ No newline at end of file
+ public string? Name { get; set; }
+ public string? DisplayName { get; set; }
+}
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/_ScopeListItem.cshtml b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/_ScopeListItem.cshtml
index 2576300f89..8149ec5dcc 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/_ScopeListItem.cshtml
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Ciba/_ScopeListItem.cshtml
@@ -19,7 +19,7 @@
@Model.DisplayName
@if (Model.Emphasize)
{
-
+
}
@if (Model.Required)
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/ConsentOptions.cs b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/ConsentOptions.cs
index 9a0e23c88f..ad11b00a36 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/ConsentOptions.cs
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/ConsentOptions.cs
@@ -1,15 +1,11 @@
-// Copyright (c) Duende Software. All rights reserved.
-// See LICENSE in the project root for license information.
-
-
namespace IdentityServer.Pages.Consent;
-public class ConsentOptions
+public static 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 bool EnableOfflineAccess = true;
+ public static readonly string OfflineAccessDisplayName = "Offline Access";
+ public static readonly 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";
-}
\ No newline at end of file
+}
diff --git a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/Index.cshtml b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/Index.cshtml
index 6616bdc398..500d4dfc1d 100644
--- a/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/Index.cshtml
+++ b/authorization-dotnet/AddingCustomClaimsToAccessTokenInDuende/IdentityServer/Pages/Consent/Index.cshtml
@@ -28,10 +28,10 @@
@if (Model.View.IdentityScopes.Any())
{
-