Accessing Function App via App Service Authentication from Dynamics 365 Plugin - azure

I am trying to access an Azure Function from Dynamics 365 Plugin via service to service call: https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-service-to-service
This function is protected via App Service Authentication.
I created a Function App and enabled App Service Authentication
under Platform Features -> Authentication/Authorisation.
I enabled Azure Active Directory as the Auth Provider and set Management mode to Express
I then got the generated Client ID and Client Secret from the Advanced Mode:
Apparently this is all that is needed to make a token request for the Azure function based, based on article I need 4 required parameters:
Client ID
Client Secret
Grant Type
Resource
I make the following request to generate a token from a Dynamics 365 Plugin but get the following error:
Invalid Plugin Execution Exception: Microsoft.Xrm.Sdk.InvalidPluginExecutionException: {"error":"invalid_client","error_description":"AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.\r\nTrace ID: 06ddda7f-2996-4c9b-ab7e-b685ee933700\r\nCorrelation ID: d582e2f2-91eb-4595-b44b-e95f42f2f071\r\nTimestamp: 2018-05-23 06:30:58Z","error_codes":[70002,50012],"timestamp":"2018-05-23 06:30:58Z","trace_id":"06ddda7f-2996-4c9b-ab7e-b685ee933700","correlation_id":"d582e2f2-91eb-4595-b44b-e95f42f2f071"}-The remote server returned an error: (401) Unauthorized.
My code is :
var tokenendpoint = "https://login.microsoftonline.com/de194c13-5ff7-4085-91c3-ac06fb869f28/oauth2/token";
var reqstring = "client_id=" + Uri.EscapeDataString("5f315431-e4da-4f68-be77-4e257b1b9295");
reqstring += "&client_secret=" + Uri.EscapeDataString("/oK7nh8pl+LImBxjm+L7WsQdyILErysOdjpzvA9g9JA=");
reqstring += "&resource=" + Uri.EscapeUriString("https://keyvaultaccess.azurewebsites.net");
reqstring += "&grant_type=client_credentials";
//Token request
WebRequest req = WebRequest.Create(tokenendpoint);
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(reqstring);
req.ContentLength = bytes.Length;
System.IO.Stream os = req.GetRequestStream();
os.Write(bytes, 0, bytes.Length);
os.Close();
//Token response
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
StreamReader tokenreader = new StreamReader(resp.GetResponseStream());
string responseBody = tokenreader.ReadToEnd();
I have made sure that I have the correct Client Secret and have also encoded it as I have read somewhere that '+' and '/' are no good.
I am getting the same error in Postman
Any ideas??

reqstring += "&resource=" + Uri.EscapeUriString("https://keyvaultaccess.azurewebsites.net");
Since you set resource parameter to https://keyvaultaccess.azurewebsites.net, I assumed that you have set the App ID URI of your AAD app (clientId equals 5f315431-xxxxxxx-4e257b1b9295) to https://keyvaultaccess.azurewebsites.net. I assume that you could retrieve the access_token, but when accessing your azure function endpoint with the access_token, you got the 401 status code.
You need to modify your advanced management mode for Active Directory Authentication, add https://keyvaultaccess.azurewebsites.net to ALLOWED TOKEN AUDIENCES or change the resource parameter to your AAD clientID when sending the token request for acquiring the access_token.
Active Directory Authentication configuration:
TEST:
Docode the JWT token to check the aud property:
Access my Azure Function endpoint:
Note: You need to take care of the Authorization level of your function as follows:
If you also enable function level authentication, your request sent to azure function needs to have the relevant code parameter in the querystring or set the header x-functions-key with the value of your function key, or you may just set the authorization level to Anonymous.

Related

DocuSign.eSign.Client.ApiException {"error":"consent_required"}" / Response Type Not Supported when attempting to grant consent

