Register MicroServices in Azure Active Directory (AAD) for Security - azure

I have a service fabric application (Stateless and Statefull) deployed in Service fabric cluster. I am trying to implement security in the applications. The application uses the Active Directory Authentication Library (ADAL) to get a token from Azure AD using the OAuth 2.0 client credential flow, where the client credential is a password. I am able to implement the same scenario in ordinary web api applications by registering them in Azure portal. Can anyone tell me how to register a service fabric microservice application with WebApi exposed using Owin. i have difficulties registering the reply url and sign on url as the urls are dynamic(for statefull partitionid and replica id). I receive unauthorized access while calling the corresponding service. I am not sure of what url has to be registered for a statefull or stateless application when adding the application in in azure active directory. Could you please suggest me where I'm wrong and what to do to implement.

Can anyone tell me how to register a service fabric microservice application with WebApi exposed using Owin. i have difficulties registering the reply url and sign on url as the urls are dynamic(for statefull partitionid and replica id).
The client credential flow is used for the service or daemon app. There is not need to use the redirect_url when we use the client credential flow to acquire the token. You can register any validate redirect_url. Here is an example that using the client credential:
POST https://login.microsoftonline.com/<tenantId>/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=<clientId>
&client_secret=<clientSecret>
&resource=<app id uri of your web api >
And it is same that to integrate with Azure AD with web API using Azure service fabric. Here is an example for your reference:
1 . register an web app(app1) which used to protect the web API on Azure portal
2 . register an web app(app2) as the client to request the web API
3 . grant the the app1 to app2 from portal
4 . create Service Fabric application with Stateless Web API template
5 . config the app.config of Service Fabric application
<add key="ida:Audience" value="app id Uri of app1" />
<add key="ida:Tenant" value="tenantId" />
6 . install the package Microsoft.Owin.Security.ActiveDirectory
Install-Package Microsoft.Owin.Security.ActiveDirectory
7. modify the startup code like below:( Note: the method appBuilder.UseWindowsAzureActiveDirectoryBearerAuthentication is before appBuilder.UseWebApi(config).
public static void ConfigureApp(IAppBuilder appBuilder)
{
// Configure Web API for self-host.
HttpConfiguration config = new HttpConfiguration();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
appBuilder.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Audience = ConfigurationManager.AppSettings["ida:Audience"],
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false
}
});
appBuilder.UseWebApi(config);
}
run the Service Fabric Application
acquire the token using the client credential flow mentioned above( clientId and clientSecret is from app2)
request the service public by Service Fabric Application with the access token and it works well

Related

azure oauth 2.0 how to protect web api from external organization web api?

I am new to Azure and trying to protect/web api hosted in azure using oauth 2.0.
This web api will be called from other web api/deamon which is in control of other organization.
I am aware of client credential flow, but in this scenario external api is hosted outside azure ad. We have no idea of where it is hosted and how this third external web api/deamon is hosted? How should we do authentication/authorization for our web api, so that any external service can use it?
You know about client credential flow, then you should know that this kind of flow doesn't need a user to sign in to generate access token, but only need an azure ad application with the client secret. This azure ad application can come from your tenant, so it doesn't require the web api/deamon which is in control of other organization to have an application, you can create the app in your tenant then provide it to the external web api. What you need to make sure is that the external is really a daemon application.
Let's assume that the external app that need to call your api which is protected by azure ad is a daemon application, then client credential flow is suitable here.
Code for external api to generate access token
//you can see it when you add api permission
var scopes = new[] { "api://exposed_apis_app_id/.default" };
var tenantId = "your_tenant_name.onmicrosoft.com";
var clientId = "YOUR_CLIENT_ID";
var clientSecret = "YOUR_CLIENT_SECRET";
// using Azure.Identity;
var options = new TokenCredentialOptions{AuthorityHost = AzureAuthorityHosts.AzurePublicCloud};
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret, options);
var tokenRequestContext = new TokenRequestContext(scopes);
var token = clientSecretCredential.GetTokenAsync(tokenRequestContext).Result.Token;
Code for your api to add authentication for azure ad, you still have some more configurations, you can refer to my this answer, some related document: authorize the token with role and jwt token configuration.
[Authorize]
public class HelloController : Controller
{
public IActionResult Index()
{
HttpContext.ValidateAppRole("User.Read");//You set it when adding app role
Student stu = new Student();
stu.age = 18;
return Json(stu) ;
}
}
appsettings:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "2c0xxxxxxx57",
"Domain": "tenantname.onmicrosoft.com", // for instance contoso.onmicrosoft.com. Not used in the ASP.NET core template
"TenantId": "common",
"Audience": "8fxxxx78"
}
startup.cs, don't forget "app.UseAuthentication();app.UseAuthorization();" in Configure method
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
services.AddControllersWithViews();
}
create an azure ad application in your tenant and expose an api with a role.
you can create another azure ad application, add client secret and add the application permission created before in the API permissions blade.
provide the application id and client secret to those external app and let them use these to generate access token, then they can use the access token to call your api.
modify your api to authorize the token if has the correct role.

