I'm trying to get Atom feed for an email account using HttpClient. Before I tried to get mail.google.com and use them with custom HttpClientHandler but it didn't work.
I've searched for solution and managed to find out that I can use `Authorization header to pass credentials to the server but this doesn't work either. Am I doing something wrong? Why am I gtting 401 error? Does this method no longer work?
Here's my code:
public async Task<bool> CheckMail()
{
AMailRefresher.handler.CookieContainer = new CookieContainer();
string url = "https://mail.google.com/mail/feed/atom";
var encoded = StringToByte64(user + ":" + password);
HttpResponseMessage res = null;
try
{
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url);
req.Headers.Add("Authorization", "Basic " + encoded);
res = await AMailRefresher.http.SendAsync(req);
}
catch { }
var xml = await res.Content.ReadAsStringAsync();
if (lastFeedScan == null)
lastFeedScan = xml;
if (xml != lastFeedScan)
{
lastFeedScan = xml;
return true;
}
return false;
}
private static string StringToByte64(string text)
{
ASCIIEncoding encoding = new ASCIIEncoding();
var bytes = encoding.GetBytes(text);
return Convert.ToBase64String(bytes, 0, bytes.Length);
}
If you have enabled 2-Step Verification for your Google account (i.e. new logins send a code in a text message to your phone which you must then enter to authorize the login) then you cannot use your (base 64 encoded) normal password with this approach. Instead, you must create an App password in order to bypass the 2-Step Verification. See Sign in using App Passwords for detail. The How to generate an App password section directs you to App passwords where you can create a custom, unique 16 character password for your application to use instead.
Related
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'm making a tool in Unity to retrieve data from a server. The server's interface can provide URLs that we can later click on which will return an XML or CSV file with the results of that query from that server. But, it requires Basic Authentication. When clicking the links, it simply pops up a login screen before giving me the results. If I try what I [think] I know in Unity (starting with WebRequest.GetResponse()) it simply fails and says I am not authorized. It does not show the popup for authentication. So how do I let that login popup appear when accessing with Unity and await the login results to get the file? Or is there some standardized way to provide that info in the link itself?
Here is some code that should you get started. Just fill in the request link and username, password. please see the comments in the code to see what it does.
//try just in case something went wrong whith calling the api
try
{
//Use using so that if the code end the client disposes it self
using (HttpClient client = new HttpClient())
{
//Setup authentication information
string yourusername = "username";
string yourpwd = "password";
//this is when you expect json to return from the api
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
//add the authentication to the request
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes($"{yourusername}:{yourpwd}")));
//api link used to make the call
var requestLink = $"apiLink";
using (HttpResponseMessage response = client.GetAsync(requestLink).Result)
{
//Make sure the request was successfull before proceding
response.EnsureSuccessStatusCode();
//Get response from website and convert to a string
string responseBody = response.Content.ReadAsStringAsync().Result;
//now you have the results
}
}
}
//Catch the exception if something went from and show it!
catch (Exception)
{
throw;
}
This is what I ended up going with after looking at the comments above. Let me know if I'm doing anything terribly inefficient!
String username = "Superman"; // Obviously handled secretly
String pw = "ILoveLex4evar!"; // Obviously handled secretly
String url = "https://www.SuperSecretServer.com/123&stuff=?uhh";
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes(username + ":" + pw));
CookieContainer myContainer = new CookieContainer();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Headers.Add("Authorization", "Basic " + encoded);
try
{
using (WebResponse response = request.GetResponse())
{
using (Stream responseStream = response.GetResponseStream())
{
using (FileStream xml = File.Create("filepath/filename.xml"))
{
byte[] buffer = new byte[BufferSize];
int read;
while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
{
xml.Write(buffer, 0, read);
}
}
}
}
}
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 ...
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.
I'm having some problems where I'm trying to send Authorization header to my server in Unity. I've already done this in my Windows Phone project that I'm porting to Unity but when I send the request to my server it returns 500 Internal Server Error.
Here is my code where I do the call:
string hash = Helper.GetHashString();
byte[] toEncodeAsBytes = System.Text.Encoding.UTF8.GetBytes(username + ":" + password);
string base64 = System.Convert.ToBase64String(toEncodeAsBytes);
Hashtable headers = new Hashtable();
headers["Authorization"] = "Basic " + base64;
StartCoroutine(Server.Authorize(Config.SERVER_URL + "/Auth/" + hash, LoginEventResponse, headers));
The Server.Authorize:
public static IEnumerator Authorize(string path, DelegateEventFunction myEvent, Hashtable headers)
{
WWW www = new WWW(SERVER_URL + path, null, headers);
yield return www;
When the www returns I get the error (500) as mentioned above. The code that I'm porting from, and is working is:
string hash = Helper.GetHashString();
byte[] toEncodeAsBytes = System.Text.Encoding.UTF8.GetBytes(Model.Username + ":" + Model.Password);
string base64 = Convert.ToBase64String(toEncodeAsBytes);
HttpClient loginClient = new HttpClient();
loginClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64);
string loginResponse = await loginClient.GetStringAsync(Config.SERVER_URL + "/Auth/" + hash);
Very much the same besides the Unitys use of WWW instead of HttpClient. Maybe this is solvable by buying PRO version letting me tap into native libraries, but every webcall works besides this one there I try to send in Authorization header.
Anyone got a tips?
EDIT
I've got this off the logs:
Error X-Token: chars_and_numbers,,chars_and_numbers2
Error Exception: Sequence contains no elements in System.Core
Google indicates that there is something wring with the encoding? I use the same method, UTF8 all the way...