This is the second part of the tutorial which can cover Using Azure AD B2C tenant with ASP.NET Web API 2 and numerous entrance finish shoppers.
The supply code for this tutorial is obtainable on GitHub.
The Web API has been revealed on Azure App Providers, so be happy to attempt it out using the Base URL (https://aadb2cresourceapi.azurewebsites.net/)
Secure ASP.NET Web API 2 using Azure AD B2C
In the previous publish, we have now configured all the wanted insurance policies in our Azure AD B2C tenant and the Reply URLs. In this Submit, weâll reconfigure the Web API weâve got created in the previous publish so it relays on the Azure AD B2C IdP we created to secure it and our Web API will only accept and belief tokens issued by our Azure AD BC IdP.
So letâs begin implementing those modifications on the Web API.
Step 1:Â Configure Web API to use Azure AD B2C tenant IDs and Policies
Now we need to modify the online.config for our Web API by adding the under keys, so open Web.config and add the under AppSettings keys:
The values for these keys as the following:
âida:AadInstanceâ value accommodates the metadata discovery endpoint for each coverage, this endpoint will probably be used internally by the middle-wares which weâll add in the subsequent steps to validate the JWT tokens.
âida:Tenantâ value accommodates the URL for our Azure AD B2C tenant we now have already outlined within the previous submit.
âida:ClientIdâ worth incorporates the App shopper Id we now have already registered with our Azure AD Bb2C tenant.
âida:SignUpPolicyIdâ value incorporates the identify of the signup policy we already created.
âida:SignInPolicyIdâ worth accommodates the identify of the Signin policy we already created.
âida:UserProfilePolicyIdâ worth accommodates the identify of the Create profile coverage we already created.
Step 2:Â Add the additional NyGet packages to the Web API undertaking
Open NuGet Package deal Supervisor Console and set up/replace the under packages:
Install-Package deal Microsoft.Owin.Safety.OAuth -Model 3.0.1 Set up-Package deal Microsoft.Owin.Security.Jwt -Model three.zero.1 Update-Package deal System.IdentityModel.Tokens.Jwt -version 4.zero.2.206221351 Install-Package deal Microsoft.IdentityModel.Protocol.Extensions
Set up-Package deal Microsoft.Owin.Safety.OAuth -Version three.0.1
Install-Package deal Microsoft.Owin.Safety.Jwt -Version 3.zero.1
Update-Package deal System.IdentityModel.Tokens.Jwt -version 4.0.2.206221351
Install-Package deal Microsoft.IdentityModel.Protocol.Extensions
The packages weâve got put in is answerable for configuring our API to make use of OAuth bearer token for cover as nicely weâve added the packages which are answerable for validating, parsing and decoding JWT tokens.
I had to downgrade the package deal âSystem.IdentityModel.Tokens.Jwtâ to the model â4.0.2.206221351â as the newer version of it showed breaking compatibility with other packages, Iâll control it and replace the publish accordingly if there are new updates.
lastly, we need to add manually using âAdd referenceâ dialog a reference for the meeting âSystem.IdentityModelâ v4, Iâm unsure if thereâs something incorrect with these NuGet packages which overlook to include this dependency by default. I will regulate this too and replace the submit if there is a change.
Step three:Â Configure the Startup class
Now we have to configure our API to depend on the Azure AD B2C IdP we already created, this is an important step in configuring the Web API to trust tokens issued by our Azure AD b2C IdP, our Web API will have the ability to eat solely JWT tokens issued by the trusted IdP and issued for a specific shopper only (The app we registered in the earlier publish âBitoftech Demo Appâ).
To take action, open the âStartupâ class for the Web API and exchange all of the content with under code, a lot of the modifications has been launched in technique âConfigureOAuthâ which I will describe what happen within the subsequent paragraph.
public class Startup
// These values are pulled from net.config public static string aadInstance = ConfigurationManager.AppSettings[âida:AadInstanceâ]; public static string tenant = ConfigurationManager.AppSettings[âida:Tenantâ]; public static string clientId = ConfigurationManager.AppSettings[âida:ClientIdâ]; public static string signUpPolicy = ConfigurationManager.AppSettings[âida:SignUpPolicyIdâ]; public static string signInPolicy = ConfigurationManager.AppSettings[âida:SignInPolicyIdâ]; public static string editProfilePolicy = ConfigurationManager.AppSettings[âida:UserProfilePolicyIdâ];
public void Configuration(IAppBuilder app)
HttpConfiguration config = new HttpConfiguration();
// Web API routes config.MapHttpAttributeRoutes();
ConfigureOAuth(app);
app.UseWebApi(config);
public void ConfigureOAuth(IAppBuilder app)
app.UseOAuthBearerAuthentication(CreateBearerOptionsFromPolicy(signUpPolicy)); app.UseOAuthBearerAuthentication(CreateBearerOptionsFromPolicy(signInPolicy)); app.UseOAuthBearerAuthentication(CreateBearerOptionsFromPolicy(editProfilePolicy));
personal OAuthBearerAuthenticationOptions CreateBearerOptionsFromPolicy(string coverage)
var metadataEndpoint = string.Format(aadInstance, tenant, policy);
TokenValidationParameters tvps = new TokenValidationParameters
// That is the place you specify that your API solely accepts tokens from its personal shoppers ValidAudience = clientId, AuthenticationType = policy, NameClaimType = âhttp://schemas.microsoft.com/identity/claims/objectidentifierâ ;
return new OAuthBearerAuthenticationOptions
// This SecurityTokenProvider fetches the Azure AD B2C metadata & signing keys from the OpenIDConnect metadata endpoint AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(metadataEndpoint)) ;
1
2
3
four
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class Startup
// These values are pulled from net.config
public static string aadInstance = ConfigurationManager.AppSettings[âida:AadInstanceâ];
public static string tenant = ConfigurationManager.AppSettings[âida:Tenantâ];
public static string clientId = ConfigurationManager.AppSettings[âida:ClientIdâ];
public static string signUpPolicy = ConfigurationManager.AppSettings[âida:SignUpPolicyIdâ];
public static string signInPolicy = ConfigurationManager.AppSettings[âida:SignInPolicyIdâ];
public static string editProfilePolicy = ConfigurationManager.AppSettings[âida:UserProfilePolicyIdâ];
public void Configuration(IAppBuilder app)
HttpConfiguration config = new HttpConfiguration();
// Web API routes
config.MapHttpAttributeRoutes();
ConfigureOAuth(app);
app.UseWebApi(config);
public void ConfigureOAuth(IAppBuilder app)
app.UseOAuthBearerAuthentication(CreateBearerOptionsFromPolicy(signUpPolicy));
app.UseOAuthBearerAuthentication(CreateBearerOptionsFromPolicy(signInPolicy));
app.UseOAuthBearerAuthentication(CreateBearerOptionsFromPolicy(editProfilePolicy));
personal OAuthBearerAuthenticationOptions CreateBearerOptionsFromPolicy(string policy)
var metadataEndpoint = string.Format(aadInstance, tenant, coverage);
TokenValidationParameters tvps = new TokenValidationParameters
// That is where you specify that your API only accepts tokens from its own shoppers
ValidAudience = clientId,
AuthenticationType = coverage,
NameClaimType = âhttp://schemas.microsoft.com/identity/claims/objectidentifierâ
;
return new OAuthBearerAuthenticationOptions
// This SecurityTokenProvider fetches the Azure AD B2C metadata &#zero38; signing keys from the OpenIDConnect metadata endpoint
AccessTokenFormat = new JwtFormat(tvps, new OpenIdConnectCachingSecurityTokenProvider(metadataEndpoint))
;
What weâve got carried out is the next:
Weâve got configured our API to eat and belief JWT tokens issued by our IdP (âBitofTechDemo.onmicrosoft.comâ) for a selected shopper (âbc348057-3c44-42fc-b4df-7ef14b926b78â) This shopper represents the app we already registered, as properly it should only accept JWT tokens for the three policies we already defined and named âB2C_1_signupâ, âB2C_1_Signinâ, and âB2C_1_Editprofileâ. Another JWT tokens donât meet these criteria can be rejected and 401 HTTP response is returned to the requestor.
Again to the Metadata Discovery Endpoint we mentioned earlier, this endpoint is auto generated by our IdP and it is extremely essential for our API to acquire the âSigning Tokens Keysâ from it; to be able to validate the JWT signature and trust this JWT token and send a response to the requestor. This finish point is constructed at run time, for instance, the metadata endpoint for the âB2C_1_signinâ can be as the comply with: https://login.microsoftonline.com/BitofTechDemo.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1_signin feel free to click on the hyperlink and observe the info returned.
A nice trick right here that Iâve configured the âNameClaimTypeâ of the âTokenValidationParametersâ to make use of the claim named âobjectidentiferâ (âoidâ) This can facilitate studying the distinctive consumer id for the authenticated consumer contained in the controllers, all we need to name now contained in the controller is: âUser.Identity.Nameâ as an alternative of querying the claims collection each time.
The last thing we need to add to the âStartupâ class is a brand new class named âOpenIdConnectCachingSecurityTokenProviderâ, this class might be answerable for communicating with the âMetadata Discovery Endpointâ and problem HTTP requests to get the signing keys that our API will use to validate signatures from our IdP, those keys exists within the jwks_uri which may read from the invention endpoint. So as to add this class create a brand new class named âOpenIdConnectCachingSecurityTokenProviderâ and paste the code under:
// This class is important as a result of the OAuthBearer Middleware does not leverage // the OpenID Connect metadata endpoint uncovered by the STS by default. public class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityTokenProvider
public ConfigurationManager _configManager; personal string _issuer; personal IEnumerable _tokens; personal readonly string _metadataEndpoint;
personal readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();
public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
_metadataEndpoint = metadataEndpoint; _configManager = new ConfigurationManager(metadataEndpoint);
RetrieveMetadata();
///
/// Gets the issuer the credentials are for. ///
/// /// The issuer the credentials are for. /// public string Issuer
get
RetrieveMetadata(); _synclock.EnterReadLock(); attempt
return _issuer;
lastly
_synclock.ExitReadLock();
///
/// Gets all recognized security tokens. ///
/// /// All recognized security tokens. /// public IEnumerable SecurityTokens
get
RetrieveMetadata(); _synclock.EnterReadLock(); attempt
return _tokens;
finally
_synclock.ExitReadLock();
personal void RetrieveMetadata()
_synclock.EnterWriteLock(); attempt
OpenIdConnectConfiguration config = Activity.Run(_configManager.GetConfigurationAsync).Outcome; _issuer = config.Issuer; _tokens = config.SigningTokens;
lastly
_synclock.ExitWriteLock();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
// This class is important because the OAuthBearer Middleware does not leverage
// the OpenID Join metadata endpoint uncovered by the STS by default.
public class OpenIdConnectCachingSecurityTokenProvider : IIssuerSecurityTokenProvider
public ConfigurationManager _configManager;
personal string _issuer;
personal IEnumerable _tokens;
personal readonly string _metadataEndpoint;
personal readonly ReaderWriterLockSlim _synclock = new ReaderWriterLockSlim();
public OpenIdConnectCachingSecurityTokenProvider(string metadataEndpoint)
_metadataEndpoint = metadataEndpoint;
_configManager = new ConfigurationManager(metadataEndpoint);
RetrieveMetadata();
///
/// Will get the issuer the credentials are for.
///
///
/// The issuer the credentials are for.
///
public string Issuer
get
RetrieveMetadata();
_synclock.EnterReadLock();
attempt
return _issuer;
finally
_synclock.ExitReadLock();
///
/// Gets all recognized security tokens.
///
///
/// All recognized safety tokens.
///
public IEnumerable SecurityTokens
get
RetrieveMetadata();
_synclock.EnterReadLock();
attempt
return _tokens;
finally
_synclock.ExitReadLock();
personal void RetrieveMetadata()
_synclock.EnterWriteLock();
attempt
OpenIdConnectConfiguration config = Activity.Run(_configManager.GetConfigurationAsync).End result;
_issuer = config.Issuer;
_tokens = config.SigningTokens;
finally
_synclock.ExitWriteLock();
Step 4:Â Add protected controller for testing
Now weâll add an (non-compulsory) class which reads all of the decoded claims in the JWT token and return them within the response, observe that weâve got protected our controller by the [Authorize] attribute so solely valid JWT tokens shall be accepted to serve the request and return response, to do so add new controller named âProtectedControllerâ beneath folder Controllers and paste the code under:
[Authorize] [RoutePrefix(âapi/protectedâ)]
public class ProtectedController : ApiController
[Route(ââ)] public IEnumerable Get()
var id = Consumer.Id as ClaimsIdentity;
var userName = id.Identify;
return id.Claims.Choose(c => new
Sort = c.Sort, Value = c.Worth );
1
2
3
four
5
6
7
eight
9
10
11
12
13
14
15
16
17
18
[Authorize]
[RoutePrefix(âapi/protectedâ)]
public class ProtectedController : ApiController
[Route(ââ)]
public IEnumerable Get()
var id = Consumer.Id as ClaimsIdentity;
var userName = id.Identify;
return id.Claims.Choose(c => new
Sort = c.Sort,
Worth = c.Worth
);
Weâll check the controller within the next steps.
Step 5:Â Modify the âOrdersControllerâ we created within the previous submit
Now we need to do a very little change on the âOrdersControllerâ by including the [Authorize] attribute on the controller to guard it, as properly to read the unique authenticated consumer id from the declare and retailer it in Azure Table Storage as an alternative of the fastened worth we defined earlier within the previous publish. The change could be very simple and ought to be as the under snippet, a lot of the code is omitted so simply add/exchange the lacking elements as the under:
[Authorize] [RoutePrefix(âapi/Ordersâ)]
public class OrdersController : ApiController
[Route(ââ)] public IHttpActionResult Get()
//This can be learn from the entry token claims. var userId = Consumer.Id.Identify;
// code ommited for brevity
[Route(ââ)] public IHttpActionResult Submit (OrderModel order)
//This shall be learn from the access token claims. var userId = Consumer.Id.Identify;
// code ommited for brevity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[Authorize] [RoutePrefix(âapi/Ordersâ)]
public class OrdersController : ApiController
[Route(ââ)]
public IHttpActionResult Get()
//This will probably be learn from the access token claims.
var userId = Consumer.Id.Identify;
// code ommited for brevity
[Route(ââ)]
public IHttpActionResult Publish (OrderModel order)
//This shall be read from the access token claims.
var userId = Consumer.Id.Identify;
// code ommited for brevity
Step 6:Â Testing out the protected controllers
Last item we have to do here is to obtain a JWT token from out IdP by executing one of many insurance policies we have now outlined, then sending the JWT token in the Authorization header for one among out API endpoints, if all is sweet we should always access the API and return the protected assets.
To do that I have choose the policy Sign up from Azure AD B2C tenant and clicked on âRun nowâ button, Â a brand new window will open and you provide a legitimate credentials, then a redirect will happen to the defined Reply URL and you may acquire the token manually by copying it, then you might want to ship the token to the top level âapi/protectedâ or âapi/ordersâ within the authorization header using the bearer scheme. The request will look because the under picture:
GET Request to the âApi/ordersâ endpoint to listing all the orders assigned to the consumer with e mail â[email protected]â
Notice: This guide testing demonstrated right here is just for testing purposes, in the subsequent submit you will notice how weâll add an MVC software which might be liable for executing the policies, get hold of JWT tokens, create the session for the authenticated users, and entry the protected API assets.
The Supply code for this tutorial is on the market on GitHub.
Comply with me on Twitter @tjoudeh
The post Secure ASP.NET Web API 2 using Azure AD B2C â Part 2 appeared first on Tech Amender.