How to secure an Azure Function accessed by a Blazor Wasm app with Azure AD B2C?

Scenario: I have a Blazor wasm app secured with B2C Authentication that needs to call an HTTP triggered Azure function. What would be the best method to secure that Azure function so that only the Blazor app and/or authenticated users could call that function?
So far I know how to secure the Blazor app with B2C (obviously silly!) and I've also been able to add B2C auth to an Azure function and secure the calls by validating the jwt token. But it's not clear in my head how the two parts should jive together.
Should I expose an API in the app registration of the Azure Function in the B2C tenant? If so, how the Blazor app would make authenticated calls to the Azure function?
Or do I simply send the jwt token from the Blazor app through the http request headers of the Azure function call and then validate that token manually inside the function?
I've been reading a lot of different posts on the subject lately but I still can't figure out what's THE best solution to achieve it.
Any help/cues would be appreciated.
Thanks!
ps: I'm not interested in using the Azure API management since it's a little bit on the pricey side for a pretty simple app solution.
If you want to call Azure function projected by Azure AD B2C, please refer to the following steps
Configure Azure AD B2C for Azure function
Create Azure B2C app.
Web App/API : Yes
Allow Implicit Flow : Yes
Set Reply URL in B2C app: https://{function app url}/.auth/login/aad/callback
Set App ID URL in B2C App : https://{tennat}/{prefix}
Note down B2C apps Application ID.
Define API scope. Go to B2C app => Published scopes.
Get your B2C user flows/policy’s metadata URL. Note down this URL.
It can be obtained from Run User Flow page.
It’s format is like https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policy}.
Go to your functions => Authentication / Authorization.
Set following
App Service Authentication : On
Action to take when not authenticated : Login with Azure AD
Authentication providers : Azure AAD
Management Mode : Advanced
Client Id : {Application Id from Step 4}
Issuer URL : {URL from step 6}
Allowed Audience: {Application Id URL from Step 3}
Create Client application For more details, please refer to here.
Configure CORS policy in Azure Function
Configure Application
Create
dotnet new blazorwasm -au IndividualB2C --aad-b2c-instance "{AAD B2C INSTANCE}" --client-id "{CLIENT ID}" --domain "{TENANT DOMAIN}" -o {APP NAME} -ssp "{SIGN UP OR SIGN IN POLICY}"
Code
Create Custom AuthorizationMessageHandler class
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { "<your function app url>" },
scopes: new[] { "<the function app API scope your define>" });
}
}
Add the following code in Program.cs.
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("app");
// register CustomAuthorizationMessageHandler
builder.Services.AddScoped<CustomAuthorizationMessageHandler>();
// configure httpclient
// call the following code please add packageMicrosoft.Extensions.Http 3.1.0
builder.Services.AddHttpClient("ServerAPI", client =>
client.BaseAddress = new Uri("<your function app url>"))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
// register the httpclient
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("ServerAPI"));
// configure Azure AD auth
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("<the function app API scope your define>");
});
await builder.Build().RunAsync();
}
Call the API
#page "/fetchdata"
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#inject HttpClient Http
<h1>Call Azure Function</h1>
<p>This component demonstrates fetching data from the server.</p>
<p>Result: #forecasts</p>
<button class="btn btn-primary" #onclick="CallFun">Call Function</button>
#code {
private string forecasts;
protected async Task CallFun()
{
forecasts = await Http.GetStringAsync("api/http");
}
}

Blazor standalone client calling a function via Azure B2C

Is there a good example or a walkthrough of a Blazor standalone app calling a function app via an Azure Active Directory B2C passing in a claim with an identity to the function?
I have been working my way through the documentation,
Secure an ASP.NET Core Blazor WebAssembly standalone app with Azure Active Directory B2C and
ASP.NET Core Blazor WebAssembly additional security scenarios but cannot get past 404 result.
So what am I trying to achieve? I want to put together a couple of POCs. The first is a Blazor standalone client app, authenticating via B2C, then passing an authorisation token claims token to an azure function, where I can unpack the user id and email address. The second is same as above, but with Api Management between the Blazor client and the functions api. Because of the nature of the project I am using the consumption plan for both the functions and api management.
So just concentrating on the first case (Blazor - B2C - Function), on the assumption if I get that to work, the api will follow…
I have a B2C client tenant with 2 applications: a front end app authorised for the scopes of a 2nd B2C application for the api. My Function app has authentication/authorisation set to 'Login with Active Directory' with the client ID set to the Front end app's client id, the issuer uri set to the B2C's pocsignupsignin Userflow and the 'Allowed Token Audiences' set to the client id of the Api in B2C.
I can get a JWT token via a browser and using postman successfully call a function in my function app passing a bearer token.
My Blazor app can log in to B2C. If I have no authorisation configured for the web app, then my function call is successful.
But once I turn authorisation I run into CORS 404 - origin 'https://localhost:44389' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. But I have CORS configured with 'Enable Access-Control-Allow-Credentials' and my client url configured.
What am I missing?
I also suspect that the token with the id will not be passed correctly.
From Program.cs
builder.Services.AddHttpClient("ServerAPI",
client => client.BaseAddress = new Uri(functionURI))
.AddHttpMessageHandler<CustomAuthorizationMessageHandler>();
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add(readScopeURI);
options.ProviderOptions.DefaultAccessTokenScopes.Add(writeScopeURI);
Console.WriteLine($"options.ProviderOptions.DefaultAccessTokenScopes {options.ProviderOptions.DefaultAccessTokenScopes}");
});
From CustomAuthorizationMessageHandler
public class CustomAuthorizationMessageHandler : AuthorizationMessageHandler
{
public CustomAuthorizationMessageHandler(IAccessTokenProvider provider,
NavigationManager navigationManager)
: base(provider, navigationManager)
{
ConfigureHandler(
authorizedUrls: new[] { B2CClientUrl },
scopes: new[] { "read", "write" });
}
}
From appsettings
"AzureAdB2C":
{
"Authority": B2C signupsignin uri,
"ClientId": B2C frontend client,
"ValidateAuthority": false
}
Function call
async Task GetFromDedicatedFunctionClient(string subUrl)
{
try
{
var client = ClientFactory.CreateClient("ServerAPI");
Console.WriteLine($"client.BaseAddress {client.BaseAddress}");
result =
await client.GetStringAsync(subUrl);
}
catch …
}

How do I authorize a service to call an API using Azure Active Directory?

I have a service that gets an access token from Azure AD. I have an API that I would like to accept that token as authorization.
My service code to call the API is
HttpClient client = new HttpClient()
{
BaseAddress = new Uri("https://localhost:44372/")
};
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, client.BaseAddress + "api/todolist");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.SendAsync(request);
The response I get back is a 401 - Unauthorized.
I have a feeling that the issue is in the API ConfigureServices function; specifically (this was taken from an example, so I don't really know what it means yet):
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.TokenValidationParameters.RoleClaimType = "roles";
});
I'm new to Azure and authentication in general so I don't know what options are available or appropriate. I also am not sure how to set up the applications in Azure to allow this. I have the application id of the service set up as an Authorized client application of the API; it is also listed int the knownClientApplications in the API manifest.
There are just so many knobs to turn, I have no idea where to go from here. If anyone can let me know some things to try, that would be outstanding.
Thanks
Here is a code sample on how to call a web API in an ASP.NET Core web app using Azure AD:
https://learn.microsoft.com/en-us/samples/azure-samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore/calling-a-web-api-in-an-aspnet-core-web-application-using-azure-ad/
This sample contains a web API running on ASP.NET Core 2.0 protected by Azure AD. The web API is accessed by an ASP.NET Core 2.0 web application on behalf of the signed-in user. The ASP.NET Web application uses the OpenID Connect middleware and the Active Directory Authentication Library (ADAL.NET) to obtain a JWT bearer token for the signed-in user using the OAuth 2.0 protocol. The bearer token is passed to the web API, which validates the token and authorizes the user using the JWT bearer authentication middleware.

Azure AD token has already access to other app without permissions

We have 2 apps registered in Azure AD, let's call them WebApi1 and WebApi2.
WebApi1 needs to call WebApi2. A secret has been configured in WebApi1 in order to get a token. Here is the code I'm using to get the token and then make the call to WebApi2:
And here is how my WebApi2 is configured:
The thing that I don't understand is that I would expect WebApi2 to return a 401 exception since I have not set any permissions in Azure (via the App Registration portal) to WebApi1.
Yet, the call is made successfully and WebApi1 has access to WebApi2.
Why WebApi1 has access to WebApi2 without the use of permissions in Azure?
Your web api application should check access using the IsInRole() or the [Authorize] attribute. If your web api doesn't check access , by default the access token with no application roles(permission) could access to your web api .
Please refer to document Roles based access control in cloud applications using Azure AD . Since you are acquiring token with application identity (client credential flow) , please check the Assigning client applications to application roles of resource APIs section in the document .
Just another thing.
If you're working with Azure and roles, when setting the WindowsAzureActiveDirectoryBearerAuthenticationOptions, you'll need to set the right role type in order for IsInRole (or Authorize("YourRole")) to work.
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuer = false,
ValidAudience = ConfigurationManager.AppSettings["AzureAd:Audience"],
RoleClaimType = System.Security.Claims.ClaimTypes.Role
},
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
Tenant = ConfigurationManager.AppSettings["AzureAd:Tenant"],
});

Resources