Skip to content

Commit

Permalink
Represent OIDC authorities as System.Uri instances to perform automat…
Browse files Browse the repository at this point in the history
…ic normalization (#3269)
  • Loading branch information
kevinchalet authored Mar 3, 2019
1 parent 676f80d commit b555810
Show file tree
Hide file tree
Showing 17 changed files with 49 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ public void Configure(string name, OpenIdConnectOptions options)
return;
}

options.Authority = settings.Authority;
options.Authority = settings.Authority.AbsoluteUri;
options.ClientId = settings.ClientId;
options.SignedOutRedirectUri = settings.SignedOutRedirectUri ?? options.SignedOutRedirectUri;
options.SignedOutCallbackPath = settings.SignedOutCallbackPath ?? options.SignedOutCallbackPath;
options.RequireHttpsMetadata = settings.Authority.StartsWith(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
options.RequireHttpsMetadata = string.Equals(settings.Authority.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
options.GetClaimsFromUserInfoEndpoint = true;
options.ResponseMode = settings.ResponseMode;
options.ResponseType = settings.ResponseType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,14 @@ public void Configure(string name, OpenIddictServerOptions options)
options.ApplicationCanDisplayErrors = true;
options.EnableRequestCaching = true;
options.IgnoreScopePermissions = true;
options.Issuer = settings.Authority;
options.UseRollingTokens = settings.UseRollingTokens;

foreach (var key in _serverService.GetSigningKeysAsync().GetAwaiter().GetResult())
{
options.SigningCredentials.AddKey(key);
}

if (!string.IsNullOrEmpty(settings.Authority))
{
options.Issuer = new Uri(settings.Authority, UriKind.Absolute);
}

if (settings.AccessTokenFormat == OpenIdServerSettings.TokenFormat.JWT)
{
options.AccessTokenHandler = new JwtSecurityTokenHandler();
Expand Down Expand Up @@ -158,9 +154,9 @@ public void Configure(string name, JwtBearerOptions options)

// If an authority was explicitly set in the OpenID server options,
// prefer it to the dynamic tenant comparison as it's more efficient.
if (!string.IsNullOrEmpty(settings.Authority))
if (settings.Authority != null)
{
options.TokenValidationParameters.ValidIssuer = settings.Authority;
options.TokenValidationParameters.ValidIssuer = settings.Authority.AbsoluteUri;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public void Configure(AuthenticationOptions options)
return;
}

if (!string.IsNullOrEmpty(settings.Authority))
if (settings.Authority != null)
{
options.AddScheme<JwtBearerHandler>(JwtBearerDefaults.AuthenticationScheme, displayName: null);

Expand Down Expand Up @@ -115,11 +115,11 @@ public void Configure(string name, JwtBearerOptions options)
// Otherwise, set the authority to allow the JWT handler to retrieve the endpoint URLs/signing keys
// from the remote server's metadata by sending an OpenID Connect/OAuth2 discovery request.

if (!string.IsNullOrEmpty(settings.Authority))
if (settings.Authority != null)
{
options.RequireHttpsMetadata = settings.Authority.StartsWith(Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
options.RequireHttpsMetadata = string.Equals(settings.Authority.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
options.Audience = settings.Audience;
options.Authority = settings.Authority;
options.Authority = settings.Authority.AbsoluteUri;

return;
}
Expand Down Expand Up @@ -148,9 +148,9 @@ public void Configure(string name, JwtBearerOptions options)

// If an authority was explicitly set in the OpenID server options,
// prefer it to the dynamic tenant comparison as it's more efficient.
if (!string.IsNullOrEmpty(configuration.Authority))
if (configuration.Authority != null)
{
options.TokenValidationParameters.ValidIssuer = configuration.Authority;
options.TokenValidationParameters.ValidIssuer = configuration.Authority.AbsoluteUri;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Localization;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using OrchardCore.DisplayManagement.Entities;
using OrchardCore.DisplayManagement.Handlers;
using OrchardCore.DisplayManagement.Notify;
using OrchardCore.DisplayManagement.Views;
using OrchardCore.Environment.Shell;
using OrchardCore.OpenId.Configuration;
Expand All @@ -27,8 +25,6 @@ public class OpenIdClientSettingsDisplayDriver : SectionDisplayDriver<ISite, Ope
private readonly IAuthorizationService _authorizationService;
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly INotifier _notifier;
private readonly IHtmlLocalizer<OpenIdClientSettingsDisplayDriver> T;
private readonly IOpenIdClientService _clientService;
private readonly IShellHost _shellHost;
private readonly ShellSettings _shellSettings;
Expand All @@ -38,19 +34,15 @@ public OpenIdClientSettingsDisplayDriver(
IDataProtectionProvider dataProtectionProvider,
IOpenIdClientService clientService,
IHttpContextAccessor httpContextAccessor,
INotifier notifier,
IHtmlLocalizer<OpenIdClientSettingsDisplayDriver> stringLocalizer,
IShellHost shellHost,
ShellSettings shellSettings)
{
_authorizationService = authorizationService;
_dataProtectionProvider = dataProtectionProvider;
_clientService = clientService;
_httpContextAccessor = httpContextAccessor;
_notifier = notifier;
_shellHost = shellHost;
_shellSettings = shellSettings;
T = stringLocalizer;
}

public override async Task<IDisplayResult> EditAsync(OpenIdClientSettings settings, BuildEditorContext context)
Expand All @@ -65,7 +57,7 @@ public override async Task<IDisplayResult> EditAsync(OpenIdClientSettings settin
{
model.DisplayName = settings.DisplayName;
model.Scopes = settings.Scopes != null ? string.Join(" ", settings.Scopes) : null;
model.Authority = settings.Authority;
model.Authority = settings.Authority?.AbsoluteUri;
model.CallbackPath = settings.CallbackPath;
model.ClientId = settings.ClientId;
model.ClientSecret = settings.ClientSecret;
Expand Down Expand Up @@ -120,7 +112,7 @@ public override async Task<IDisplayResult> UpdateAsync(OpenIdClientSettings sett

settings.DisplayName = model.DisplayName;
settings.Scopes = model.Scopes.Split(new char[] { ' ', ',' }, StringSplitOptions.RemoveEmptyEntries);
settings.Authority = model.Authority;
settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null;
settings.CallbackPath = model.CallbackPath;
settings.ClientId = model.ClientId;
settings.SignedOutCallbackPath = model.SignedOutCallbackPath;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using OrchardCore.DisplayManagement.Handlers;
Expand All @@ -21,7 +22,7 @@ public override Task<IDisplayResult> EditAsync(OpenIdServerSettings settings, Bu
=> Task.FromResult<IDisplayResult>(Initialize<OpenIdServerSettingsViewModel>("OpenIdServerSettings_Edit", async model =>
{
model.AccessTokenFormat = settings.AccessTokenFormat;
model.Authority = settings.Authority;
model.Authority = settings.Authority?.AbsoluteUri;

model.CertificateStoreLocation = settings.CertificateStoreLocation;
model.CertificateStoreName = settings.CertificateStoreName;
Expand Down Expand Up @@ -64,7 +65,7 @@ public override async Task<IDisplayResult> UpdateAsync(OpenIdServerSettings sett
await context.Updater.TryUpdateModelAsync(model, Prefix);

settings.AccessTokenFormat = model.AccessTokenFormat;
settings.Authority = model.Authority;
settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null;

settings.CertificateStoreLocation = model.CertificateStoreLocation;
settings.CertificateStoreName = model.CertificateStoreName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -22,7 +23,7 @@ public OpenIdValidationSettingsDisplayDriver(IShellHost shellHost)
public override Task<IDisplayResult> EditAsync(OpenIdValidationSettings settings, BuildEditorContext context)
=> Task.FromResult<IDisplayResult>(Initialize<OpenIdValidationSettingsViewModel>("OpenIdValidationSettings_Edit", async model =>
{
model.Authority = settings.Authority;
model.Authority = settings.Authority?.AbsoluteUri;
model.Audience = settings.Audience;
model.Tenant = settings.Tenant;

Expand Down Expand Up @@ -51,7 +52,7 @@ public override async Task<IDisplayResult> UpdateAsync(OpenIdValidationSettings

await context.Updater.TryUpdateModelAsync(model, Prefix);

settings.Authority = model.Authority?.Trim();
settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null;
settings.Audience = model.Audience?.Trim();
settings.Tenant = model.Tenant;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
using System;
using System.Security.Cryptography.X509Certificates;
using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using OrchardCore.OpenId.Services;
using OrchardCore.Recipes.Models;
using OrchardCore.Recipes.Services;
using static OrchardCore.OpenId.Settings.OpenIdServerSettings;

namespace OrchardCore.OpenId.Recipes
{
Expand All @@ -31,7 +30,7 @@ public async Task ExecuteAsync(RecipeExecutionContext context)

var settings = await _clientService.GetSettingsAsync();
settings.Scopes = model.Scopes.Split(' ', ',');
settings.Authority = model.Authority;
settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null;
settings.CallbackPath = model.CallbackPath;
settings.ClientId = model.ClientId;
settings.ClientSecret = model.ClientSecret;
Expand All @@ -48,6 +47,7 @@ public async Task ExecuteAsync(RecipeExecutionContext context)
public class OpenIdClientSettingsStepModel
{
public string DisplayName { get; set; }
[Url]
public string Authority { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -32,7 +33,7 @@ public async Task ExecuteAsync(RecipeExecutionContext context)

var settings = await _serverService.GetSettingsAsync();
settings.AccessTokenFormat = model.AccessTokenFormat;
settings.Authority = model.Authority;
settings.Authority = !string.IsNullOrEmpty(model.Authority) ? new Uri(model.Authority, UriKind.Absolute) : null;

settings.CertificateStoreLocation = model.CertificateStoreLocation;
settings.CertificateStoreName = model.CertificateStoreName;
Expand Down Expand Up @@ -92,6 +93,7 @@ public async Task ExecuteAsync(RecipeExecutionContext context)
public class OpenIdServerSettingsStepModel
{
public TokenFormat AccessTokenFormat { get; set; } = TokenFormat.Encrypted;
[Url]
public string Authority { get; set; }
public StoreLocation CertificateStoreLocation { get; set; } = StoreLocation.LocalMachine;
public StoreName CertificateStoreName { get; set; } = StoreName.My;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,21 +55,21 @@ public Task<ImmutableArray<ValidationResult>> ValidateSettingsAsync(OpenIdClient

var results = ImmutableArray.CreateBuilder<ValidationResult>();

if (string.IsNullOrEmpty(settings.Authority))
if (settings.Authority == null)
{
results.Add(new ValidationResult(T["The authority cannot be null or empty."], new[]
{
nameof(settings.Authority)
}));
}
else if (!Uri.TryCreate(settings.Authority, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
else if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString())
{
results.Add(new ValidationResult(T["The authority must be a valid absolute URL."], new[]
{
nameof(settings.Authority)
}));
}
else if (!string.IsNullOrEmpty(uri.Query) || !string.IsNullOrEmpty(uri.Fragment))
else if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment))
{
results.Add(new ValidationResult(T["The authority cannot contain a query string or a fragment."], new[]
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,17 @@ public Task<ImmutableArray<ValidationResult>> ValidateSettingsAsync(OpenIdServer
results.Add(new ValidationResult(T["At least one OpenID Connect flow must be enabled."]));
}

if (!string.IsNullOrEmpty(settings.Authority))
if (settings.Authority != null)
{
if (!Uri.TryCreate(settings.Authority, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString())
{
results.Add(new ValidationResult(T["The authority must be a valid absolute URL."], new[]
{
nameof(settings.Authority)
}));
}

if (!string.IsNullOrEmpty(uri.Query) || !string.IsNullOrEmpty(uri.Fragment))
if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment))
{
results.Add(new ValidationResult(T["The authority cannot contain a query string or a fragment."], new[]
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public async Task<ImmutableArray<ValidationResult>> ValidateSettingsAsync(OpenId

var results = ImmutableArray.CreateBuilder<ValidationResult>();

if (!(string.IsNullOrEmpty(settings.Authority) ^ string.IsNullOrEmpty(settings.Tenant)))
if (!(settings.Authority == null ^ string.IsNullOrEmpty(settings.Tenant)))
{
results.Add(new ValidationResult(T["Either a tenant or an authority must be registered."], new[]
{
Expand All @@ -87,17 +87,17 @@ public async Task<ImmutableArray<ValidationResult>> ValidateSettingsAsync(OpenId
}));
}

if (!string.IsNullOrEmpty(settings.Authority))
if (settings.Authority != null)
{
if (!Uri.TryCreate(settings.Authority, UriKind.Absolute, out Uri uri) || !uri.IsWellFormedOriginalString())
if (!settings.Authority.IsAbsoluteUri || !settings.Authority.IsWellFormedOriginalString())
{
results.Add(new ValidationResult(T["The specified authority is not valid."], new[]
{
nameof(settings.Authority)
}));
}

if (!string.IsNullOrEmpty(uri.Query) || !string.IsNullOrEmpty(uri.Fragment))
if (!string.IsNullOrEmpty(settings.Authority.Query) || !string.IsNullOrEmpty(settings.Authority.Fragment))
{
results.Add(new ValidationResult(T["The authority cannot contain a query string or a fragment."], new[]
{
Expand All @@ -114,7 +114,7 @@ public async Task<ImmutableArray<ValidationResult>> ValidateSettingsAsync(OpenId
}));
}

if (!string.IsNullOrEmpty(settings.Authority) && string.IsNullOrEmpty(settings.Audience))
if (settings.Authority != null && string.IsNullOrEmpty(settings.Audience))
{
results.Add(new ValidationResult(T["An audience must be set when configuring the authority."], new[]
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;

namespace OrchardCore.OpenId.Settings
{
public class OpenIdClientSettings
{
public string DisplayName { get; set; }
public string Authority { get; set; }
public Uri Authority { get; set; }
public string ClientId { get; set; }
public string ClientSecret { get; set; }
public string CallbackPath { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace OrchardCore.OpenId.Settings
public class OpenIdServerSettings
{
public TokenFormat AccessTokenFormat { get; set; }
public string Authority { get; set; }
public Uri Authority { get; set; }
public StoreLocation? CertificateStoreLocation { get; set; }
public StoreName? CertificateStoreName { get; set; }
public string CertificateThumbprint { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;

namespace OrchardCore.OpenId.Settings
{
public class OpenIdValidationSettings
{
public string Audience { get; set; }
public string Authority { get; set; }
public Uri Authority { get; set; }
public string Tenant { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Security.Cryptography.X509Certificates;
using static OrchardCore.OpenId.Settings.OpenIdServerSettings;

Expand All @@ -8,6 +9,7 @@ namespace OrchardCore.OpenId.ViewModels
public class OpenIdServerSettingsViewModel
{
public TokenFormat AccessTokenFormat { get; set; }
[Url]
public string Authority { get; set; }
public StoreLocation? CertificateStoreLocation { get; set; }
public StoreName? CertificateStoreName { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace OrchardCore.OpenId.ViewModels
{
public class OpenIdValidationSettingsViewModel
{
[Url]
public string Authority { get; set; }
public string Audience { get; set; }
public string Tenant { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
</fieldset>

<h3>Advanced options</h3>

<p class="alert alert-info">@T["These options are all optional and are for advanced users only."]</p>

<fieldset class="form-group" asp-validation-class-for="Authority">
<label asp-for="Authority">@T["Authority"]</label>
<input asp-for="Authority" class="form-control" />
Expand Down

0 comments on commit b555810

Please sign in to comment.