Based on the example at https://gist.github.com/nfriedly/0240e862901474a9447a600e5795d500,
I am trying to use WebSocket to use the IBM Speech to Text API.
But I am having problems with the authentication part.
It looks like now IBM does not provide a username/password anymore.
Only an api key.
So I cannot find a way to added that example to use an api to get the token.
Any know how to use WebSocket with the IBM apikey for authentication?
The IBM doc does not seem to be up to date either as their example are using CURL with username and password https://console.bluemix.net/docs/services/speech-to-text/getting-started.html#getting-started-tutorial
I even saw somewhere that I could replace the username with "api" and the password by my apikey.
But that's not working as I get an Unauthorized error from the server.
Maybe I misread and I should pass a token instead of the password.
But then how do I get a token from my APIkey with websockets?
I can get a token using HttpClient without problems.
But it looks like I cannot use that token with Websocket after that, only further HttpClient calls.
With some help I finally found how to handle the WebSocket with the apiKey.
I post the code here in case someone else needs it
IamTokenData GetIAMToken(string apikey)
{
var wr = (HttpWebRequest)WebRequest.Create("https://iam.bluemix.net/identity/token");
wr.Proxy = null;
wr.Method = "POST";
wr.Accept = "application/json";
wr.ContentType = "application/x-www-form-urlencoded";
using (TextWriter tw = new StreamWriter(wr.GetRequestStream()))
{
tw.Write($"grant_type=urn:ibm:params:oauth:grant-type:apikey&apikey={apikey}");
}
var resp = wr.GetResponse();
using (TextReader tr = new StreamReader(resp.GetResponseStream()))
{
var s = tr.ReadToEnd();
return JsonConvert.DeserializeObject<IamTokenData>(s);
}
}
IamTokenData tokenData = GetIAMToken([Your IamApiKey]);
CancellationTokenSource cts = new CancellationTokenSource();
ClientWebSocket clientWebSocket = new ClientWebSocket();
clientWebSocket.Options.Proxy = null;
clientWebSocket.Options.SetRequestHeader("Authorization", $"Bearer {token.AccessToken}");
// Make the sure the following URL is that one IBM pointed you to
Uri connection = new Uri($"wss://gateway-wdc.watsonplatform.net/speech-to-text/api/v1/recognize");
try
{
//await clientWebSocket.ConnectAsync(connection, cts.Token);
clientWebSocket.ConnectAsync(connection, cts.Token).GetAwaiter().GetResult();
Console.WriteLine("Connected!");
}
catch (Exception e)
{
Console.WriteLine("Failed to connect: " + e.ToString());
return null;
}
// ... Do what you need with the websocket after that ...
Related
I'm writing a windows service in C# that needs to authenticate with an API and make some calls. I'm able to authenticate successfully with this API I'm talking to, but I can't seem to figure out how to use the response. The response looks like this:
{"access_token":"Es-Zjs_LI0tcXyLe3aEfgKPNLHN7CwyUhTss-cTld1A","expires_in":1800,"token_type":"Bearer","scope":"example","auth_state":1,"company":"examplecompany"}
I can get the access token out of that string if I want, but no matter how I pass it to a request, I get a 401 error. This is what my current iteration looks like:
string results = "";
var client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer",token);
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri("https://example.ca/endpoint"),
//Headers =
//{
// { "authorization", "Bearer"},
//},
};
try
{
using (var response = await client.SendAsync(request))
{
response.EnsureSuccessStatusCode();
var body = await response.Content.ReadAsStringAsync();
results = body;
}
}
catch (Exception ex)
{
results = "ERROR: " + ex.Message;
}
return results;
Where "token" is the string "Es-Zjs_LI0tcXyLe3aEfgKPNLHN7CwyUhTss-cTld1A" in this example. I had previously tried stitching the access_token value as a string to the "Bearer" string in the commented out section in the middle there. What am I doing wrong? Do I need to make a JwtSecurityToken out of the response?
AuthenticationResult authResult = await daemonClient.AcquireTokenForClient(new[] { MSGraphScope })
.ExecuteAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
I've used the authResult.AccessToken. Not sure if it works in your scenario. The return type in my case was Microsoft.Identity.Client.AuthenticationResult type when I retrieved the token for a Graph API that I was using.
Be aware that the token you have received ("Es-Zjs_LI0tcXyLe3aEfgKPNLHN7CwyUhTss-cTld1A") is a reference token and not a JWT-token. Make sure your API accepts that type of token.
To use the token effectively in production then I would consider using the various helper methods found in the IdentityModel library and especially the Worker application helpers.
While I understand it's largely situational depending on what API you're trying to connect to, for me the solution was to use this method to pass in the authentication token:
request.Headers.TryAddWithoutValidation("Authorization", "Bearer " + token);
I am working on a Hashicorp Vault management .net-core 3.1.3 console application written in C#. I have been tasked with creating a RabbitMQ user on an MQ server from the console app utilizing the RabbitMQ restful API. I have zero experience with this API. I have been reading the documentation but still don't have a clue as to how to begin.
I have limited experience with APIs in general and have never tried to do anything like this from a console app.
Any guidance or example code would be greatly appreciated.
Cheers.
Matt
You'll need the RabbitMQ Management HTTP API, the docs for which are here. Specifically you'll want to PUT a user on the /api/users/name endpoint.
There are many ways to make an HTTP request in c#, the simplest is probably the WebRequest class as documented here. You'll need to set the method to PUT, write your json payload to the request and set your rabbitmq credentials for the request.
Thanks for the clue-bat Adam. Here is where I ended up, and works well.
try
{
// Set MQ server credentials
NetworkCredential networkCredential = new NetworkCredential("mqUserName", "mqPassword");
// Instantiate HttpClientHandler, passing in the NetworkCredential
HttpClientHandler httpClientHandler = new HttpClientHandler { Credentials = networkCredential };
// Instantiate HttpClient passing in the HttpClientHandler
using HttpClient httpClient = new HttpClient(httpClientHandler);
// Get the response from the API endpoint.
HttpResponseMessage httpResponseMessage = await httpClient.GetAsync("http://YourServer:AndPort/api/users/");
// Get the response content.
HttpContent httpContent = httpResponseMessage.Content;
// Get the stream of the content.
using StreamReader streamReader = new StreamReader(await httpContent.ReadAsStreamAsync());
// Get the output string.
string returnedJsonString = await streamReader.ReadToEndAsync();
// Instantiate a list to loop through.
List<string> mqAccountNames = new List<string>();
if (returnedJsonString != "")
{
// Deserialize into object
dynamic dynamicJson = JsonConvert.DeserializeObject(returnedJsonString);
if (dynamicJson != null)
{
foreach (dynamic item in dynamicJson)
{
mqAccountNames.Add(item.name.ToString());
}
}
}
bool accountExists = false;
foreach (string mqAccountName in mqAccountNames)
{
if (mqAccountName == userName)
{
accountExists = true;
}
}
switch (accountExists)
{
case true:
Console.WriteLine("This user already exists on the MQ server.");
break;
case false:
// Create the new user on the MQ Server
Console.WriteLine("This user will be created on the MQ server.");
string uri = $"http://YourServer:AndPort/api/users/{userName}";
MqUser mqUser = new MqUser
{
password = password,
tags = "administrator"
};
string info = JsonConvert.SerializeObject(mqUser);
StringContent content = new StringContent(info, Encoding.UTF8, "application/json");
httpResponseMessage = await httpClient.PutAsync(uri, content);
if (!httpResponseMessage.IsSuccessStatusCode)
{
Console.WriteLine("There was an error creating the MQ user account.");
Thread.Sleep(2500);
return false;
}
uri = $"http://YourServer:AndPort/api/permissions/%2F/{userName}";
MqPermissions mqPermissions = new MqPermissions
{
configure = ".*",
write = ".*",
read = ".*"
};
info = JsonConvert.SerializeObject(mqPermissions);
content = new StringContent(info, Encoding.UTF8, "application/json");
httpResponseMessage = await httpClient.PutAsync(uri, content);
if (!httpResponseMessage.IsSuccessStatusCode)
{
Console.WriteLine("There was an error creating the permissions on the MQ user account.");
Thread.Sleep(2500);
return false;
}
break;
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
I created simple MqUser and MqPermissions classes so I could just JsonConvert.SerializeObject to pass the info.
Another weird thing was that my company chose to name the MQ Virtual Host as "/".
This had not been an issue up to this point as we had never tried to use the API before.
Since the / character is expected in a uri, this was a hangup, but I tried encoding it as %2F and it works just fine.
I've created a ASP.Net Core 2.1 web application which gathers data from two sources. One is a SQL-database which supplies data via Entity Framework to my application. This one works fine. The other source is a REST API. I'm having troubles connecting to this.
I'm calling this Task which should return a user via his/hers e-mail address:
public async Task<PersonsModel> ReturnPersonByEmail(string email)
{
const string apiKey = "xxx";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://xx.xxx.xxxx.xx:xxx/xxxx/xxx/xx/xx/person/?email={email}");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("x-api-key", "123456");
var url = new Uri(client.BaseAddress.ToString());
string json = "";
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var response = await client.GetAsync(url);
using (var content = response.Content)
{
json = await content.ReadAsStringAsync();
}
}
catch (Exception e)
{
var exception = e;
}
return JsonConvert.DeserializeObject<PersonsModel>(json);
}
}
}
When I try calling it via Postman client.GetAsync(url) always returns an exception:
Message = "The remote certificate is invalid according to the validation procedure."
Message = "The SSL connection could not be established, see inner exception."
I tried adding the following codesegment to launchSettings.json(as per a reply to a similar question posted here: HttpClient client.GetAsync works in full .Net framework but not in .Net core 2.1? ) "DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER": "0" and then I get another error:
Message = "Error 12175 calling WINHTTP_CALLBACK_STATUS_REQUEST_ERROR, 'A security error has occurred'."
If you have any idea on what might cause this problem I would be very grateful for any help. Thanks!
I am using Stormpath for my authentication service. I call the RestAPI of Stormpath by using HttpWebRequest.
I am also using HttpWebRequest to call the RestAPI but it does not work.
private void BtnGetResetApiClick(object sender, EventArgs e)
{
var username = "aaaa";
var password = "bbbb";
ServicePointManager.ServerCertificateValidationCallback = Callback;
var request = WebRequest.Create("https://api.stormpath.com/v1/tenants/current") as HttpWebRequest;
request.UserAgent = ".NET SDK";
request.Method = "GET";
request.Accept = "*/*";
var data = string.Format("{0}:{1}", username, HttpUtility.HtmlEncode(password));
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
string authHeader = string.Format("Basic {0}", token);
request.Headers.Add("Authorization", authHeader);
request.ServerCertificateValidationCallback = Callback;
using (var response = request.GetResponse())
{
var stream = response.GetResponseStream();
if (stream != null)
{
var streamReader = new StreamReader(stream);
var str = streamReader.ReadToEnd();
streamReader.Close();
stream.Close();
}
}
}
private bool Callback(object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
}
When calling:
var response = request.GetResponse()
I got an exception:
An unhandled exception of type 'System.Net.WebException' occurred in System.dll The remote server returned an error: (401) Unauthorized.
Can you help me to see if my code has something wrong?
Update - use the SDK, it's much easier!
If you're calling the Stormpath API from C# frequently, don't bother with writing requests by hand. Use the Stormpath .NET SDK instead. I'm the author. :)
Install it using install-package Stormpath.SDK from the Package Manager Console. Then, create an IClient object:
// In a production environment, you'll want to load these from
// environment variables or a secure file, instead of hardcoding!
var apiKey = ClientApiKeys.Builder()
.SetId("Your_Stormpath_API_key_ID")
.SetSecret("Your_Stormpath_API_key_secret")
.Build();
var client = Clients.Builder()
.SetApiKey(apiKey)
.Build();
Getting the tenant info is now just a simple call:
var tenant = await client.GetCurrentTenantAsync();
Console.WriteLine($"Current tenant is: {tenant.Name}");
If you really want to make raw requests, you can still do that! I'll explain below.
Constructing the Authorization header
A 401 Unauthorized response means that the API was not able to find a valid Authorization header in your request. To authenticate correctly, you need two things:
An authorization payload in the format apiKeyID:apiKeySecret
An Authorization header with value: Basic base64(payload)
Here's how to construct the complete header:
// API_KEY_ID and API_KEY_SECRET populated elsewhere
var authPayload = string.Format("{0}:{1}", API_KEY_ID, API_KEY_SECRET);
var authPayloadEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authPayload));
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + authPayloadEncoded);
You don't need the ServerCertificateValidationCallback = Callback stuff. With the above header, the request will be seen by the API as a valid request (assuming your API Key ID and Secret are correct, of course).
Redirection handling
One thing to watch out for (this tripped me up at first!) is that WebRequest will follow HTTP 302 redirects automatically, but will not apply the existing headers to the new request.
The solution is to disable redirect following:
request.AllowAutoRedirect = false;
This means you'll have to handle 302 responses yourself, but it's the only way to correctly apply the Authorization header to each request.
Working example
I created a simple working example in this gist. Since I'll be creating requests multiple times, I wrote a helper function:
private static HttpWebRequest BuildRequest(string method, string uri)
{
var request = WebRequest.Create(uri) as HttpWebRequest;
request.UserAgent = "dotnet/csharp web-request";
request.Method = method;
request.ContentType = "application/json";
// Important, otherwise the WebRequest will try to auto-follow
// 302 redirects without applying the authorization header to the
// subsequent requests.
request.AllowAutoRedirect = false;
// Construct HTTP Basic authorization header
var authPayload = string.Format("{0}:{1}", API_KEY_ID, API_KEY_SECRET);
var authPayloadEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authPayload));
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + authPayloadEncoded);
return request;
}
And a simple console app to demonstrate getting the current tenant URL and name:
// Get these from the Stormpath admin console
private static string API_KEY_ID = "Your_Stormpath_API_key_ID";
private static string API_KEY_SECRET = "Your_Stormpath_API_key_secret";
static void Main(string[] args)
{
// First, we need to get the current tenant's actual URL
string tenantUrl = null;
var getCurrentTenantRequest = BuildRequest("GET", "https://api.stormpath.com/v1/tenants/current");
try
{
using (var response = getCurrentTenantRequest.GetResponse())
{
tenantUrl = response.Headers["Location"];
}
}
catch (WebException wex)
{
Console.WriteLine("Request failed. {0}", wex.Message);
throw;
}
// Now that we have the real tenant URL, get the tenant info
string tenantData = null;
var getTenantInfoRequest = BuildRequest("GET", tenantUrl);
try
{
using (var response = getTenantInfoRequest.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var reader = new StreamReader(responseStream))
{
tenantData = reader.ReadToEnd();
}
}
catch (WebException wex)
{
Console.WriteLine("Request failed. {0}", wex.Message);
throw;
}
// Use JSON.NET to parse the data and get the tenant name
var parsedData = JsonConvert.DeserializeObject<Dictionary<string, object>>(tenantData);
Console.WriteLine("Current tenant is: {0}", parsedData["name"]);
// Wait for user input
Console.ReadKey(false);
}
The code is pretty verbose because we're making raw requests to the API. Again, if you're making requests frequently, use the SDK instead!
I need your help!.
Im trying to insert a new photo into a Picasa Album using Oauth 2.0 and a simple HttpRequest process. The result is that I cant insert a new photo into my Picasa web album after following the instructions listed on: https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol#Auth
I also have to say that I tried using the .Net library that they provide with the same results.
The implementation that I'm using now is the following:
public static string PostImage(
string streamImageConvertedToString)
{
string url = string.Format("https://picasaweb.google.com/data/feed/api/user/{0}/albumid/{1}", "username#gmail.com", "idAlbum");
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.ContentType = "image/jpeg";
request.ContentLength = Encoding.UTF8.GetByteCount(data);
request.Method = "POST";
request.Headers.Add("GData-Version", "2");
request.Headers.Add("Slug", "cute_baby_kitten.jpg");
request.Headers.Add("Authorization", "Bearer " + GetToken());
if (data != null)
{
using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))
{
writer.Write(data);
}
}
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
string result = string.Empty;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
result = reader.ReadToEnd();
}
return result;
}
private static string GetToken() {
const string ServiceAccountEmail = "someid#developer.gserviceaccount.com";
var servicio = new PicasaService(null);
var certificate = new X509Certificate2(HttpContext.Current.Server.MapPath("/key2.p12"), "notasecret", X509KeyStorageFlags.Exportable);
var serviceAccountCredentialInitializer =
new ServiceAccountCredential.Initializer(ServiceAccountEmail)
{
Scopes = new[] { "https://picasaweb.google.com/data/" }
}.FromCertificate(certificate);
var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
if (!credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Result)
throw new InvalidOperationException("Access token request failed.");
return credential.Token.AccessToken;
}
Any help is welcome!!
(403) Forbidden
Means that you are trying to use a method insert which requires authorization to do.
you are connecting to service account someid#developer.gserviceaccount.com which should give you access to someid#developer.gserviceaccount.com pictures then.
you appear to be trying to access username#gmail.com unless you have given someid#developer.gserviceaccount.com access to insert pictures on behalf of username#gmail.com (Which I am not even sure is possible) you are not going to have permission to do this.
Remember a service account is a sudo user it has its own drive account, calendar account ... it does not have access to a random users data unless that user has given them access like they would any other user.
Note: Google .net client library does not support gdata APIs. Picasa is a gdata library I like how are trying to merge the two I am have to test this.
You're best (imho) approach would be to forget libraries and forget service accounts. Get a refresh token for the google user account you're trying to insert to, and use the raw HTTP REST API to invoke Picasa.