I tried to receive a token with the code below. Unfortunatelly I get the error:
DocuSign.eSign.Client.ApiException
HResult=0x80131500
Nachricht = Error while requesting server, received a non successful HTTP code with response Body: {"error":"consent_required"}
I tried with set TLS 12 and without. We run it in dev mode with base path https://demo.docusign.net/restapi
and oAuthBasePath =account-d.docusign.com
I tried also to set the consens manually with the URL below. But I receive the error in (Login Window) invalid Authorization: RequestType is not supported.
https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=a5ed47d5-xxxx-xxxx-8a19-756da64391de&redirect_uri=https://www.docusign.com
Is the something wrong with my account setting?
byte[] privateKey=DSHelper.ReadFileContent(DSHelper.PrepareFullPrivateKeyFilePath(privateKeyFilename));
var scopes = new List<string>
{
"signature",
"impersonation",
};
var basePath = ApiClient.Production_REST_BasePath;
var oAuthBasePath = OAuth.Production_OAuth_BasePath;
if (!production)
{
basePath = ApiClient.Demo_REST_BasePath;
oAuthBasePath = OAuth.Demo_OAuth_BasePath;
}
var _apiClient = new ApiClient(basePath);
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var authToken = _apiClient.RequestJWTUserToken(
clientId,
ImpersonatedUserId,
oAuthBasePath,
privateKey,
1,
scopes);
I found the solution. instead of reponse_type= code I have to use token
What response_type is supported for an integration key depends on how the key is configured. In the Authentication section of the key's configuration panel, Auth Code Grant allows the response type of code, while Implicit Grant allows the response type of token.
DocuSign's authentication docs assume you have "Auth Code Grant" selected, but either is technically acceptable to allow JWT consent.

MSAL authentication token issues

I have integrated MSAL library in iOS to get the token and send to the our backend server for further use. we are using below code to get the token:
let kClientID = "xxxxxx-xxxx-xxxx-xxxx-xxxxxx"
let kGraphEndpoint = "https://graph.microsoft.com/"
let kAuthority = "https://login.microsoftonline.com/xxxxxx-xxxx-xxxx-xxxx-xxxxxx"
let kScopes: [String] = ["user.read"]
let bundleID = Bundle.main.bundleIdentifier ?? "com.default.test"
let kRedirectUri = "msauth.\(bundleID)://auth"
Aquire Token code:
if let applicationContext = self.applicationContext, let webViewParameters = self.webViewParamaters {
let parameters = MSALInteractiveTokenParameters(scopes: kScopes, webviewParameters: webViewParameters)
parameters.promptType = .selectAccount
applicationContext.acquireToken(with: parameters) { (result, error) in
if let error = error {
self.log(text: "Could not acquire token: \(error)")
return
}
guard let result = result else {
self.log(text: "Could not acquire token: No result returned")
return
}
self.token = result.accessToken
// calling graph API to get the name and user id ( Success )
// sending this token to our API backend ( Failure 401 )
}
}
Problem:
When Graph API is called from frontend iOS application after getting token, it is working, and when we are sending same token to backend then it is not working getting 401 error. The same token is not valid for backend application, but this was working Earlier when we were using ADAL Library in iOS application.
Is it because of Redirect URI ?? in ADAL we were using API endpoint as redirect and now we are using "msauth.\(bundleID)://auth" this format.
Please help.
Token you may have received is only for MS Graph API, not your API ,As front-end acquires access token for Microsoft Graph API.
In your front-end you need to specify scopes for your backend API as mentioned by #juunas.
When your application needs to request an access token with specific permissions for a resource API, pass the scopes containing the app ID URI of the API in the format like this-> app ID URI/scope
From MSdocs reference, Some example scope values for different resources:
Microsoft Graph API: https://graph.microsoft.com/User.Read
Custom web API:api://11111111-1111-1111-1111-111111111111/api.read
To set scopes in portal ,
go to the app registration of your API in Azure AD > Expose an API> add a scope.
Azure AD should then give you a token that is meant for your API.
SO reference

Azure API Management invalid access token

I am trying to generate an access token for my API Management. I have enabled the Management REST API in the Azure portal and then I tried generating the token using both options- through the portal as well as programmatically. Both the options doesn't work and I get error response:
"{\"error\":{\"code\":\"InvalidAuthenticationToken\",\"message\":\"The
access token is invalid.\"}}"
REST API which I am trying to access: https://management.azure.com/subscriptions/{subscriptionID}/resourceGroups/{resourceGroupName}/providers/Microsoft.ApiManagement/service/{serviceName}/reports//byApi?%24filter=timestamp%20ge%20datetime%272019-08-01T00%3A00%3A00%27%20and%20timestamp%20le%20datetime%272019-08-09T00%3A00%3A00%27&api-version=2019-01-01
My code:
public string GetAnalytics()
{
string data = String.Empty;
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(_url);
string token = GetToken();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
data = client.GetAsync(_url).Result.Content.ReadAsStringAsync().Result;
}
return data;
}
private string GetToken()
{
var id = "integration";
var key = _key;
var expiry = DateTime.UtcNow.AddDays(10);
string token = String.Empty;
using (var encoder = new HMACSHA512(Encoding.UTF8.GetBytes(key)))
{
var dataToSign = id + "\n" + expiry.ToString("O", CultureInfo.InvariantCulture);
var hash = encoder.ComputeHash(Encoding.UTF8.GetBytes(dataToSign));
var signature = Convert.ToBase64String(hash);
token = string.Format("SharedAccessSignature uid={0}&ex={1:o}&sn={2}", id, expiry, signature);
}
return token;
}
References:
https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/azure-api-management-rest-api-authentication
https://learn.microsoft.com/en-us/rest/api/apimanagement/2019-01-01/reports/listbyapi
Any help with this please?
The API which you are using is the Azure API and not Azure APIM API. The Shared Access Signature will work only with the Azure APIM API and not with Azure API. In order for Shared Access Signature to work use the API with base url - https://{servicename}.management.azure-api.net
For the Azure API to work, use OAuth2 credentials. Setup a client as mentioned - https://learn.microsoft.com/en-us/rest/api/azure/#register-your-client-application-with-azure-ad
The URL you used is azure rest api endpoint. If you want to call azure rest api, you need to get azure ad access token. However, the token you get is SAS token. It just can be used to call azure API management rest api. For more details, please refer to
https://learn.microsoft.com/en-us/rest/api/apimanagement/apimanagementrest/api-management-rest
https://learn.microsoft.com/en-us/rest/api/azure/

