How to authenticate Google Cloud Vision via HTTP Request - c#

I am trying to use Google Cloud Vision via a rest http request using c#. As described here, I tried to authenticate with the api key as a parameter:
string uri = "https://vision.googleapis.com/v1/images:annotate?key=" + API_KEY;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
[...]
response = await client.SendAsync(request);
However, I always get a 403 PERMISSION DENIED Code:
Cloud Vision API has not been used in project XXXXX before or it is
disabled. Enable it by visiting
https://console.developers.google.com/apis/api/vision.googleapis.com/overview?project=XXXXXXX
then retry. If you enabled this API recently, wait a few minutes for
the action to propagate to our systems and retry.
Of course I checked, The API is activated, enabled and there are no API restrictions:
Since there seemed to exist some problems with that authentication method, especially with cloud vision, I tried authentication via an access token of a service account that I created. I gave the service account full access just to be sure that there is no issue with the rights of the service account:
string uri = "https://vision.googleapis.com/v1/images:annotate";
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Headers.Add("Authorization", "Bearer " + ACCESS_TOKEN);
response = await client.SendAsync(request);
Still, same error message. Same goes with curl:
curl -k -X POST -H "Content-Type:application/json"
-d "#{REQUEST_AS_JSON_OR_PATH_TO_JSON-FILE}"
https://vision.googleapis.com/v1/images:annotate?key={API_KEY}
What am I missing out?

Turned out it is NOT sufficient to activate an API key (even when its without any restrictions), but one needs to actively go to the Google console menu -> APIs and Services -> Library -> Search for "Cloud Vision" -> klick on the Cloud vision API and activate it. It takes a couple of minutes. The status of this is not shown in your dashboard.
Then, you can simply authenticate with the api key as URL parameter.

Related

Postman omits original Oauth2 authentication handshake code

