Service to service authentication in Azure without ADAL - azure

I configured azure application proxy for our on-premise hosted web service and turned on Azure AD authentication. I am able to authenticate using ADAL but must find a way to get the token and call web service without ADAL now (we are going to use this from Dynamics 365 online and in sandbox mode I can't use ADAL). I followed some examples regarding service to service scenario and I successfully retrieve the token using client credentials grant flow. But when I try to call the app proxy with Authorization header and access token, I receive an error "This corporate app can't be accessed right now. Please try again later". Status code is 500 Internal server error.
Please note the following:
I don't see any error in app proxy connectors event log.
I added tracing on our on-premise server and it seems like the call never comes there.
If I generate token with ADAL for a NATIVE app (can't have client_secret so I can't use client credentials grant flow), I can call the service.
I created an appRole in manifest for service being called and added application permission to the client app.
This is the way I get the token:
public async static System.Threading.Tasks.Task<AzureAccessToken> CreateOAuthAuthorizationToken(string clientId, string clientSecret, string resourceId, string tenantId)
{
AzureAccessToken token = null;
string oauthUrl = string.Format("https://login.microsoftonline.com/{0}/oauth2/token", tenantId);
string reqBody = string.Format("grant_type=client_credentials&client_id={0}&client_secret={1}&resource={2}", Uri.EscapeDataString(clientId), Uri.EscapeDataString(clientSecret), Uri.EscapeDataString(resourceId));
HttpClient client = new HttpClient();
HttpContent content = new StringContent(reqBody);
content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded");
using (HttpResponseMessage response = await client.PostAsync(oauthUrl, content))
{
if (response.IsSuccessStatusCode)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(AzureAccessToken));
Stream json = await response.Content.ReadAsStreamAsync();
token = (AzureAccessToken)serializer.ReadObject(json);
}
}
return token;
}
AzureAccessToken is my simple class marked for serialization.
I assume it must be something I haven't configured properly. Am I missing some permissions that are required for this scenario?
Any help is appriciated.

Related

How can we secure API to serve only whitelisted clients? Communication between Azure function and Web API

I am using the below design to secure communication between Azure Function and Web API
Step 1 - Request token from AD
Step 2 - Use token to request web api
Code to call the API
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
var endpoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT");
var identity_header = Environment.GetEnvironmentVariable("IDENTITY_HEADER");
var resource = "4df52c7e-3d6f-4865-a499-cebbb2f79d26"; //how to secure this ID
var requestURL = endpoint + "?resource=" + resource + "&api-version=2019-08-01";
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("X-IDENTITY-HEADER", identity_header);
HttpResponseMessage response = await httpClient.GetAsync(requestURL);
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
var access_token = JsonConvert.DeserializeObject<TokenResp>(responseBody).access_token;
var APIURL = "https://frankapp.azurewebsites.net";
HttpClient callAPI = new HttpClient();
callAPI.DefaultRequestHeaders.Add("Authorization","Bearer "+ access_token);
HttpResponseMessage APIResponse = await callAPI.GetAsync(APIURL);
return new OkObjectResult(APIResponse.StatusCode);
}
Question
The solution works as planned, However, I see a security loophole here. That is, any azure function that has the above code or resource id can call this API!!!
How can I solve this security issue? How can I make only listed azure functions to call the API?
There are several solutions to secure the API App, as mentioned in the comment, you could validate the token via the claims, use the access restriction rules, etc.
From your code, it uses the MSI(managed identity) to get the token for the AD App of the API App, then uses the token to call the API. In this case, I recommend you to use User assignment required setting to restrict the access of the API App, after doing the steps below, just the MSI of the function can get the token for the API App, no need to do anything else.
1.Navigate to the AD App of your API App in the Azure Active Directory in the portal -> click the Managed application in local directory -> Properties -> set the User assignment required to Yes.
2.Create a security group in AAD and add the MSI service principal as a member to it, then add the group to the Users and groups, then the MSI will also be able to call the function.(For the user, it can be added directly, MSI is different, you need to use this nested way, or leverage the App role)
After the steps above, just the MSI added to the Users and groups can get the token successfully and call the API, there are two similar issues I have answered, here and here. In the two posts, they want to secure the function app, in your case, it is the same logic.
Security between Azure Function and API app using AAD can be done in multiple ways:
Claims in access token
Whitelist all the IP range where azure function is hosted, deny others.
Users and group policy in AAD as security group.
Put App service and AF in a single VNET (though that restricts multi-region)
Object ID verification
Read more: https://www.tech-findings.com/2020/01/securing-function-app-with-azure-active-directory.html

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.

Xamarin MobileServiceClient RefreshUserAsync with Google 403

