Last active
October 16, 2017 11:49
-
-
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/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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=\"Web\" /optionInfer+" /> | |
</compilers> | |
</system.codedom> | |
</configuration> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Needs this to be fixed to prevent deadlock...or build your own version
AzureADQuickStarts/B2C-WebApp-WebAPI-OpenIDConnect-DotNet#2