Google Dialogflow V2 - Authentication - access token - dialogflow-es

I am using Google Dialogflow V2 API for chatbot design.
In order to pass Authorisation header in request parameter of Detect Intent API, I need the access token dynamically.
As per the official documentation, we have to install gcloud on a machine which I don't want.
I want to implement same via java but I am getting an error.
I have followed the link: https://github.com/googleapis/google-auth-library-java and set Environment Variable for GOOGLE_APPLICATION_CREDENTIALS
I have used below Code snippet :
String GoogleCredentialsEnv = System.getenv("GOOGLE_APPLICATION_CREDENTIALS");
GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("/Users/Downloads/testingbot-29671-d9229dd1e3f9.json"));
credentials.createScoped(Arrays.asList("https://www.googleapis.com/auth/dialogflow"));
credentials.refreshIfExpired();
AccessToken token = credentials.getAccessToken();
//AccessToken token = credentials.refreshAccessToken();
System.out.println("Token is " + token);
but most of the times I am getting below error
Exception in thread "main" java.io.IOException: Scopes not configured for service account. Scoped should be specified by calling createScoped or passing scopes to constructor.
at com.google.auth.oauth2.ServiceAccountCredentials.refreshAccessToken(ServiceAccountCredentials.java:363)
at com.google.auth.oauth2.OAuth2Credentials.refresh(OAuth2Credentials.java:181)
at com.google.auth.oauth2.OAuth2Credentials.refreshIfExpired(OAuth2Credentials.java:198)
at com.Model.demo.getBotResponse(demo.java:60)
at com.Model.demo.main(demo.java:40)

Try:
GoogleCredentials credentials = GoogleCredentials.fromStream(new FileInputStream("/Users/Downloads/testingbot-29671-d9229dd1e3f9.json"));
if (credentials.createScopedRequired()) {
credentials = credentials.createScoped(Collections.singletonList("https://www.googleapis.com/auth/dialogflow"));
}
credentials.refreshIfExpired();
AccessToken token = credentials.getAccessToken();
System.out.println(token.getTokenValue());

Make sure the service account you're using has been created for "Dialogflow Integrations".

Related

Azure Active Directory v2 - Get Custom Scope Token

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.

google.auth.exceptions.RefreshError: Invalid Client

I am working on a project to let a client authorize their google ads account, and then use those authorized credentials to download data on their behalf. I have a webapp that successfully Authorizes the app to do things on the clients behalf. This generates an access code that I then trade for two credentials, an access token and a refresh token. This refresh token then gets passed to a database, where a separate app attempts to query the googleAds API.
It is my understanding that the Google Oauth engine only needs the refresh token.
I am trying to authorize by use of load_from_dict() or load_from_env() methods of the GoogleAdsClient class. Both yield the same error: google.auth.exceptions.RefreshError: ('invalid_client: Unauthorized', {'error': 'invalid_client', 'error_description': 'Unauthorized'})
I have verified the developer_token, client_id, and client_secret are all accurate to what is in the API console. I have also verified the refresh_token is being passed correctly to the credential dict.
I am really at a loss on where to go from here. I have read many stack overflow threads with similar titles, and yet I am still stuck at the same place.
Here are some relevant links.
Google Ads API configuration
Google Identity and Server side web apps
Google's example of an API call
Relevant code
class GoogleAds:
def __init__(self):
self.scope = ['https://www.googleapis.com/auth/adwords']
self.client_id = os.getenv('GOOGLE_ADS_CLIENT_ID')
self.client_secret = os.getenv('GOOGLE_ADS_CLIENT_SECRET')
self.developer_token = os.getenv('GOOGLE_ADS_DEVELOPER_TOKEN')
self.refresh_token = os.getenv('GOOGLE_ADS_REFRESH_TOKEN')
def authorize(self):
credentials = {
"developer_token": self.developer_token,
"refresh_token": self.refresh_token,
"client_id": self.client_id,
"client_secret": self.client_secret,
"use_proto_plus":"True",
"grant_type": "refresh_token",
}
print(credentials)
googleads_client = GoogleAdsClient.load_from_dict(credentials)
service = googleads_client.get_service("GoogleAdsService")
request = googleads_client.get_type("SearchGoogleAdsRequest")
return service, request
'error': 'invalid_client', 'error_description': 'Unauthorized' Can be a very hard error to debug. It can mean a number of things.
Currently it Implies to me that the user has not authorized this client.
First ensure that the refresh token has not expired. Second ensure that the client id and client secrete used to create the refresh token are the same one that you are using to request a new access token.
oauth2#expiration
I ended up refreshing the Client_Secret in the google API client and that seemed to have gotten me through.
Q: It is outside the scope of this question, but is it possible to get that value from the authorization step?
A: You can get the customer IDs you have access to with the client.get_service("CustomerService") method. There is also a way to get account hierarchy. I will probably be using (Frankensteining) that to move forward

Service to service authentication in Azure without ADAL

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.

Authenticate App Services backend using Microsoft Graph token?

Edit:
I have added the "id_token" but still get an "Unauthorized" response.
Here is my login code:
PublicClientApplication myApp = new PublicClientApplication("My-AppID-From-App-Registration-Portal");
string[] scopes = new string[] { "User.Read" };
AuthenticationResult authenticationResult = await myApp.AcquireTokenAsync(scopes).ConfigureAwait(false);
JObject payload = new JObject();
payload["access_token"] = authenticationResult.AccessToken;
payload["id_token"] = authenticationResult.IdToken;
user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, payload);
Original Post:
Is it possible to authenticate to a App Services backend using the token retrieved from Microsoft Graph?
I have already tried using this token and calling LoginAsync() with AzureActiveDirectory as the provider, this doesn't work.
JObject payload = new JObject();
payload["access_token"] = GraphAuthenticationHelper.TokenForUser;
user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
Is this possible?
UPDATE: In my original answer, I said you cannot do this. But in reality, you can do this but it's a dangerous thing to do since anyone with a valid Microsoft Graph token could theoretically access your APIs. Before I walk you down that path, let me describe the "right" way to access the Microsoft Graph on behalf of your end user.
The right way to do this is to use the on-behalf-of flow in the mobile backend code to exchange the user's ID token for a Microsoft Graph token. The flow looks like the following:
Client initiates a login with AAD using MSAL and sets the resource to the mobile backend (not the Graph). The result should be a set of tokens.
Client uses the mobile SDK to do a login with BOTH the access_token AND the id_token from #1.
Example code:
JObject payload = new JObject();
payload["access_token"] = {access_token.from.msal};
payload["id_token"] = {id_token.from.msal};
var user = await MobileService.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
payload);
The backend code exchanges the user's ID token (from the x-ms-token-aad-id-token request header) for a graph token. This token exchange is known as "on-behalf-of" and is documented here. I think this can be done using ADAL or MSAL libraries, but I wasn't able to find documentation. It's also simple enough that you could implement the HTTP protocol directly without too much trouble.
The backend uses the newly acquired MS Graph token and makes the graph API call.
You can also cache the graph token that you acquire on the backend so that each API call doesn't require more AAD API calls to do token exchange.
I think no ,please refer to document : https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-dotnet-how-to-use-client-library#a-nameauthenticationaauthenticate-users
Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.
The audience of the access token should be the client ID for your mobile app backend . So if resource is https://graph.microsoft.com/(aud claim in access token) , then Client-managed authentication won't work .

How to get accesstoken via code using Azure Oauth 2.0

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/";

Resources