Skip to content

Instantly share code, notes, and snippets.

@nzpcmad
Created June 8, 2016 04:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nzpcmad/75e19fb6a37d1cbe7ea549d5f697d389 to your computer and use it in GitHub Desktop.
Save nzpcmad/75e19fb6a37d1cbe7ea549d5f697d389 to your computer and use it in GitHub Desktop.
Azure B2C with IdentityServer (sign-in only)
using Microsoft.IdentityModel.Protocols;
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
// Per https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-web-dotnet/
namespace Host.Configuration
{
class HttpDocumentRetriever : IDocumentRetriever
{
private readonly HttpClient _httpClient;
public HttpDocumentRetriever()
: this(new HttpClient())
{
}
public HttpDocumentRetriever(HttpClient httpClient)
{
if (httpClient == null)
{
throw new ArgumentNullException("httpClient");
}
_httpClient = httpClient;
}
public async Task<string> GetDocumentAsync(string address, CancellationToken cancel)
{
if (string.IsNullOrWhiteSpace(address))
{
throw new ArgumentNullException("address");
}
try
{
HttpResponseMessage 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 Microsoft.IdentityModel.Protocols;
using System;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
// Per https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-web-dotnet/
namespace Host.Configuration
{
class HttpDocumentRetriever : IDocumentRetriever
{
private readonly HttpClient _httpClient;
public HttpDocumentRetriever()
: this(new HttpClient())
{
}
public HttpDocumentRetriever(HttpClient httpClient)
{
if (httpClient == null)
{
throw new ArgumentNullException("httpClient");
}
_httpClient = httpClient;
}
public async Task<string> GetDocumentAsync(string address, CancellationToken cancel)
{
if (string.IsNullOrWhiteSpace(address))
{
throw new ArgumentNullException("address");
}
try
{
HttpResponseMessage 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 is the SaaS application. I'm just using a normal MVC application to illustrate the point.
new RelyingParty
{
Realm = "https://localhost:44320/",
Enabled = true,
ReplyUrl = "https://localhost:44320/",
//TokenType = TokenTypes.Saml2TokenProfile11,
TokenLifeTime = 1,
ClaimMappings = new Dictionary<string, string>
{
{ "sub", ClaimTypes.NameIdentifier },
{ "name", ClaimTypes.Name },
{ "given_name", ClaimTypes.GivenName },
{ "surname", ClaimTypes.Surname },
{ "email", ClaimTypes.Email }
}
}
...
// idsrv3 runs as https://my-pc/idsrv3.Host.Web
// You need to configure B2C yourself
var signUpPolicyId = "B2C_1_Sign-up-email";
var signInPolicyId = "B2C_1_Sign-in-email";
var userProfilePolicyId = "B2C_1_Edit-email";
var clientId = "c3...f1f";
var redirectUri = "https://my-pc/idsrv3.Host.Web/core/external";
var OIDCMetadataSuffix = "/.well-known/openid-configuration";
var tenant = "myb2c.onmicrosoft.com";
var aadInstance = "https://login.microsoftonline.com/{0}{1}{2}";
var b2c = new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "b2c",
Caption = "Azure B2C",
SignInAsAuthenticationType = signInAsType,
// These are standard OpenID Connect parameters, with values pulled from web.config
ClientId = clientId,
RedirectUri = redirectUri,
PostLogoutRedirectUri = redirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthenticationFailed = AuthenticationFailed,
RedirectToIdentityProvider = OnRedirectToIdentityProvider,
},
Scope = "openid",
ResponseType = "id_token",
// 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, aadInstance, tenant, "/v2.0", OIDCMetadataSuffix),
new string[] { signUpPolicyId, signInPolicyId, userProfilePolicyId }),
};
app.UseOpenIdConnectAuthentication(b2c);
// Per https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-devquickstarts-web-dotnet/
// This notification can be used to manipulate the OIDC request before it is sent. Here we use it to send the correct policy.
private static async Task OnRedirectToIdentityProvider(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
var policyKey = "b2cpolicy";
PolicyConfigurationManager mgr = notification.Options.ConfigurationManager as PolicyConfigurationManager;
if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
// Not handled = sign-in only
OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, notification.OwinContext.Authentication.AuthenticationResponseRevoke.Properties.Dictionary[policyKey]);
notification.ProtocolMessage.IssuerAddress = config.EndSessionEndpoint;
}
else
{
// Policy hard-coded
OpenIdConnectConfiguration config = await mgr.GetConfigurationByPolicyAsync(CancellationToken.None, "B2C_1_Sign-in-email");
notification.ProtocolMessage.IssuerAddress = config.AuthorizationEndpoint;
}
}
// Used for avoiding yellow-screen-of-death
private static Task AuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
{
notification.HandleResponse();
notification.Response.Redirect("/Home/Error?message=" + notification.Exception.Message);
return Task.FromResult(0);
}
@nzpcmad
Copy link
Author

nzpcmad commented Jun 13, 2016

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