I have a Postman request sent by a partner/client that requires Oauth2 to hit their web service endpoint. Here is the authorization section:
I need to re-create this request in C#. Should be easy, just click on Code link, and grab the sample code in C# - RestSharp. Two problems:
1- When I execute this in Postman, I get The SAML2 token is not valid because its validity period has ended. So then I click the Get New Access Token button, and I get challenged for credentials:
(With the Client ID and Client Secret, shouldn't I be able to bypass this challenge?)
2- Ultimately I need to be able to run this request in my c# app. When I click the Code link in Postman it gives a nice C# example, but the problem with the sample code generated by Postman is that it assumes the bearer token has already been obtained, and just plops that into the source sample. But the obtaining of the bearer token is a very important piece that Postman omits. Here is my specific example, copied from Postman:
var client = new RestClient("https://myurl-here:7148/foo/ODataV4/WS3_stuff?Company='abc123'");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/json");
request.AddHeader("Authorization", "Bearer ey...massive-string...Zb");
request.AddHeader("Cookie", "ApplicationGatewayAffinity=ab721.more.d6c1a341bc; ApplicationGatewayAffinityCORS=ab..more...bc");
request.AddParameter("application/json", "{a-bunch-of-json-here}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
What are the pieces I'm missing? How can I get the C# equivalent of whatever Postman is doing to obtain the bearer and those Cookie values? And I thought there's a way for applications to achieve all the Oauth handshaking without sending the user to that MS window as in screenshot above - but how is it done?
ClientID and ClientSecret are there to identify your application not the user. They are sent to the Authorization Server so that the server knows that it can issue access tokens to this application. User authentication is another thing. You will always have to authenticate the user in order to get an access token which allows to access that user's data.
Unless you only need to authenticate your application and want to access data which does not belong to any user. In that case you need a client credentials flow, an OAuth flow which allows your application to get an access token.
Postman is a tool for making calls to APIs, it just generates the code which enables you to make the API call. Have a look at some C# OAuth clients (or maybe something for your framework, if you're using one). Those clients will enable you to easily generate new access tokens. Still, if you need a user's access token remember that you will need your users to open a browser. If you're developing a backend app, which does not serve any pages then you can have a look at the OAuth device flow, which enables you to authenticate users on a different device than your app runs.
As for the cookies - make sure whether you really need them. If you're calling an API chances are that those cookies are not required to make the request.

Google Identity Toolkit API in C#

Some Background Information
I am building a game in Unity, using C#. Since I am using Firebase and the ready-made Unity Firebase SDK won't work in my case1, I have resorted to interfacing with Firebase's REST API through C#'s HttpClient class (which comes with System.Net.Http).
I am currently struggling with the Firebase Authentication APIs. For those unfamiliar with OAuth APIs, when I call the Sign Up or Sign In endpoints, I get both an ID Token and a Refresh Token. ID Tokens expire; refresh tokens do not. When the ID Token expires, I must call another endpoint, called the Token Exchange, to get a new ID token using my refresh token.
1 There is a rumored Google Identity Toolkit C# library, but the first result returned by that search I found it by leads to a 404 error instead of documentation. 😢
What Does Work
Following the Firebase guides and the underlying Identity Toolkit guides, I have been successful so far interfacing the token exchange endpoint with a cURL command from bash:
curl 'https://securetoken.googleapis.com/v1/token?key=[MY FIREBASE API KEY]' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'grant_type=refresh_token&refresh_token=[MY REFRESH TOKEN]'
of course, I replace [MY FIREBASE API KEY] with my Firebase Web API key, and [MY REFRESH TOKEN] with the refresh token returned from the sign in/sign up endpoints.
However, despite my many attempts, I have not been able to replicate this cURL command in C#!
My Failed Attempts
1.
Here's my original code that didn't work.
public async Task<bool> ExchangeToken()
{
FormUrlEncodedContent body = new FormUrlEncodedContent(new Dictionary<string, string>() {
{"grant_type", "refresh_token" },
{"refresh_token", Uri.EscapeDataString(user.refreshToken) }
});
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Post, "https://securetoken.googleapis.com/v1/token?key=" + apiKey);
message.Content = body;
message.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
HttpResponseMessage res = await client.SendAsync(message);
}
Unfortunately, I get this 401 (Unauthorized) error response:
{
"error": {
"code": 401,
"message": "Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
This is quite strange, considering that I get a 2XX (OK) response from what should be an equivalent cURL command...
2.
Thanks to a very nice website I just discovered, I was able to "convert" my cURL command into C# code. However, this did not work. I got the exact same error as attempt #1.
public async Task<bool> ExchangeToken()
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), "https://securetoken.googleapis.com/v1/token?key=[I WOULD INSERT MY KEY HERE]"))
{
request.Content = new StringContent("grant_type=refresh_token&refresh_token=[INSERT REFRESH TOKEN HERE]", Encoding.UTF8, "application/x-www-form-urlencoded");
var res = await client.SendAsync(request);
}
}
Possible Leads
All of my other requests to all of the other endpoints work. There are two notable differences: 1) the API is technically not Firebase's, but Google Identity Toolkit's. 2) This is the only endpoint that I'm using that uses a Content-Type header of application/x-www-form-urlencoded instead of application/json.
My Question / TL;DR
How do I interface with the Google Identity Toolkit API's Token Exchange endpoint using C#?
(Though I'm currently using the HttpClient class, I'm totally open to other solutions! They just have to be compatible with Unity3D.)
Thanks in advance!
Google wants you to be using Firebase for auth. While Firebase does not have a .NET client SDK shipped by Google, there are other options.
If you don't use Firebase and perform the requisite tasks with oAuth/OpenID Connect endpoints yourself, that works too. There is a Google .NET SDK that helps you with this...a little bit . The SDK lists Google APIs that are "supported" by the SDK; Google Cloud Identity v1 and v1beta APIs are both on the list. So you could call Google Cloud Identity endpoints via this .NET SDK. You do have to understand the implications of various auth flows whereas in Firebase much of that machinery is abstracted away from you as the app developer.
After playing around with the code a little more, I remembered that I was setting default authorization on my HttpClient:
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", idToken);
This was included for all of the other API calls, as they require authentication with the ID Token. However, this seemed to be confusing the token exchange endpoint; it must 1) look for an authorization header, then 2) use the refresh token supplied in the message contents.
How to fix this issue
Instead of setting the default header on your HttpClient (Which is supposed to be reused for all of your HTTP requests), do the following:
var req = new HttpRequestMessage(/* HttpMethod enum value, endpoint URL/IP */);
req.Headers.Authorization = new AuthenticationHeaderValue("Bearer", user.idToken); //Set the authorization per-request, not globally
var res = await client.SendAsync(req);
Then just simply call the Token Exchange endpoint!
From the Firebase REST API Documentation:
You can refresh a Firebase ID token by issuing an HTTP POST request to the securetoken.googleapis.com endpoint.
https://securetoken.googleapis.com/v1/token?key=[API_KEY]
Just provide a refresh_token parameter in the body (along with the mandatory grant_type with value refresh_token). DO NOT provide an authorization header.
Hope this helps anyone with the same problem!
As an alternative, you can also use https://github.com/google/apis-client-generator to generate the c# client from the discovery docs. I had some naming conflicts when I generated mine, but after resolving those it works.