Unable to access Graph API when using Azure AD B2C

I have a test user ID as test#gollahalliauth.onmicrosoft.com (without global admin rights) and I am trying to access Graph API for Azure AD.
Try 1 (Success)
I used Azure AD Graph Explorer, logged in with test#gollahalliauth.onmicrosoft.com and using the API https://graph.windows.net/gollahalliauth.onmicrosoft.com/users/test#gollahalliauth.onmicrosoft.com to get the contents. I was able to do this without any issue.
Try 2 (Fail)
I wrote a Go program with profile edit policy
import (
"crypto/rand"
"encoding/base64"
"fmt"
"golang.org/x/oauth2"
"os"
)
const AuthDomainName string = "https://gollahalliauth.b2clogin.com/gollahalliauth.onmicrosoft.com/oauth2/v2.0"
func main() {
conf := &oauth2.Config{
ClientID: os.Getenv("clientID"),
ClientSecret: os.Getenv("clientSecret"),
RedirectURL: "http://localhost:8080/callback",
Scopes: append([]string{"openid", "profile"}),
Endpoint: oauth2.Endpoint{
AuthURL: AuthDomainName + "/authorize?p=b2c_1_gollahalli_edit",
TokenURL: AuthDomainName + "/token?p=b2c_1_gollahalli_edit",
},
}
// Generate random state
b := make([]byte, 32)
rand.Read(b)
state := base64.StdEncoding.EncodeToString(b)
parms := oauth2.SetAuthURLParam("response_type", "id_token")
url := conf.AuthCodeURL(state, parms)
fmt.Println("AUth URL:",url)
}
This creates an auth URL to get the token. I used the id_token to access the graph API using Authorization: Barer id_token and I get an error as
{
"odata.error": {
"code": "Authentication_ExpiredToken",
"message": {
"lang": "en",
"value": "Your access token has expired. Please renew it before submitting the request."
}
}
}
Try 3 (Fail)
I tried adding User.Read in Azure AD B2C > Applications >
<application name> > Published scopes and used the full scope URL and now I get an error as Error: AADB2C90205: This application does not have sufficient permissions against this web resource to perform the operation.
I am not sure what the problem is here. Any idea as to how to get over this?
The AAD B2C is a specialized instance of AAD. You may consider it as a AAD tenant with some B2C extensions. Note: this is a separate tenant from your organization's main AAD tenant in which you've already created the B2C directory/feature!
You can access the B2C records through the AAD Graph API, in 2 steps:
acquire an AAD Graph token by providing the ClientID and ClientSecret to the AAD endpoint (e.g. https://login.microsoftonline.com/yourtenant.onmicrosoft.com).
connect to the AAD Graph REST endpoint (e.g. https://graph.windows.net/yourtenant.onmicrosoft.com/users?api-version=1.6) with the desired method (GET/POST/PATCH/DELETE), passing it the token obtained in step 1 in the Authentication header of the request.
The best example is probably the user migration tool provided by MS. The AAD B2C configuration is covered here and the sample code can be downloaded from the documentation page or directly from the Github project.
You should take a look to the SendGraphPostRequest method and its friends in B2CGraphClient.cs. The code uses ADAL to get the AAD Graph token, but you can also obtain it directly with REST requests. A simplified version in C# (you'll have to translate it yourself to GO, and maybe replace ADAL if it's not available in GO):
// NOTE: This client uses ADAL v2, not ADAL v4
AuthenticationResult result = aadAuthContext.AcquireToken(Globals.aadGraphResourceId, aadCredential);
HttpClient http = new HttpClient();
string url = Globals.aadGraphEndpoint + tenant + api + "?" + Globals.aadGraphVersion;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
request.Content = new StringContent(json, Encoding.UTF8, "application/json");
HttpResponseMessage response = await http.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
string error = await response.Content.ReadAsStringAsync();
object formatted = JsonConvert.DeserializeObject(error);
throw new WebException("Error Calling the Graph API: \n" + JsonConvert.SerializeObject(formatted, Formatting.Indented));
}

Is it possible to operate OneNote with azure daemon App?

In order to operate OneNote with azure's daemon app,
I created a new ClientID, acquired the Access Token by user authentication with that ClientID, and realized access to the OneNote API using it.
However, instead of user authentication, Access token is acquired by ClientID and certificate, and access to OneNote API using it is refused.(401 Unauthorized)
How can I operate OneNote from azure dameon App?
The way I tried
The AccessToken creation by the certificate was implemented with reference to the following.
https://azure.microsoft.com/ja-jp/resources/samples/active-directory-dotnet-daemon-certificate-credential/
Specific AccessToken acquisition codes are as follows,
public async Task AuthWithCertAsync(string tenant, string clientID, string certName)
{
var authority = $"{aadInstance}{tenant}";
var authContext = new AuthenticationContext(authority);
//refer: above URL
ClientAssertionCertificate certCred = GetCertificate(clientID, certName);
if (certCred == null) {return false;}
//"https://graph.microsoft.com/";
var graphResult = await authContext.AcquireTokenAsync(graphResourceID, certCred);
graphToken = graphResult.AccessToken;
//"https://www.onenote.com/";
var onenoteResult = await authContext.AcquireTokenAsync(onenoteResourceID, certCred);
onenoteToken = onenoteResult.AccessToken;
}
With this graphToken, access to the Graph API succeeds.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {graphToken}");
//e.g. "https://graph.microsoft.com/v1.0/groups", "https://graph.microsoft.com/v1.0/users"
var response = await client.GetStringAsync(url);
...
}
However, if the target URL is an API on onenote, it fails.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {graphToken}");
//e.g:"https://graph.microsoft.com/beta/users/{userID}/notes/notebooks"
// Occured HttpRequestException(401 Unauthorized)
var response = await client.GetStringAsync(url);
...
}
This request returns HTTP 401 Unauthorized status.
Also when accessing OneNote API on onenoteToken failed.
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {onenoteToken}");
//e.g.:"https://www.onenote.com/api/v1.0/users/{userID}/notes/notebooks"
var response = await client.GetStringAsync(url);
return response;
}
This request also returns HTTP 401 Unauthorized status.
The application setting in Azure Active Directory:
Type:
WEB APPLICATION AND/OR WEB API
Multi Tenant:
ON
permissions to other applications:
Graph, OneNote, Active Directory, SharePoint :Application Permissions all checked.
In the admin account of the target tenant, the following admin consent URL is accessed and accepted.
https://login.microsoftonline.com/common/adminconsent?client_id={clientID}&state={state}&redirect_uri={redirectUrl}
Update
According to the answer of https://stackoverflow.com/a/41890179/1411521,
I understood that there is no way to access OneNote by daemon App with the current Graph API. (at 2017-1-31)
However, Application Permission of OneNote API can set as follows.
View and modify notes for all users
View notes for all users
Despite the fact that they are valid, what causes the authentication error (401 Unauthorized) with the following code?
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {onenoteToken}");
//e.g.:"https://www.onenote.com/api/v1.0/users/{userID}/notes/notebooks"
var response = await client.GetStringAsync(url); // Occured HttpRequestException(401 Unauthorized)
...
}
You were mixing the Microsoft Graph and OneNote API.
The token you were acquire is for the Microsoft Graph REST, and you can manipulate the OnenNote through Microsoft Graph REST which in beta version by following the document here(beta reference->OneNote).
And if you want to use the OneNoe API, you can refer the document here for the authentication.
Update
To list the notebooks, we need permissions like Notes.Read, Notes.ReadWrite.CreatedByApp, Notes.ReadWrite, Notes.Read.All, or Notes.ReadWrite.All. However there is no such kinds of permission for the Client Credential flow for Microsoft Graph.
If you want the Microsoft Graph to support the Client Credential flow to manipulate the OneNote, you can submit the feedback from here.
This problem was solved today(2017-2-10).
The OneNote REST API now supports application-level permissions
OneNote authentication and Azure AD application permissions

Resources