I am using Azure's MobileServiceClient sdk to authenticate with my server. With the upgrades to 4.x version I am also using Xamarin.Auth to authenticate users with Google and Facebook. When the response comes back from Google I am getting a refresh token. I then call the mobile service sdk like so:
var accessToken = account.Properties["access_token"];
var idToken = account.Properties["id_token"];
var zumoPayload = new JObject();
zumoPayload["access_token"] = accessToken;
zumoPayload["id_token"] = idToken;
var user = await client.LoginAsync(MobileServiceAuthenticationProvider.Google, zumoPayload, );
This work perfectly fine. What does not work is the call to client.RefreshUserAsync(). That is throwing a 403 every time saying the refresh token is either expired or no longer valid even when I call that method right after I logged in. I do not see many examples at all using the MobileServiceClient 4.x sdk and none of them have examples of how to use the refresh token.
I have tried sending that upin the zumo payload as well but it does not work. I have tried invalidating my user on Google (I am getting the refresh token back), tried logging in through the browser and going to auth/me but the refresh token is not there. Any help would be great!
AFAIK, you could leverage the Xamarin.Auth SDK to independently contact the identity provider and retrieve the access token on your mobile client side, then you need to login with your backend (azure mobile app) along with the token for retrieving the authenticationToken, then you could leverage the authenticationToken to access the resources under your mobile app.
Since you are using Client-managed authentication, for refreshing the new access_token, you need to do it on your mobile client side. I checked Xamarin.Auth and found that there is no method for requesting an access token. You need to refer to Refreshing an access token and implement this feature by yourself. I followed OAuth2Authenticator.cs and created a extension method for requesting an access token as follows:
public static class OAuth2AuthenticatorExtensions
{
public static Task RefreshAccessTokenAsync(this OAuth2Authenticator authenticator, Account account)
{
var dics = new Dictionary<string, string>
{
{"refresh_token",account.Properties["refresh_token"]},
{"client_id", authenticator.ClientId},
{"grant_type", "refresh_token"}
};
if (!string.IsNullOrEmpty(authenticator.ClientSecret))
{
dics["client_secret"] = authenticator.ClientSecret;
}
return authenticator.RequestAccessTokenAsync(dics).ContinueWith(task =>
{
if (task.IsFaulted)
{
//todo:
}
else
{
authenticator.OnRetrievedAccountProperties(task.Result);
}
});
}
}
Additionally, if you leverage Server-managed authentication with Microsoft.Azure.Mobile.Client, then you could leverage RefreshUserAsync for refreshing the access token, at this point your previous access_token, clientId are stored on azure, and your mobile app backend would directly communicate with Google's OAuth 2.0 endpoint and request a new access token for you and update the token store on Azure. For more details about token store within App Service, you could follow here.

AZURE AD ADAL "error":"invalid_grant","error_description":"AADSTS70000: Transmission data parser failure: Authorization Code is malformed or invalid

I am trying to Authenticate using AZURE AD. I took the java client code from the git [https://github.com/Azure-Samples/active-directory-java-webapp-openidconnect][1].
I am able to make the authorize call and get the Authorization code. By passing the authorization code to get the access token using the acquireTokenByAuthorizationCode method from the oauth2 jar. Here I am getting the below error.
"error":"invalid_grant","error_description":"AADSTS70000: Transmission data parser failure: Authorization Code is malformed or invalid.
How to pass the grant_type=authorization_code to the acquireTokenByAuthorizationCode method?
How to check the post request which got fired? I am not able to see it in the network section of the chrome?
Below is the code:
String authCode = authorizationCode.getValue();
ClientCredential credential = new ClientCredential(clientId,
clientSecret);
AuthenticationContext context;
AuthenticationResult result = null;
ExecutorService service = null;
try {
ThreadFactory factory = ThreadManager.currentRequestThreadFactory();
service = Executors.newCachedThreadPool(factory);
context = new AuthenticationContext(authority + tenant + "/", true,
service);
Future<AuthenticationResult> future = context
.acquireTokenByAuthorizationCode(authCode, new URI(
currentUri), credential, null);
The post request should be:
String redirectUrl = authority
+ this.tenant
+ "/oauth2/v2.0/token?p=b2c_1_abcd&grant_type=authorization_code&resource=https%3a%2f%2fgraph.windows.net&redirect_uri="
+ URLEncoder.encode(REDIRECT_URL, "UTF-8");
Not sure how to provide the information which is in bold.
EDIT after more info
If you are using v2 endpoints, you can't use ADAL.
If you have registered your app in the v2 dev portal, you need to register your app via Azure Portal's Azure AD interface. And then make sure your Azure AD URLs do not include v2.0.
To get a token with authorization code, you use acquireTokenByAuthorizationCode (https://github.com/Azure-Samples/active-directory-java-webapp-openidconnect/blob/master/src/main/java/com/microsoft/aad/adal4jsample/BasicFilter.java#L268-L270):
Future<AuthenticationResult> future = context
.acquireTokenByAuthorizationCode(authCode, new URI(
currentUri), credential, null);
You will not see the request in Chrome because it goes from your web server to Azure AD. The browser is not a part of the chain. And that's a good thing since we are passing the client secret to Azure AD. You can use tools like Fiddler to monitor the traffic.

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