What's the difference between Authentication Token and API Token in Jenkins remote build?

I'm trying to trigger Jenkins build from C#, using this link from here. I'm trying to invoke a paramaterised Jenkins Job from .NET.
In the Jenkins job, under the "Build Triggers' section, the "Trigger builds remotely (e.g., from scripts)" is checked and an authentication token is provided like
In my C# code, I'm asked to provide a username and an API token to invoke the Jenkins build. (_username, _apiToken from below)
private static HttpWebRequest CreateHttpRequest(string URLName)
{
//Creating HTTP web request
var httpWebRequest = (HttpWebRequest)WebRequest.Create(URLName);
httpWebRequest.ContentType = "application/x-www-form-urlencoded";
httpWebRequest.Method = "POST";
byte[] credentialBuffer =
new UTF8Encoding().GetBytes(
_username + ":" +
_apiToken);
httpWebRequest.Headers["Authorization"] =
"Basic " + Convert.ToBase64String(credentialBuffer);
httpWebRequest.PreAuthenticate = true;
return httpWebRequest;
}
This API token, I get it from the Jenkins server by navigating to [Jenkins server URL]/me/configure and I provide this in my code.
Now, when I try to invoke this Jenkins job which has this remote build trigger enabled, I get Forbidden/Not found error via code. When I try to manually navigate that URl with the API token, it says invalid token. Could this be because of the 'Authentication Token' that I mentioned earlier?
I'm asking this, because if I uncheck that option and invoke the Jenkins Job from code, there is no issue at all.
The API Token located within jenkins/me/configure is what you use to authenticate yourself when making REST calls against the Jenkins API, as stated in the tooltip:
This API token can be used for authenticating yourself in the REST API call.
The Authenticiation Token is a way to provide a shorthand URL so that you can easily trigger builds, as stated in the tooltip:
Enable this option if you would like to trigger new builds by accessing a special predefined URL (convenient for scripts).
If you're calling Jenkins via REST there there's no need for you to use the Authenticiation Token, however you will require your API token to authenticate yourself as the one making the request.

Calling Azure AD secured web API with client / secret

