The situation:
I have successfully get the code parameter from returning url via
https://login.microsoftonline.com/{tenant}/oauth2/authorize?client_id=XXXX-XXXX-XXXX&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%2F&response_mode=query
now I need to get accesstoken for getting user info, I post parameters to this url:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
2 ways I had tried:
1.
var nvc = new NameValueCollection();
nvc.Add("grant_type", "authorization_code");
nvc.Add("client_id", "xxx-xxxx-xxxx");
nvc.Add("code", code.Value);
nvc.Add("redirect_uri", "http://localhost/");
nvc.Add("client_secret", "XXXXXXXXXXXXXX=");
nvc.Add("resource", "https://graph.microsoft.com/");
nvc.Add("scope", "email");
client.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
var response = Encoding.UTF8.GetString(client.UploadValues(url,"POST", nvc));
2.
var xx = new StringContent("grant_type=authorization_code"+
"&client_id=xxx-xxxx-xxxx" +
"&code=codeXXXXXXXX" +
...
"&resource=https://graph.microsoft.com/",
Encoding.UTF8,
"application/x-www-form-urlencoded");
client.PostAsync(url,xx);
All of them returned a error 400, and I got a error message :
{
"error":"invalid_request",
"error_description":"AADSTS90014: The request body must contain the following parameter: 'grant_type'.\r\nTrace ID: 207dd940-78ff-46ba-bec0-00821c850f00\r\nCorrelation ID: 803afff4-3917-4030-a19b-b5629e1faf97\r\nTimestamp: 2017-05-19 02:51:19Z",
"error_codes":[
90014
],
"timestamp":"2017-05-19 02:51:19Z",
"trace_id":"207dd940-78ff-46ba-bec0-00821c850f00",
"correlation_id":"803afff4-3917-4030-a19b-b5629e1faf97"
}
First, you were mixing the Azure AD endpoint with Azure AD V2.0 endpoint. And from the error message, you didn't specify the grant_type parameter. Please make sure to send this parameter in the request. And the send request should also return the different error like The 'resource' request parameter is not supported.
If you were using the Azure AD endpoint, you can refer this link for the request to acquire the token. And for the Azure AD V2.0 you can refer v2.0 Protocols - OAuth 2.0 Authorization Code Flow.
If you still have the problem, please share the exact code you were developing and let us know which endpoint you were developing.
As Fei Xue mentioned, you should not mix the endpoints. You can use:
https://login.microsoftonline.com/{tenant}/oauth2/token
to get the access token.
Thank you guys.
I finally fellow this article and successfully got the access token, logged user info. The point to successfully get the access token is to give right parameters, the following block shows an example of correct parameters.
public static string clientId = "9fb8ee69-xxxx-xxxx-xxxx-xxxxxxx";
public static string authority = "https://login.windows.net/9c80d42c-yyyy-yyyy-yyyy-yyyyyyyyy/oauth2/authorize";
public static string returnUri = "https://kuozuinotification.azurewebsites.net/.auth/login/aad/callback"; << my issue caused by here
private const string resource = "https://graph.windows.net/";
Related
I am learning about generating a token for an OAuth service and it will be used in a chatbot. When I use the following code displayed below, I can get a default scope Graph Token successfully, and this token is valid for MS Graph API calls. Now, what I am trying to achieve is generating a custom scope token in a similar way in order to call an external service(Not MS Graph API). This token needs to have a custom scope. I tried to change the dictionary parameter "scope" to the name of my scope configured for a chatbot in Azure but it fails:
private async Task<string> GetGraphTokenAsync()
{
var dict = new Dictionary<string, string>();
dict.Add("client_id", _graphTokenSettings.ClientId);
dict.Add("client_secret", _graphTokenSettings.ClientSecret);
dict.Add("scope", "https://graph.microsoft.com/.default");
dict.Add("grant_type", "client_credentials");
string gUrl = $"https://login.microsoftonline.com/{_graphTokenSettings.Tenant}/oauth2/v2.0/token";
var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, gUrl) { Content = new FormUrlEncodedContent(dict) };
var httpResponseFromService = await client.SendAsync(req);
httpResponseFromService.EnsureSuccessStatusCode();
if (httpResponseFromService.Content is object
&& httpResponseFromService.Content.Headers.ContentType.MediaType == "application/json")
{
string stringFromservice = await httpResponseFromService.Content.ReadAsStringAsync();
JObject tokenresponse = JsonConvert.DeserializeObject<JObject>(stringFromservice);
string token = tokenresponse["access_token"].Value<string>();
return token;
}
else
{
_logger.LogError($"Cannot get token for Microsoft Graph. httpResponseFromService.Content:{httpResponseFromService.Content}" );
throw new Exception("Cannot get token for Microsoft Graph.");
}
}
The provider configuration in my Bot is the following, is it using as Service Provider: Azure Active Directory v2:
This is an example of a custom token generated with an OAuth tool (tenant id and other values changed to just illustrate the data, but all these values match and are correct when working with them), it is calling to the same url "login.microsoftonline.com" that I am trying to call to generate the custom scope token:
This generated custom scope token works. It has been configured at my Tenant Azure level as "api://botid-GUID/access_as_user" but I would like to generate it via http client as my code example. Would you know how can I get a token using this custom scope with a similar httpClient approach? It seems the scope parameter that I am sending ("api://botid-GUID/access_as_user") is not correct for client_credentials grant type call:
Default scope:
dict.Add("client_id", _graphTokenSettings.ClientId);
dict.Add("client_secret", _graphTokenSettings.ClientSecret);
dict.Add("scope", "https://graph.microsoft.com/.default");
dict.Add("grant_type", "client_credentials");
Replaced by:
dict.Add("client_id", _graphTokenSettings.ClientId);
dict.Add("client_secret", _graphTokenSettings.ClientSecret);
dict.Add("scope", "api://botid-GUID/access_as_user");
dict.Add("grant_type", "client_credentials");
Any help will be very appreciated.
I tried to reproduce the same in my environment and got below results:
I have one Azure AD application where I created one custom scope by exposing the API like below:
I registered another application named ClientApp and added above custom scope by granting consent like below:
In my Azure Bot, I added one connection setting with Service Provider as Azure Active Directory v2 like below:
When I ran Test connection, I got the token successfully like below:
When I decoded the above token, I got claims with scope as below:
When you create custom scope by exposing an API, it comes under Delegated permissions that involves user interaction like below:
Note that, client credential flow only works with Application
permissions that does not involve user interaction.
You need to create App role instead of exposing the API in the application with different unique value access-as-user like below:
You can add above App role to your client application that comes under Application permissions and make sure to grant consent as below:
In addition to that, client credentials grant type supports scope that ends with only /.default while using v2 endpoint. Otherwise, it will throw exception like below:
To resolve the above error, you need to replace scope with /.default at end like below while generating token:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
client_id:appID
grant_type:client_credentials
client_secret:secret
scope: api://87xxxa-6xex-4dxa-9xaf-b1dxxxx9819/.default
Response:
When I decoded the above token, I got claims with roles as below:
Note that, decoded token contains Application permissions in roles claim whereas Delegated permissions in scp claim.
In your scenario, if you want to use custom scope with client credentials grant type, you need to create App role with unique value that comes under Application permissions.
Make sure to change scope with /.default at end.
I have been using MSAL in my React app for some time with success. One of the tokens that my app requests is for scope 'https://management.core.windows.net/user_impersonation'. I have a nodeJS server that I want to push that token acquisition to so I installed msal-node (1.12.1) and tried using the OBO flow:
const pca = new msal.ConfidentialClientApplication({
auth: {
clientId: settings.config.azure.clientId,
clientSecret: settings.config.azure.clientSecret,
authority: "https://login.microsoftonline.com/<tenantid>",
knownAuthorities: ["https://login.microsoftonline.com/<tenantid>"],
}
});
const request = {
scopes: ['https://management.core.windows.net//user_impersonation'],
oboAssertion: <token_extracted_from_auth_header>
}
const response = await pca.acquireTokenOnBehalfOf(request);
return response.accessToken;
However the above code results in the following error:
ClientAuthError: endpoints_resolution_error: Error: could not resolve endpoints. Please check network and try again. Detail: ClientAuthError: openid_config_error: Could not retrieve endpoints. Check your authority and verify the .well-known/openid-configuration endpoint returns the required endpoints. Attempted to retrieve endpoints from: https://login.microsoftonline.com/tenantid/v2.0/.well-known/openid-configuration
If I visit the URL it complains about I do get back some metadata so not really sure why it is failing.
Anybody have a suggestion?
Also in regards to OBO flow:
For my nodeJS app I have added that permission to the required API list
I presume the oboAssertion field is the token that is passed to my nodeJS app by the client? I simply extracted it from the Auth header
The actual error message there means that the URL that we are trying to contact is wrong. And it is wrong https://login.microsoftonline.com/tenantid/v2.0/.well-known/openid-configuration returns an error.
A coorrect one is: https://login.microsoftonline.com/19d5f71f-6c9a-4e7f-b629-2b0c38f2b167/v2.0/.well-known/openid-configuration
Notice how I used an actual teanant_id there. You can get yours from the Azure Portal - it's the "directory id"
If your web api is single tenant, i.e. it is only meant for the people in 1 organization, then the is the tenant id of that organization. It is also known as "directory id". You get it from the Azure Portal.
However, if your api is multi-tenant, i.e. it's a bit more complicated, and the "correct" answer is to use the tenant id of the incoming assertion. It's the tid claim in it.
I have an Azure Hybrid Connection that's supposed to connect to some on-prem service and expose it to my app services. However, the setup is failing somewhere, and I'm trying to narrow down exactly what the problem is.
As part of this process (and because it would be useful to me in the future) I'm trying to make a call to the underlying on-prem service using SoapUI, but the initial GET request that's supposed to give me the WSDL is instead giving me an authentication error:
GET https://my-relay.servicebus.windows.net/my-endpoint/my-service.svc?wsdl
{
"error": {
"code": "TokenMissingOrInvalid",
"message": "MissingToken: Relay security token is required. TrackingId:b58c004c-e0e6-4dd0-a233-e0d304795e4e_G21, SystemTracker:my-relay.servicebus.windows.net:my-endpoint/my-service.svc, Timestamp:2019-03-05T10:17:26"
}
}
From where do I get the Relay security token, and how do I tell SoapUI about it?
This guide may give you the answers you need.
https://www.soapui.org/soap-and-wsdl/authenticating-soap-requests.html
I suspect your normal app automatically accesses the webservice as the current user on the current system. If so, I believe you should look at the NTLM authentication.
You need to create a security token and pass it in the header of your request.
Something like this:
var tokenProvider = TokenProvider.CreateSharedAccessSignatureTokenProvider(KeyName, Key);
var uri = new Uri(string.Format("https://{0}/{1}", RelayNamespace, ConnectionName));
var token = (await tokenProvider.GetTokenAsync(uri.AbsoluteUri, TimeSpan.FromHours(1))).TokenString;
var client = new HttpClient();
var request = new HttpRequestMessage()
{
RequestUri = uri,
Method = HttpMethod.Get,
};
request.Headers.Add("ServiceBusAuthorization", token);
var response = await client.SendAsync(request);
For SOAPUI, you would add the resultant token value in a header named "ServiceBusAuthorization."
in my Xamarin.forms project, I use ADAL (Microsoft.IdentityModel.Clients.ActiveDirectory) to authenticate on the Azure portal (Auth 1.0 endpoint). That part work great, but I need to get the security group of the user. So I use this code and passing the token received with ADAL:
HttpClient client = new HttpClient();
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me/memberOf");
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
HttpResponseMessage response = await client.SendAsync(message);
string responseString = await response.Content.ReadAsStringAsync();
I always got StatusCode: 401, ReasonPhrase: 'Unauthorized'.
In my azure AD app registration, I add the Graph API and these permissions:
I think I miss something. Any idea?
----- EDIT 1 ---
Here my payload. I changed it for a picture for lisibility. I don't know how to post json here
----- EDIT 2 ---
OH! I see. I think I need to understand more the Azure Login process. For now I follow an example of ADAL and Azure that let my log and use some function in my backend. So the login process use:
var authContext = new AuthenticationContext(authority); var authResult = await authContext.AcquireTokenAsync(resource, clientId, uri, platformParams);
Where authority = https://login.microsoftonline.com/mysite.com, ResourceID is my backend app ID and clientID is my native app ID. So Shawn is correct, I do not use the Graph.microsoft.com to get the token. Do we have another way to achieve all that? The need of using Graph is only to get the AD group the user has to adjust permission inside the app.
You are correct in your comment. If you add a new permission to your application, you must ask the user to re-consent to the app.
You can force re-consent through ADAL by setting the PromptBehavior to Always:
platformParams.PromptBehavior = PromptBehavior.Always
Or you can simply modify your Login URL to force it, by adding the query string:
&prompt=consent
In terms of building an app to help overcome this problem, if you think your app will be changing permissions after release, you can integrate logic which detects Unauthorized, and then sends the user to re-consent.
Another option is for your app to track changes which may require a new consent prompt, and detect when the user uses this new version of your application the first time, and asks them to consent.
In our new App Model V2, we support the concept of Incremental and Dynamic Consent, which should get rid of this problem all together for you.
I have set up a new web app to be able to use the Oauth2 V2 authorization endpoint. I defined the app in https://apps.dev.microsoft.com/
If I want to obtain a new authorization token, following instructions in
https://blogs.msdn.microsoft.com/richard_dizeregas_blog/2015/09/04/working-with-the-converged-azure-ad-v2-app-model/
I get the following error on the login page:
Sorry, but we’re having trouble signing you in.
We received a bad request.
Additional technical information:
Correlation ID: eb9c2331-32bd-45a9-90d1-e9105f0bfa87
Timestamp: 2016-05-22 18:10:48Z
AADSTS70011: The provided value for the input parameter 'scope' is not valid. The scope https://graph.microsoft.com/Calendar.Read is not valid.
The scope is taken from an example in :
https://github.com/Azure/azure-content/blob/master/articles/active-directory/active-directory-v2-scopes.md
So I imagine it is a valid scope.
In v1 of the OAuth2 protocol, it was necessary to configure access to APIs in the Azure AD of my tenant, prior to using them. So I attempted to do so for the new application.
Attempting to do so, the Azure application management reports an error:
{
"message":"This request has a value that is not valid.",
"ErrorMessage":"This request has a value that is not valid.",
"httpStatusCode":"InternalServerError","operationTrackingId":null,"stackTrace":null,"Padding":null
}
What is missing to be able to use the new authorization endpoint ?
The documentation contains a typo if states calendar.read. It must be calendars.read:
private static string[] scopes = {
"https://graph.microsoft.com/calendars.readwrite"};
Uri authUri = await authContext.GetAuthorizationRequestUrlAsync(scopes, additionalScopes, clientId, redirectUri, UserIdentifier.AnyUser, null);