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
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,17 @@ public static TokenValidationParameters GetTokenValidationParameters(TokenValida

var tokenValidator = new CloudFoundryTokenValidator(options ?? new AuthServerOptions());
parameters.IssuerValidator = tokenValidator.ValidateIssuer;
parameters.AudienceValidator = tokenValidator.ValidateAudience;

CloudFoundryTokenKeyResolver tkr;
if (options is null)
{
tkr = new CloudFoundryTokenKeyResolver(keyUrl, handler, validateCertificates);
}
else
if (!string.IsNullOrEmpty(parameters.ValidAudience) || parameters.ValidAudiences != null)
{
tkr = new CloudFoundryTokenKeyResolver(keyUrl, handler, validateCertificates, options.ClientTimeout);
parameters.ValidateAudience = true;
parameters.AudienceValidator = tokenValidator.ValidateAudience;
}

parameters.IssuerSigningKeyResolver = tkr.ResolveSigningKey;
var tokenKeyResolver = options is null
? new CloudFoundryTokenKeyResolver(keyUrl, handler, validateCertificates)
: new CloudFoundryTokenKeyResolver(keyUrl, handler, validateCertificates, options.ClientTimeout);

parameters.IssuerSigningKeyResolver = tokenKeyResolver.ResolveSigningKey;

return parameters;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
Expand All @@ -27,12 +28,7 @@ public CloudFoundryTokenValidator(AuthServerOptions options = null)
/// <returns>The issuer, if valid, else <see langword="null" /></returns>
public virtual string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
{
if (issuer.Contains("uaa"))
{
return issuer;
}

return null;
return issuer.Contains("uaa") ? issuer : null;
}

/// <summary>
Expand All @@ -46,14 +42,19 @@ public virtual bool ValidateAudience(IEnumerable<string> audiences, SecurityToke
{
foreach (var audience in audiences)
{
if (audience.Equals(_options.ClientId))
if (audience.Equals(_options.ClientId, StringComparison.Ordinal))
{
return true;
}

if (validationParameters != null && (audience == validationParameters.ValidAudience || validationParameters.ValidAudiences?.Contains(audience) == true))
{
return true;
}

if (_options.AdditionalAudiences != null)
{
var found = _options.AdditionalAudiences.Any(x => x.Equals(audience));
var found = _options.AdditionalAudiences.Any(x => x == audience);
if (found)
{
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,7 @@ public static AuthenticationBuilder AddCloudFoundryIdentityCertificate(this Auth
/// <returns><see cref="AuthenticationBuilder"/> configured to use application identity certificates</returns>
public static AuthenticationBuilder AddCloudFoundryIdentityCertificate(this AuthenticationBuilder builder, string authenticationScheme, Action<MutualTlsAuthenticationOptions> configurer)
{
builder.AddMutualTls(authenticationScheme, options =>
{
configurer?.Invoke(options);
});
builder.AddMutualTls(authenticationScheme, configurer);
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public CloudFoundryJwtBearerOptions()
public bool Validate_Certificates { get; set; } = true;

/// <summary>
/// Gets or sets a value indicating whether gets a value indicating whether to validate auth server certificate
/// Gets or sets a value indicating whether to validate auth server certificate
/// </summary>
public bool ValidateCertificates
{
Expand All @@ -39,7 +39,11 @@ public bool ValidateCertificates

public void SetEndpoints(string authDomain)
{
JwtKeyUrl = (!string.IsNullOrWhiteSpace(authDomain)) ?
authDomain + CloudFoundryDefaults.JwtTokenUri : JwtKeyUrl;
if (string.IsNullOrEmpty(JwtKeyUrl) || JwtKeyUrl == $"http://{CloudFoundryDefaults.OAuthServiceUrl}{CloudFoundryDefaults.JwtTokenUri}")
Comment thread
bart-vmware marked this conversation as resolved.
{
JwtKeyUrl = !string.IsNullOrWhiteSpace(authDomain)
? authDomain + CloudFoundryDefaults.JwtTokenUri
: JwtKeyUrl;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ internal static void Configure(SsoServiceInfo si, OpenIdConnectOptions oidcOptio
oidcOptions.BackchannelHttpHandler = CloudFoundryHelper.GetBackChannelHandler(cfOptions.ValidateCertificates);
oidcOptions.CallbackPath = cfOptions.CallbackPath;
oidcOptions.ClaimsIssuer = cfOptions.ClaimsIssuer;
oidcOptions.MetadataAddress = cfOptions.MetadataAddress;
oidcOptions.RequireHttpsMetadata = cfOptions.RequireHttpsMetadata;
oidcOptions.ResponseType = cfOptions.ResponseType;
oidcOptions.SaveTokens = cfOptions.SaveTokens;
oidcOptions.SignInScheme = cfOptions.SignInScheme;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using Microsoft.AspNetCore.Authentication.Certificate;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
Expand All @@ -23,13 +24,21 @@ public static class ServiceCollectionExtensions
/// <param name="configuration">Application Configuration</param>
[Obsolete("The IConfiguration parameter is not used")]
public static void AddCloudFoundryContainerIdentity(this IServiceCollection services, IConfiguration configuration)
=> AddCloudFoundryContainerIdentity(services);
=> AddCloudFoundryContainerIdentity(services, configurer: null);

/// <summary>
/// Adds options and services to use Cloud Foundry container identity certificates
/// </summary>
/// <param name="services">Service collection</param>
public static void AddCloudFoundryContainerIdentity(this IServiceCollection services)
=> AddCloudFoundryContainerIdentity(services, configurer: null);

/// <summary>
/// Adds options and services to use Cloud Foundry container identity certificates
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="configurer">Used to configure the <see cref="CertificateForwardingOptions"/></param>
public static void AddCloudFoundryContainerIdentity(this IServiceCollection services, Action<CertificateForwardingOptions> configurer)
{
if (services == null)
{
Expand All @@ -43,7 +52,11 @@ public static void AddCloudFoundryContainerIdentity(this IServiceCollection serv
services.AddSingleton<ICertificateRotationService, CertificateRotationService>();
services.AddHostedService<CertificateRotationHostedService>();
services.AddSingleton<IAuthorizationHandler, CloudFoundryCertificateIdentityAuthorizationHandler>();
services.AddCertificateForwarding(opt => opt.CertificateHeader = "X-Forwarded-Client-Cert");
services.AddCertificateForwarding(opt =>
{
opt.CertificateHeader = "X-Forwarded-Client-Cert";
configurer?.Invoke(opt);
});
}

/// <summary>
Expand All @@ -68,15 +81,23 @@ public static void AddCloudFoundryCertificateAuth(this IServiceCollection servic
/// <param name="services">Service collection</param>
/// <param name="configurer">Used to configure the <see cref="MutualTlsAuthenticationOptions"/></param>
public static void AddCloudFoundryCertificateAuth(this IServiceCollection services, Action<MutualTlsAuthenticationOptions> configurer)
=> AddCloudFoundryCertificateAuth(services, CertificateAuthenticationDefaults.AuthenticationScheme, configurer);
=> AddCloudFoundryCertificateAuth(services, CertificateAuthenticationDefaults.AuthenticationScheme, configurer, null);

/// <summary>
/// Adds options and services for Cloud Foundry container identity certificates along with certificate-based authentication and authorization
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="configurer">Used to configure the <see cref="CertificateForwardingOptions"/></param>
public static void AddCloudFoundryCertificateAuth(this IServiceCollection services, Action<CertificateForwardingOptions> configurer)
=> AddCloudFoundryCertificateAuth(services, CertificateAuthenticationDefaults.AuthenticationScheme, null, configurer);

/// <summary>
/// Adds options and services for Cloud Foundry container identity certificates along with certificate-based authentication and authorization
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="authenticationScheme">An identifier for this authentication mechanism. Default value is <see cref="CertificateAuthenticationDefaults.AuthenticationScheme"/></param>
public static void AddCloudFoundryCertificateAuth(this IServiceCollection services, string authenticationScheme)
=> AddCloudFoundryCertificateAuth(services, authenticationScheme, null);
=> AddCloudFoundryCertificateAuth(services, authenticationScheme, null, null);

/// <summary>
/// Adds options and services for Cloud Foundry container identity certificates along with certificate-based authentication and authorization
Expand All @@ -85,17 +106,27 @@ public static void AddCloudFoundryCertificateAuth(this IServiceCollection servic
/// <param name="authenticationScheme">An identifier for this authentication mechanism. Default value is <see cref="CertificateAuthenticationDefaults.AuthenticationScheme"/></param>
/// <param name="configurer">Used to configure the <see cref="MutualTlsAuthenticationOptions"/></param>
public static void AddCloudFoundryCertificateAuth(this IServiceCollection services, string authenticationScheme, Action<MutualTlsAuthenticationOptions> configurer)
=> AddCloudFoundryCertificateAuth(services, authenticationScheme, configurer, null);

/// <summary>
/// Adds options and services for Cloud Foundry container identity certificates along with certificate-based authentication and authorization
/// </summary>
/// <param name="services">Service collection</param>
/// <param name="authenticationScheme">An identifier for this authentication mechanism. Default value is <see cref="CertificateAuthenticationDefaults.AuthenticationScheme"/></param>
/// <param name="configureMutualTlsAuthentication">Used to configure the <see cref="MutualTlsAuthenticationOptions"/></param>
/// <param name="configureCertificateForwarding">Used to configure the <see cref="CertificateForwardingOptions"/></param>
public static void AddCloudFoundryCertificateAuth(this IServiceCollection services, string authenticationScheme, Action<MutualTlsAuthenticationOptions> configureMutualTlsAuthentication, Action<CertificateForwardingOptions> configureCertificateForwarding)
{
if (services is null)
{
throw new ArgumentNullException(nameof(services));
}

services.AddCloudFoundryContainerIdentity();
services.AddCloudFoundryContainerIdentity(configureCertificateForwarding);

services
.AddAuthentication(authenticationScheme)
.AddCloudFoundryIdentityCertificate(authenticationScheme, configurer);
.AddCloudFoundryIdentityCertificate(authenticationScheme, configureMutualTlsAuthentication);

services.AddAuthorization(cfg =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using Microsoft.IdentityModel.Tokens;
using System;
using System.Text.Json;
using Xunit;
Expand All @@ -25,12 +26,27 @@ public void GetTokenValidationParameters_ReturnsExpected()
{
var parameters = CloudFoundryHelper.GetTokenValidationParameters(null, "https://foo.bar.com/keyurl", null, false);
Assert.False(parameters.ValidateAudience, "Audience validation should not be enabled by default");
Assert.Null(parameters.AudienceValidator);
Assert.True(parameters.ValidateIssuer, "Issuer validation should be enabled by default");
Assert.NotNull(parameters.IssuerValidator);
Assert.True(parameters.ValidateLifetime, "Token lifetime validation should be enabled by default");
Assert.NotNull(parameters.IssuerSigningKeyResolver);
}

[Fact]
public void GetTokenValidationParameters_ValidatesAudienceWhenProvided()
{
var tokenValidationParameters =
new TokenValidationParameters { ValidAudience = "some-api", ValidAudiences = new[] { "another-audience" } };
var parameters = CloudFoundryHelper.GetTokenValidationParameters(tokenValidationParameters, "https://foo.bar.com/keyurl", null, false);

Assert.True(parameters.ValidateAudience, "Audience validation should be enabled when ValidAudience or ValidAudiences are provided");
Assert.NotNull(parameters.AudienceValidator);
Assert.True(parameters.AudienceValidator(new[] { "some-api" }, null, tokenValidationParameters), "Validates single audience");
Assert.True(parameters.AudienceValidator(new[] { "another-audience" }, null, tokenValidationParameters), "Validates from list of audiences");
Assert.False(parameters.AudienceValidator(new[] { "invalid-audience" }, null, tokenValidationParameters), "Unlisted audience is not valid");
}

[Fact]
public void GetExpTime_FindsTime()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using Microsoft.IdentityModel.Tokens;
using Xunit;

namespace Steeltoe.Security.Authentication.CloudFoundry.Test;
Expand All @@ -19,4 +20,44 @@ public void ValidateIssuer_ValidatesCorrectly()
Assert.NotNull(uaaResult);
Assert.Null(foobarResult);
}

[Fact]
public void ValidateAudience_ValidatesFromAuthServerOptionsCorrectly()
{
var cftv = new CloudFoundryTokenValidator(new AuthServerOptions
{
ClientId = "test-client",
AdditionalAudiences = new[] { "additional-audience" }
});
var audiences = new[] { "profile", "some-api", "additional-audience" };
var result = cftv.ValidateAudience(audiences, null, null);
Assert.True(result);

audiences = new[] { "invalid-audience" };
result = cftv.ValidateAudience(audiences, null, null);
Assert.False(result);
}

[Fact]
public void ValidateAudience_ValidatesFromTokenValidationParameters()
{
var cftv = new CloudFoundryTokenValidator();
var audiences = new[] { "profile", "some-api", "additional-audience" };
var validationParametersSingleAudience = new TokenValidationParameters { ValidAudience = "some-api" };
var result = cftv.ValidateAudience(audiences, null, validationParametersSingleAudience);
Assert.True(result, "Valid from single audience in TokenValidationParameters");

var validationParametersListOfAudiences = new TokenValidationParameters
{
ValidAudiences = new[] { "some-api" }
};
result = cftv.ValidateAudience(audiences, null, validationParametersListOfAudiences);
Assert.True(result, "Valid from audience list in TokenValidationParameters");

audiences = new[] { "invalid-audience" };
result = cftv.ValidateAudience(audiences, null, validationParametersSingleAudience);
Assert.False(result, "Invalid from single audience in TokenValidationParameters");
result = cftv.ValidateAudience(audiences, null, validationParametersSingleAudience);
Assert.False(result, "Invalid from audience list in TokenValidationParameters");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ public static TheoryData<string, string> SetEndpointsData()

data.Add(string.Empty, DEFAULT_JWT_TOKEN_URL);
data.Add(" ", DEFAULT_JWT_TOKEN_URL);
data.Add(default, DEFAULT_JWT_TOKEN_URL);
data.Add(null, DEFAULT_JWT_TOKEN_URL);
data.Add(newDomain, newDomain + CloudFoundryDefaults.JwtTokenUri);

return data;
}

[Fact]
public void DefaultConstructor_SetsupDefaultOptions()
public void DefaultConstructor_SetsUpDefaultOptions()
{
var opts = new CloudFoundryJwtBearerOptions();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,18 @@ public class CloudFoundryOpenIdConnectConfigurerTest
public void Configure_NoServiceInfo_ReturnsExpected()
{
var oidcOptions = new OpenIdConnectOptions();
var cloudFoundryOptions = new CloudFoundryOpenIdConnectOptions
{
Authority = "http://localhost:8080/uaa",
MetadataAddress = "http://localhost:8080/.well-known/openid-configuration",
RequireHttpsMetadata = false,
ValidateCertificates = false
};

CloudFoundryOpenIdConnectConfigurer.Configure(null, oidcOptions, new CloudFoundryOpenIdConnectOptions() { ValidateCertificates = false });
CloudFoundryOpenIdConnectConfigurer.Configure(null, oidcOptions, cloudFoundryOptions);

Assert.Equal("http://localhost:8080/uaa", oidcOptions.Authority);
Assert.Equal("http://localhost:8080/.well-known/openid-configuration", oidcOptions.MetadataAddress);
Assert.Equal(CloudFoundryDefaults.AuthenticationScheme, oidcOptions.ClaimsIssuer);
Assert.Equal(CloudFoundryDefaults.ClientId, oidcOptions.ClientId);
Assert.Equal(CloudFoundryDefaults.ClientSecret, oidcOptions.ClientSecret);
Expand All @@ -28,6 +37,7 @@ public void Configure_NoServiceInfo_ReturnsExpected()
Assert.Equal(CookieAuthenticationDefaults.AuthenticationScheme, oidcOptions.SignInScheme);
Assert.False(oidcOptions.SaveTokens);
Assert.NotNull(oidcOptions.BackchannelHttpHandler);
Assert.False(oidcOptions.RequireHttpsMetadata);
}

[Fact]
Expand All @@ -49,5 +59,6 @@ public void Configure_WithServiceInfo_ReturnsExpected()
Assert.Equal(CookieAuthenticationDefaults.AuthenticationScheme, oidcOptions.SignInScheme);
Assert.False(oidcOptions.SaveTokens);
Assert.Null(oidcOptions.BackchannelHttpHandler);
Assert.True(oidcOptions.RequireHttpsMetadata);
}
}
Loading
Loading