I've been at this whole day and it's driving me NUTS. I've got a web API published on Azure which is secured with Azure AD authentication. I've been using this API in combination with a mobile app for quite a while now. On the mobile app, I use the library for client authentication (Microsoft account which is in AD) and this works perfectly. Within the app, API requests are authenticated with the X-ZUMO-AUTH header.
Now for a new part of the application, I've got a C# console app which also needs to be able to call the app. This needs to run stand-alone, so without asking for user credentials. So I created an application in Azure AD, gave it permissions to the Web API instance and got me some code to get an authentication token. That token is then passed into the HttpClient object:
AuthenticationContext ac = new AuthenticationContext(authority);
ClientCredential clientCred = new ClientCredential(clientID, clientSecret);
AuthenticationResult authenticationResult = await ac.AcquireTokenAsync(resource, clientCred);
string authToken = authenticationResult.AccessToken;
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, endpoint);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
HttpResponseMessage response = await client.SendAsync(request);
Server auth is set-up like this:
string aadTenant = configProvider.GetConfigurationSettingValue("ida.Tenant");
string aadAudience = configProvider.GetConfigurationSettingValue("ida.Audience");
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = aadTenant,
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = aadAudience
},
});
I have triple checked all the variables. I also verified that the Bearer token is correctly passed to the service (Fiddler shows it). Everything seems absolutely fine, but still, the server keeps responding with Unauthorized.
My hunch is that this has something to do with the Web API. It's been set up as mobile services back-end. I'm suspecting that this is somehow interfering with 'normal' authentication schemes and the 401 is coming from mobile services not understanding this authentication request, even though the bearer authentication has been setup. But this is just a hunch and I'm not sure whether it actually makes sense or not.
I've seen the related questions on this topic, and all of the samples (used this one as the primary source of inspiration). I've tried all of the variations out there, to no avail. So what I'm looking for is ways to find out where the actual problem is. "Unauthorized" is a bit vague to say the least, so I'm trying to find out why the calls are returned with this response. Any help is greatly appreciated.
Additional endpoint info
As requested, here's some info on the endpoints I'm using:
authority = https://login.microsoftonline.com/{aad-tenant-name}
Also tried the OAuth2 token endpoints listed on the management page:
authority = https://login.microsoftonline.com/{aad-guid}/oauth2/token
authority = https://login.microsoftonline.com/{aad-guid}/oauth2/authorize
For the client, information I'm passing in the ClientID and generated secret I got from the AAD Application page for the application I created. I have granted the application permissions to access the App Service instance for my web API endpoint. Those calls are going to:
https://{app service url}.azurewebsites.net/api/controller
Whilst trying to get this going I noticed more weird behavior. I thought for now I'd just set this particular controller to [AllowAnonymous] so that I could call it without any authentication. But when I do so and omit passing in the Bearer token, I still get Unauthorized as response. That kind of enforces my idea that there's something wrong on the server side and that this might have something to do with how the mobile app stuff is strapped to the webapi controllers.
Never mind, the controller being forced to authorize was caused by Web App level authentication settings, documented here

Authenticate Android client on WCF Webapi site?

I want to implement a REST-api in C#. I found that WCF Webapi can do that.
My first question is how I can give only authenticated users access to my api?
And the second question is if the client to be authenticated is a Android-device, how do I do the HTTP request to authenticate?
Thanks!
em...
We did similar things, we use basic authentication+HTTPS,
that means the user name and password will be passed along each request, in the http header.
Thus in your web service, you can authenticate then, if it is from not valid user, then kick them out.
Or alternatively you can generate a GUID for each of your client, ask then to pass the GUID back to the search along with each http request, authenticate the GUID.
on Android device , when you send out the http request , add an http header
Authorization:Basic ****
quite easy , here is a codesnipet on android
String baseUrl = this.getValue(ServiceBaseUrlKey);</i>
DefaultHttpClient client = new ConnectionManager().getHttpClient();//create a httpclient
HttpGet request = new HttpGet();
request.setURI(new URI(baseUrl + "Path"));
//TODO need to wrap up how to apply the basic authentication.
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("UserName", "****");
request.addHeader(new BasicScheme().authenticate(credentials, request));
request.addHeader("Content-Type","Application/JSON");
HttpResponse response = client.execute(request);

Categories

Resources