Skip to content

Instantly share code, notes, and snippets.

@clairernovotny
Last active October 16, 2017 11:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save clairernovotny/46b827d72b165e937e44b14b28231a49 to your computer and use it in GitHub Desktop.
Save clairernovotny/46b827d72b165e937e44b14b28231a49 to your computer and use it in GitHub Desktop.
B2C to SharePoint configuration for IdentityServer3: https://oren.codes/2016/09/08/connecting-sharepoint-to-azure-ad-b2c/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using IdentityServer3.Core.Models;
using IdentityServer3.Core.Services;
using IdentityServer3.Core.Services.Default;
using Newtonsoft.Json.Linq;
namespace ClaimsProxy.Services
{
public class AadUserService : UserServiceBase
{
public override Task AuthenticateExternalAsync(ExternalAuthenticationContext context)
{
var current = context.ExternalIdentity.Claims.ToList();
var oid = current.First(c => c.Type == "oid").Value;
var name = current.First(c => c.Type == "name").Value;
var iss = current.First(c => c.Type == "iss").Value;
var acr = current.First(c => c.Type == "acr").Value;
var ar = new AuthenticateResult(oid, name, current, "B2C", acr);//, iss, acr);
context.AuthenticateResult = ar;
return base.AuthenticateExternalAsync(context);
}
public override Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// pass through claims from incoming context
context.IssuedClaims = context.Subject.Claims;
return base.GetProfileDataAsync(context);
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Protocols;
namespace ClaimsProxy.PolicyAuthHelpers
{
class HttpDocumentRetriever : IDocumentRetriever
{
readonly HttpClient httpClient;
public HttpDocumentRetriever()
: this(new HttpClient())
{
}
public HttpDocumentRetriever(HttpClient httpClient)
{
if (httpClient == null)
{
throw new ArgumentNullException(nameof(httpClient));
}
this.httpClient = httpClient;
}
public async Task<string> GetDocumentAsync(string address, CancellationToken cancel)
{
if (string.IsNullOrWhiteSpace(address))
{
throw new ArgumentNullException(nameof(address));
}
try
{
var response = await httpClient.GetAsync(address, cancel)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync()
.ConfigureAwait(false);
}
catch (Exception ex)
{
throw new IOException("Unable to get document from: " + address, ex);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.IdentityModel;
using Microsoft.IdentityModel.Protocols;
namespace ClaimsProxy.PolicyAuthHelpers
{
// This class is a temporary workaround for AAD B2C,
// while our current libraries are unable to support B2C
// out of the box. For the original source code (with comments)
// visit https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/master/src/Microsoft.IdentityModel.Protocol.Extensions/Configuration/ConfigurationManager.cs
class PolicyConfigurationManager : IConfigurationManager<OpenIdConnectConfiguration>
{
private const string policyParameter = "p";
public static readonly TimeSpan DefaultAutomaticRefreshInterval = new TimeSpan(5, 0, 0, 0);
public static readonly TimeSpan DefaultRefreshInterval = new TimeSpan(0, 0, 0, 30);
public static readonly TimeSpan MinimumAutomaticRefreshInterval = new TimeSpan(0, 0, 5, 0);
public static readonly TimeSpan MinimumRefreshInterval = new TimeSpan(0, 0, 0, 1);
private readonly OpenIdConnectConfigurationRetriever _configRetriever;
private readonly IDocumentRetriever _docRetriever;
private readonly string _metadataAddress;
private readonly SemaphoreSlim _refreshLock;
private TimeSpan _automaticRefreshInterval = DefaultAutomaticRefreshInterval;
private readonly Dictionary<string, OpenIdConnectConfiguration> _currentConfiguration;
private readonly Dictionary<string, DateTimeOffset> _lastRefresh;
private TimeSpan _refreshInterval = DefaultRefreshInterval;
private readonly Dictionary<string, DateTimeOffset> _syncAfter;
public PolicyConfigurationManager(string metadataAddress, string[] policies)
: this(metadataAddress, policies, new HttpDocumentRetriever())
{
}
public PolicyConfigurationManager(string metadataAddress, string[] policies, IDocumentRetriever docRetriever)
{
if (string.IsNullOrWhiteSpace(metadataAddress))
{
throw new ArgumentNullException(nameof(metadataAddress));
}
if (docRetriever == null)
{
throw new ArgumentNullException(nameof(docRetriever));
}
_metadataAddress = metadataAddress;
_docRetriever = docRetriever;
_configRetriever = new OpenIdConnectConfigurationRetriever();
_refreshLock = new SemaphoreSlim(1);
_syncAfter = new Dictionary<string, DateTimeOffset>();
_lastRefresh = new Dictionary<string, DateTimeOffset>();
_currentConfiguration = new Dictionary<string, OpenIdConnectConfiguration>();
foreach (var policy in policies)
{
_currentConfiguration.Add(policy, null);
}
}
public TimeSpan AutomaticRefreshInterval
{
get { return _automaticRefreshInterval; }
set
{
if (value < MinimumAutomaticRefreshInterval)
{
throw new ArgumentOutOfRangeException(nameof(value), value, string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10107, MinimumAutomaticRefreshInterval, value));
}
_automaticRefreshInterval = value;
}
}
public TimeSpan RefreshInterval
{
get { return _refreshInterval; }
set
{
if (value < MinimumRefreshInterval)
{
throw new ArgumentOutOfRangeException(nameof(value), value, string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10106, MinimumRefreshInterval, value));
}
_refreshInterval = value;
}
}
// This non-policy specific method effectively gets the metadata for all policies specified in the constructor,
// and merges their signing key metadata. It selects the other metadata from one of the policies at random.
// This is done so that the middleware can take an incoming id_token and validate it against all signing keys
// for the app, selecting the appropriate signing key based on the key identifiers.
public async Task<OpenIdConnectConfiguration> GetConfigurationAsync(CancellationToken cancel)
{
var configUnion = new OpenIdConnectConfiguration();
var clone = new Dictionary<string, OpenIdConnectConfiguration>(_currentConfiguration);
foreach (var entry in clone)
{
var config = await GetConfigurationByPolicyAsync(cancel, entry.Key)
.ConfigureAwait(false);
configUnion = MergeConfig(configUnion, config);
}
return configUnion;
}
public void RequestRefresh()
{
foreach (var entry in _currentConfiguration)
{
RequestRefresh(entry.Key);
}
}
public async Task<OpenIdConnectConfiguration> GetConfigurationByPolicyAsync(CancellationToken cancel, string policyId)
{
var now = DateTimeOffset.UtcNow;
DateTimeOffset sync;
if (!_syncAfter.TryGetValue(policyId, out sync))
{
sync = DateTimeOffset.MinValue;
}
OpenIdConnectConfiguration config;
if (!_currentConfiguration.TryGetValue(policyId, out config))
{
config = null;
}
if (config != null && sync > now)
{
return config;
}
await _refreshLock.WaitAsync(cancel)
.ConfigureAwait(false);
try
{
Exception retrieveEx = null;
if (sync <= now)
{
try
{
// We're assuming the metadata address provided in the constructor does not contain qp's
config = await OpenIdConnectConfigurationRetriever.GetAsync(string.Format(_metadataAddress + "?{0}={1}", policyParameter, policyId), _docRetriever, cancel)
.ConfigureAwait(false);
_currentConfiguration[policyId] = config;
Contract.Assert(_currentConfiguration[policyId] != null);
_lastRefresh[policyId] = now;
_syncAfter[policyId] = now.UtcDateTime.Add(_automaticRefreshInterval);
}
catch (Exception ex)
{
retrieveEx = ex;
_syncAfter[policyId] = now.UtcDateTime.Add(_automaticRefreshInterval < _refreshInterval ? _automaticRefreshInterval : _refreshInterval);
}
}
if (config == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10803, _metadataAddress ?? "null"), retrieveEx);
}
return config;
}
finally
{
_refreshLock.Release();
}
}
public void RequestRefresh(string policyId)
{
var now = DateTimeOffset.UtcNow;
DateTimeOffset refresh;
if (!_lastRefresh.TryGetValue(policyId, out refresh) || now >= _lastRefresh[policyId].UtcDateTime.Add(RefreshInterval))
{
_syncAfter[policyId] = now;
}
}
// Takes the ohter and copies it to source, preserving the source's multi-valued attributes as a running sum.
private OpenIdConnectConfiguration MergeConfig(OpenIdConnectConfiguration source, OpenIdConnectConfiguration other)
{
var existingSigningTokens = source.SigningTokens;
var existingAlgs = source.IdTokenSigningAlgValuesSupported;
var existingSigningKeys = source.SigningKeys;
foreach (var token in existingSigningTokens)
{
other.SigningTokens.Add(token);
}
foreach (var alg in existingAlgs)
{
other.IdTokenSigningAlgValuesSupported.Add(alg);
}
foreach (var key in existingSigningKeys)
{
other.SigningKeys.Add(key);
}
return other;
}
}
}
using System.Collections.Generic;
using System.Configuration;
using System.Security.Claims;
using IdentityServer3.WsFederation.Models;
using IdentityModel.Constants;
using System;
namespace ClaimsProxy.Models
{
public class RelyingParties
{
public static IEnumerable<RelyingParty> Get()
{
var relyingPartyList = new List<RelyingParty>();
for (int i = 1; i < 200; i++) {
if (String.IsNullOrEmpty(ConfigurationManager.AppSettings.Get($"rp:{i}:ReplyUrl"))) {
break;
}
var newRP = new RelyingParty() {
Realm = ConfigurationManager.AppSettings[$"rp:{i}:Realm"],
Name = ConfigurationManager.AppSettings[$"rp:{i}:Name"],
Enabled = true,
ReplyUrl = ConfigurationManager.AppSettings[$"rp:{i}:ReplyUrl"],
PostLogoutRedirectUris =
{
ConfigurationManager.AppSettings[$"rp:{i}:PostLogoutRedirectUri"]
},
TokenType = TokenTypes.Saml11TokenProfile11,
TokenLifeTime = 60,
ClaimMappings = GetClaimMappings()
};
relyingPartyList.Add(newRP);
}
return relyingPartyList;
}
static Dictionary<string, string> GetClaimMappings() => new Dictionary<string, string>
{
{"name", ClaimTypes.Name},
{"given_name", ClaimTypes.GivenName},
{"family_name", ClaimTypes.Surname },
{"oid", ClaimTypes.NameIdentifier },
{"acr", "http://schemas.microsoft.com/claims/authnclassreference" },
{"email", ClaimTypes.Email},
{"role", ClaimTypes.Role},
{"amr", ClaimTypes.AuthenticationMethod},
{"idp", "http://schemas.microsoft.com/identity/claims/identityprovider"},
{"extension_ValidationLevel", "http://schemas.moog.com/identity/claims/validationLevel"},
};
}
internal static class TokenTypes
{
public const string Kerberos = "http://schemas.microsoft.com/ws/2006/05/identitymodel/tokens/Kerberos";
public const string OasisWssSaml11TokenProfile11 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1";
public const string OasisWssSaml2TokenProfile11 = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
public const string Rsa = "http://schemas.microsoft.com/ws/2006/05/identitymodel/tokens/Rsa";
public const string Saml11TokenProfile11 = "urn:oasis:names:tc:SAML:1.0:assertion";
public const string Saml2TokenProfile11 = "urn:oasis:names:tc:SAML:2.0:assertion";
public const string UserName = "http://schemas.microsoft.com/ws/2006/05/identitymodel/tokens/UserName";
public const string X509Certificate = "http://schemas.microsoft.com/ws/2006/05/identitymodel/tokens/X509Certificate";
public const string SimpleWebToken = "http://schemas.xmlsoap.org/ws/2009/11/swt-token-profile-1.0";
public const string JsonWebToken = "urn:ietf:params:oauth:token-type:jwt";
}
}
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.IdentityModel.Tokens;
using System.Linq;
using Microsoft.Owin.Security;
using Owin;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ClaimsProxy.PolicyAuthHelpers;
using Microsoft.IdentityModel.Protocols;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Notifications;
using Microsoft.Owin.Security.OpenIdConnect;
using IdentityServer3.Core;
namespace ClaimsProxy
{
partial class Startup
{
public const string AcrClaimType = "http://schemas.microsoft.com/claims/authnclassreference";
public const string PolicyKey = "b2cpolicy";
public const string OIDCMetadataSuffix = "/.well-known/openid-configuration";
// App config settings
public static readonly string B2CClientId = ConfigurationManager.AppSettings["b2c:ClientId"];
public static readonly string B2CAadInstance = ConfigurationManager.AppSettings["b2c:AadInstance"];
public static readonly string B2CTenant = ConfigurationManager.AppSettings["b2c:Tenant"];
public static readonly string redirectUri = ConfigurationManager.AppSettings["b2c:RedirectUri"];
// B2C policy identifiers
public static readonly string SignUpPolicyId = ConfigurationManager.AppSettings["b2c:SignUpPolicyId"];
public static readonly string ResetPasswordPolicyId = ConfigurationManager.AppSettings["b2c:ResetPasswordPolicyId"];
public const string B2CProvider = "B2C";
static void ConfigureB2CAuth(IAppBuilder app, string signInAsType)
{
var options = new OpenIdConnectAuthenticationOptions
{
// These are standard OpenID Connect parameters, with values pulled from web.config
Caption = "Partners",
SignInAsAuthenticationType = signInAsType,
AuthenticationType = B2CProvider,
ClientId = B2CClientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = OnB2CAuthenticationFailed,
RedirectToIdentityProvider = OnB2CRedirectToIdentityProvider,
SecurityTokenValidated = OnB2CTokenValidated
},
Scope = "openid",
ResponseType = OpenIdConnectResponseTypes.IdToken,
// The PolicyConfigurationManager takes care of getting the correct Azure AD authentication
// endpoints from the OpenID Connect metadata endpoint. It is included in the PolicyAuthHelpers folder.
ConfigurationManager = new PolicyConfigurationManager(
string.Format(CultureInfo.InvariantCulture, B2CAadInstance, B2CTenant, "/v2.0", OIDCMetadataSuffix),
new[] {SignUpPolicyId, ResetPasswordPolicyId}),
// This piece is optional - it is used for displaying the user's name in the navigation bar.
TokenValidationParameters = new TokenValidationParameters
{
AuthenticationType = signInAsType,
NameClaimType = "name"
}
};
app.UseOpenIdConnectAuthentication(options);
}
static Task OnB2CTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
// For password reset, we need to redirect back to sign in again
var context = notification.OwinContext;
// see if it's a password reset
var acr = notification.AuthenticationTicket.Identity.FindFirst(AcrClaimType).Value;
if (ResetPasswordPolicyId.EndsWith(acr, StringComparison.OrdinalIgnoreCase))
{
var signIn = context.Request.Cookies["b2cSignIn"];
var redir = context.Request.Cookies["b2cRedirUri"];
notification.HandleResponse();
var authProps = new AuthenticationProperties(
new Dictionary<string, string>
{
{PolicyKey, SignUpPolicyId},
{Constants.Authentication.SigninId, signIn },
{Constants.Authentication.KatanaAuthenticationType, B2CProvider}
})
{
RedirectUri = redir
};
notification.OwinContext.Authentication.Challenge(authProps, B2CProvider);
}
return Task.CompletedTask;
}
// Used for avoiding yellow-screen-of-death
static Task OnB2CAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
// Check for password reset message
if (notification.ProtocolMessage.ErrorDescription?.Contains("AADB2C90118") ?? false)
{
var authProps = new AuthenticationProperties(
new Dictionary<string, string>
{
{PolicyKey, ResetPasswordPolicyId},
{Constants.Authentication.KatanaAuthenticationType, B2CProvider }
});
notification.OwinContext.Authentication.Challenge(authProps, B2CProvider);
}
else if (notification.ProtocolMessage.ErrorDescription?.Contains("AADB2C90091") ?? false)
{
// cancelled, so send to sign in again
var context = notification.OwinContext;
var signIn = context.Request.Cookies["b2cSignIn"];
var redir = context.Request.Cookies["b2cRedirUri"];
notification.HandleResponse();
var authProps = new AuthenticationProperties(
new Dictionary<string, string>
{
{PolicyKey, SignUpPolicyId},
{Constants.Authentication.SigninId, signIn },
{Constants.Authentication.KatanaAuthenticationType, B2CProvider}
})
{
RedirectUri = redir
};
notification.OwinContext.Authentication.Challenge(authProps, B2CProvider);
}
else
{
notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
}
return Task.CompletedTask;
}
// This notification can be used to manipulate the OIDC request before it is sent. Here we use it to send the correct policy.
static async Task OnB2CRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var mgr = notification.Options.ConfigurationManager as PolicyConfigurationManager;
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, SignUpPolicyId);
notification.ProtocolMessage.IssuerAddress = config.EndSessionEndpoint;
}
else
{
var dict = notification.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary;
var co = new CookieOptions()
{
HttpOnly = true,
Secure = true,
Expires = DateTime.UtcNow.AddMinutes(10)
};
string policy;
dict.TryGetValue(PolicyKey, out policy);
policy = policy ?? SignUpPolicyId;
if (policy != ResetPasswordPolicyId)
{
// Don't persist the pw reset as that's intermediate
notification.OwinContext.Response.Cookies.Append("b2cSignIn", dict[Constants.Authentication.SigninId], co);
notification.OwinContext.Response.Cookies.Append("b2cRedirUri", notification.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.RedirectUri, co);
}
var config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, policy);
notification.ProtocolMessage.IssuerAddress = config.AuthorizationEndpoint;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using System.Web.Http;
using System.Web.Mvc;
using ClaimsProxy.Models;
using ClaimsProxy.Services;
using IdentityServer3.Core.Configuration;
using IdentityServer3.Core.Models;
using IdentityServer3.Core.Services;
using IdentityServer3.Core.Services.Default;
using IdentityServer3.WsFederation.Configuration;
using IdentityServer3.WsFederation.Models;
using IdentityServer3.WsFederation.Services;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Owin;
using Owin;
using Serilog;
using CookieOptions = IdentityServer3.Core.Configuration.CookieOptions;
[assembly: OwinStartup(typeof(ClaimsProxy.Startup))]
namespace ClaimsProxy
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
TelemetryConfiguration.Active.InstrumentationKey = ConfigurationManager.AppSettings["ai:InstrumentationKey"];
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
Log.Logger = new LoggerConfiguration()
.WriteTo.Trace(outputTemplate: "{Timestamp} [{Level}] ({Name}){NewLine} {Message}{NewLine}{Exception}")
.WriteTo.ApplicationInsightsTraces(new TelemetryClient())
.CreateLogger();
var prefix = ConfigurationManager.AppSettings["cookiePrefix"];
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(new List<Client>())
.UseInMemoryScopes(StandardScopes.All);
factory.UserService = new Registration<IUserService>(new AadUserService());
var viewOptions = new DefaultViewServiceOptions();
viewOptions.CacheViews = false;
factory.ConfigureDefaultViewService(viewOptions);
var options = new IdentityServerOptions
{
SiteName = ConfigurationManager.AppSettings["siteName"],
SigningCertificate = LoadPrimaryCertificate(),
SecondarySigningCertificate = LoadSecondaryCertificate(),
Factory = factory,
PluginConfiguration = ConfigureWsFederation,
AuthenticationOptions = new AuthenticationOptions
{
EnableLocalLogin = false,
EnableSignOutPrompt = false,
IdentityProviders = ConfigureIdentityProviders,
CookieOptions = new CookieOptions
{
ExpireTimeSpan = TimeSpan.FromHours(1),
Prefix = prefix
},
EnableAutoCallbackForFederatedSignout = true,
EnablePostSignOutAutoRedirect = true,
PostSignOutAutoRedirectDelay = 2
}
};
app.UseIdentityServer(options);
}
void ConfigureWsFederation(IAppBuilder pluginApp, IdentityServerOptions options)
{
var factory = new WsFederationServiceFactory(options.Factory);
// data sources for in-memory services
factory.Register(new Registration<IEnumerable<RelyingParty>>(RelyingParties.Get()));
factory.RelyingPartyService = new Registration<IRelyingPartyService>(typeof(InMemoryRelyingPartyService));
var wsFedOptions = new WsFederationPluginOptions
{
IdentityServerOptions = options,
Factory = factory
};
pluginApp.UseWsFederationPlugin(wsFedOptions);
}
X509Certificate2 LoadPrimaryCertificate()
{
// Look for a primary cert config, and use that if set, otherwise, fallback to this test cert for dev
var thumb1 = ConfigurationManager.AppSettings["signingCert:1"];
#if !DEBUG
if (string.IsNullOrWhiteSpace(thumb1))
throw new ArgumentException("Primary certificate thumbprint is required");
#endif
#if DEBUG
if(string.IsNullOrWhiteSpace(thumb1))
return new X509Certificate2(HostingEnvironment.MapPath("~/App_Data/idsrv3test.pfx"), "idsrv3test");
#endif
return GetCertificateFromStore(thumb1);
}
X509Certificate2 LoadSecondaryCertificate()
{
// Look for a secondary cert config, and use that if set, otherwise, return null
var thumb2 = ConfigurationManager.AppSettings["signingCert:2"];
if (string.IsNullOrWhiteSpace(thumb2))
{
return null;
}
return GetCertificateFromStore(thumb2);
}
X509Certificate2 GetCertificateFromStore(string thumbprint)
{
var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
if (certs.Count != 1)
{
throw new ArgumentException($"Certificate {thumbprint} was not found", nameof(thumbprint));
}
return certs[0];
}
static void ConfigureIdentityProviders(IAppBuilder app, string signInAsType)
{
ConfigureB2CAuth(app, signInAsType);
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=301880
-->
<configuration>
<appSettings>
<add key="webpages:Version" value="3.0.0.0" />
<add key="webpages:Enabled" value="false" />
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
<add key="siteName" value="OIDC Bridge" />
<add key="ai:InstrumentationKey" value="" />
<add key="cookiePrefix" value="dev1" />
<add key="signingCert:1" value="" />
<add key="signingCert:2" value="" />
<add key="b2c:Tenant" value=" ... .onmicrosoft.com" />
<add key="b2c:ClientId" value="" />
<add key="b2c:AadInstance" value="https://login.microsoftonline.com/{0}{1}{2}" />
<add key="b2c:RedirectUri" value="https://localhost:44352/" />
<add key="b2c:SignUpPolicyId" value="B2C_1_signuporsignin" />
<add key="b2c:ResetPasswordPolicyId" value="B2C_1_passwordreset" />
<add key="rp:1:Realm" value="urn:sharepoint:SharePoint-Dev" />
<add key="rp:1:Name" value="SharePoint RP" />
<add key="rp:1:ReplyUrl" value="https://sharepoint.example.com/_trust/" />
<add key="rp:1:PostLogoutRedirectUri" value="https://sharepoint.example.com/_layouts/SignOut.aspx" />
<add key="rp:2:Realm" value="urn:testrp:local" />
<add key="rp:2:Name" value="Test RP Local" />
<add key="rp:2:ReplyUrl" value="https://localhost:44369/" />
<add key="rp:2:PostLogoutRedirectUri" value="https://localhost:44369/Account/SignOutCallback" />
</appSettings>
<system.web>
<compilation debug="true" targetFramework="4.6.1" />
<httpRuntime targetFramework="4.6.1" />
<httpModules>
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
<!-- This is required to use our patched version of Microsoft.IdentityModel.Protocol.Extensions -->
<hostingEnvironment shadowCopyBinAssemblies="false" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="ApplicationInsightsWebTracking" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" preCondition="managedHandler" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers></system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-9.0.0.0" newVersion="9.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.20622.1351" newVersion="4.0.20622.1351" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.IdentityModel.Protocol.Extensions" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-1.0.2.33" newVersion="1.0.2.33" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.1.0.0" newVersion="2.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Runtime.Serialization.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Security.Cryptography.X509Certificates" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Win32.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Serilog" publicKeyToken="24c2f752a8e58a10" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<system.diagnostics>
<trace autoflush="true" indentsize="0">
<listeners>
<add name="myAppInsightsListener" type="Microsoft.ApplicationInsights.TraceListener.ApplicationInsightsTraceListener, Microsoft.ApplicationInsights.TraceListener" />
</listeners>
</trace>
</system.diagnostics>
<system.codedom>
<compilers>
<compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:6 /nowarn:1659;1699;1701" />
<compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=1.0.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:14 /nowarn:41008 /define:_MYTYPE=\&quot;Web\&quot; /optionInfer+" />
</compilers>
</system.codedom>
</configuration>
@clairernovotny
Copy link
Author

Needs this to be fixed to prevent deadlock...or build your own version
AzureADQuickStarts/B2C-WebApp-WebAPI-OpenIDConnect-DotNet#2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment