Regarding Configure AAD Integration using ASPNet Core 2.1 Web API - azure

I already have a AAD integrated WebAPP using ASP.Net Core 2.1 but now i want to develop an API using ASPNet Core 2.1 to authenticate AAD user to my api using JWT bearer token. I am unable to perform the same because in web app i am using Cookie Auth mode but here i need to implement JWT Bearer which was not working for me. I tried a lot of code from different code repo.
References:
https://github.com/juunas11/Joonasw.AzureAdApiSample
https://github.com/Azure-Samples/active-directory-b2c-dotnetcore-webapi
https://azure.microsoft.com/en-in/resources/samples/active-directory-dotnet-native-aspnetcore-v2/
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = "https://localhost:44395/";
options.Authority = "https://localhost:44395/identity/";
})
.AddJwtBearer("AzureAD", options =>
{
options.Audience = "https://localhost:44395/";
options.Authority = "https://login.microsoftonline.com/tenantID/";
});
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
"AzureAD");
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
When i am changing the mode to Cookie Mode it is working fine but not working in JWTBearer code.
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes (Bearer, AzureAD).
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler:Information: AuthenticationScheme: AzureAD was challenged.
Could anyone help me here? I am stuck here due to this issue.
Thanks in advance

Suppose you have REST API resources written in ASP.NET Core Web API which protected by Azure AD , the client(ASP.NET Web application) can use 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, for example , refer to code sample in first link :
services
.AddAuthentication(o =>
{
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o =>
{
o.Authority = Configuration["Authentication:Authority"];
o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
// Both App ID URI and client id are valid audiences in the access token
ValidAudiences = new List<string>
{
Configuration["Authentication:AppIdUri"],
Configuration["Authentication:ClientId"]
}
};
});
Above code sample use JwtBearerExtensions to validate the access token . You could click here for explanation about scenario.

Related

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.

Access Microsoft Graph API from Azure B2C

I have an Azure AD B2C tenant with an application running. It is configured to use only Azure AD and Microsoft Accounts to login. This application is used by App Center Auth.
I want to access some Microsoft APIs (Microsoft Graph API, Azure DevOps API) from my mobile application with the same login. Therefore I added the API permissions (Azure DevOps -> user_impersonation and Microsoft Graph -> User.Read) to my application in my Azure AD tenant (not the B2C tenant) to grant these permissions on login.
If I now try to use the access token after the login in my application to access e.g. the user in Microsoft Graph, I get an Unauthorized error.
// Sign-in succeeded, UserInformation is not null.
var userInfo = await Auth.SignInAsync();
// Get tokens. They are not null.
var idToken = userInfo.IdToken;
var accessToken = userInfo.AccessToken;
Within the same method, I try to get the user photo from Microsoft Graph
var graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";
var scopes = new[] { "user.read" };
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, graphAPIEndpoint + "/photo/$value");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.SendAsync(request);
var image = await response.Content.ReadAsByteArrayAsync();
UserImage.Source = ImageSource.FromStream(() => new MemoryStream(image));
Has anyone an advice how to configure the B2C / Azure AD application to get access to these API with the Access Token? Or am I on the complete wrong way?
Just like #juunas said, as of today,you need to use AAD Graph API to access Azure AD B2C tenant, this is different from the Microsoft Graph API.
Here is the document for this: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet
Take a look at this documentation published about using the Microsoft Graph API and authenticating against it for B2C instances:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=applications
With recent migration away from login.microsoftonline.com to *.b2clogin.com, if you're using MSAL to obtain the authentication token from the AAD B2C instance, you need to override the authority config, and turn off authority validation, as per this document:
https://learn.microsoft.com/bs-latn-ba/azure/active-directory/develop/msal-b2c-overview
In JavaScript, this is done like below:
const msalConfig = {
auth: {
clientId: "e760cab2-b9a1-4c0d-86fb-ff7084abd902" //This is your client/application ID
authority: "https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_susi", //This is your tenant info
validateAuthority: false
},
};
// create UserAgentApplication instance
const myMSALObj = new Msal.UserAgentApplication(msalConfig);
In C# with the MSAL.NET library, it would be done with the authority URL passed to the PublicClientApplication class constructor.
I was able to consume Graph REST API using the below steps,
Get access token,
POST
URL: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
form-data:
{"client_id": <Registered application clientId>, "client_secret": <Registered application generated secret>, "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials"
Response:
{"token_type" : "", "expires_in" : "", "ext_expires_in" : "", "access_token" : ""}
NOTE: Registered application within b2c tenant should have required grants to access Graph API
Pass token to access API resources, ex: to get user details with objectId,
GET
URL: https://graph.microsoft.com/v1.0/users/{objectId}
set header key
"Authorization": "Bearer " ++ {access_token from above step 1.>

Azure B2C - JWT Signature validation failed

I have setup MSAL to fetch tokens from Azure AD B2C, setup dotnet core WebAPI to accept JWT tokens. Pointed WebApi at the Authority Endpoint:
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(jwtOptions =>
{
string tenant = Configuration["AzureAdB2C:Tenant"], policy = Configuration["AzureAdB2C:Policy"], clientId = Configuration["AzureAdB2C:ClientId"];
jwtOptions.Authority = $"https://login.microsoftonline.com/tfp/{tenant}/{policy}/v2.0/";
jwtOptions.Audience = clientId;
jwtOptions.Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
};
});
as per the samples. MSAL is configured to use the same policy and same client Id and receives token.
MSAL Authority - https://login.microsoftonline.com/tfp/{tenant}.onmicrosoft.com/{policy}/v2.0.
However, that AuthFailed event handler just returns
IDX10501: Signature validation failed. Unable to match keys.
and bounces the auth as a 401.
I went looking for signing keys and the kid of the token is not the same as the kid listed at the discovery endpoint.
https://login.microsoftonline.com/tfp/{tenant}/{policy}/discovery/v2.0/keys
Any ideas?
Seems that I had not selected the correct Issuer claim setting. MSAL was grabbing its token using the https://login.microsoftonline.com/{guid}/v2.0 endpoint whereas WebAPI was using the https://login.microsoftonline.com/tfp/{guid}/{policy}/v2.0/ issuer.
As per the docs this isn't an openid compatible endpoint, but works fine for B2C. Pays to check over the two different claim sets!

Using Oauth to protect WebAPI with Azure active directory

I have browsed all the tutorials regarding using Oauth to protect WebAPI in Azure active directory online. But unfortunately, none of them can work.
I am using VS 2017 and my project is .net core.
So far what I have tried is:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
ervices.AddAuthentication(); // -----------> newly added
}
In "Configure", I added:
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAD:Tenant"]),
Audience = Configuration["AzureAd:Audience"],
});
Here is my config:
"AzureAd": {
"AadInstance": "https://login.microsoftonline.com/{0}",
"Tenant": "tenantname.onmicrosoft.com",
"Audience": "https://tenantname.onmicrosoft.com/webapiservice"
}
I have registered this "webapiservice" (link is: http://webapiservice.azurewebsites.net) on my AAD.
Also, to access this web api service, I created a webapi client "webapiclient" which is also a web api and also registered it on my AAD and requested permission to access "webapiservice". The webapi client link is: http://webapiclient.azurewebsites.net
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("http://webapiservice.azurewebsites.net/");
//is this uri correct? should it be the link of webapi service or the one of webapi client?
HttpResponseMessage response = client.GetAsync("api/values").Result;
if (response.IsSuccessStatusCode)
{
var result = response.Content.ReadAsAsync<IEnumerable<string>>().Result;
return result;
}
else
{
return new string[] { "Something wrong" };
}
So theoretically, I should receive the correct results from webapiservice. but I always received "Something wrong".
Am I missing anything here?
You need an access token from Azure AD.
There are plenty of good example apps on GitHub, here is one for a Daemon App: https://github.com/Azure-Samples/active-directory-dotnet-daemon/blob/master/TodoListDaemon/Program.cs#L96
AuthenticationResult authResult = await authContext.AcquireTokenAsync(todoListResourceId, clientCredential);
This app fetches an access token with its client id and client secret for an API. You can follow a similar approach in your case. You can just replace todoListResourceId with "https://graph.windows.net/" for Azure AD Graph API, or "https://graph.microsoft.com/" for Microsoft Graph API, for example. That is the identifier for the API that you want a token for.
This is the way it works in AAD. You want access to an API, you ask for that access from AAD. In a successful response you will get back an access token, that you must attach to the HTTP call as a header:
Authorization: Bearer accesstokengoeshere......
Now if you are building a web application, you may instead want to do it a bit differently, as you are now accessing the API as the client app, not the user. If you want to make a delegated call, then you will need to use e.g. the Authorization Code flow, where you show the user a browser, redirect them to the right address, and they get sent back to your app for login.
To call web api protected by azure ad , you should pass this obtained access token in the authorization header using a bearer scheme :
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);

Resources