I'm trying to connect to jsonwhoisapi.com to drag down some whois data, but am having no luck. Has anyone done this or done something similar and can help spot my folly? I've never done an api connection using HTTP headers.
I've basically copied this from a post online where it was apparently working, but the following dies at GetResponseStream.
public static void WebRequest()
{
string WEBSERVICE_URL = "https://jsonwhoisapi.com/api/v1/whois?identifier=google.com";
try
{
var webRequest = System.Net.WebRequest.Create(WEBSERVICE_URL);
if (webRequest != null)
{
webRequest.Method = "GET";
webRequest.Timeout = 20000;
webRequest.ContentType = "application/json";
webRequest.Headers.Add("userid:apikey");
using (System.IO.Stream s = webRequest.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(s))
{
var jsonResponse = sr.ReadToEnd();
Console.WriteLine(String.Format("Response: {0}", jsonResponse));
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
My guess is you'll have to replace 'apikey' with your ApiKey string, and userId with your CustomerId string.
Direct quote:
With every request you must send the [customer ID:API key] pair to authenticate - both pieces of information can be found when logged in to you account.
Chances are you might have to encode the auth request using the instructions here: https://en.wikipedia.org/wiki/Basic_access_authentication#Protocol
Client side:
When the user agent wants to send the server authentication
credentials it may use the Authorization field.
The Authorization field is constructed as follows:
The username and password are combined with a single colon. (:)
The resulting string is encoded into an octet sequence.
The resulting string is encoded using a variant of Base64.
The authorization method and a space is then prepended to the encoded string, separated with a space (e.g. "Basic ").
For example, if the browser uses Aladdin as the username and
OpenSesame as the password, then the field's value is the
base64-encoding of Aladdin:OpenSesame, or QWxhZGRpbjpPcGVuU2VzYW1l.
Then the Authorization header will appear as:
Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
e.g. Something like:
webRequest.Headers.Add(string.Format("Authorization: Basic {0}", Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userId, apiKey)))));
Related
I am trying to add some API tests for our application that validate that a message is sent out and eventually received in Gmail. I went ahead and added an API key for my email in the Google Cloud Services developer console and created the following code.
The Client.cs class is just an example I found online that I tweaked and made work with our endpoints (maybe this type of strategy wont work with Gmail API?)
Enpoints.cs is just the class to return the URL. Im also including the key as an optional parameter
Test.cs just contains a segment of how I am calling the endpoint and getting the 401 Unauthorized error
Are we able to use the list endpoint to get the contents of a gmail inbox by using an API Key?
Client.cs
public enum Command {
GET,
POST,
PUT,
DELETE }
namespace Project{
public class Client
{
public string endPoint { get; set; }
public Command execute { get; set; }
public string contentType { get; set; }
public string postData { get; set; }
public string encoded = "";
// Basic Client
public Client()
{
endPoint = "";
execute = Command.GET;
contentType = "application/JSON";
postData = "";
encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("email#gmail.com"
+ ":" + "mypass"));
}
// Basic Request
public string Request(string parameters)
{
var request = (HttpWebRequest)WebRequest.Create(parameters);
request.Headers.Add("Authorization", "Basic " + encoded);
request.Method = execute.ToString();
request.ContentLength = 0;
request.ContentType = contentType;
if (postData != "")
{
request.SendChunked = true;
request.ContentType = "application/json";
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
string json = postData;
streamWriter.Write(json);
}
}
using (var response = (HttpWebResponse)request.GetResponse())
{
var responseValue = string.Empty;
if (response.StatusCode != HttpStatusCode.OK)
{
var message = String.Format("Failed: Received HTTP {0}", response.StatusCode);
Console.WriteLine(message);
//throw new ApplicationException(message);
}
using (var responseStream = response.GetResponseStream())
{
if (responseStream != null)
using (var reader = new StreamReader(responseStream))
{
responseValue = reader.ReadToEnd();
}
}
return responseValue;
}
}
}
}
Endpoints.cs
namespace Project
{
class Endpoints
{
// Class to store URLs for Gmail Integrations
public static class Gmail
{
// Class to interface with Gmail Messages
public static class Messages
{
// Retrieve Messsages (List)
public static string List(string userId)
{
return "https://gmail.googleapis.com/gmail/v1/users/" + userId + "/messages?key=mykey";
}
}
}
}
}
Test.cs
// Prepare the Client
Client client = new Client();
// Get the Response
client.execute = Command.GET;
var response = client.Request(Endpoints.Gmail.Messages.List("email#gmail.com"));
// Print Response
Console.WriteLine(response);
No.
Because Gmail includes personal|user data, you will need to provide a facility for the specific user to permit access using OAuth2. There's a facility within Workspace for domain-wide delegation of a Service Account that permits the Service Account to operate on your users' behalf.
Google's APIs Explorer is very useful.
For Gmail API, you can interact with e.g. users.messages.list in the browser.
Google provides auto-generated API Client Libraries for every service in 8 languages. Here's Gmail API Client Library for .NET. I strongly encourage you to use Google SDKs to avoid dealing with the APIs' types, auth, logging etc. directly. As you've seen, this can be challenging, unnecessarily duplicates work (best left to Google) and increases the scope of your security audit.
If you look at the HTTP example from APIs Explorer. You will see that the HTTPS request must include an Authorization header with a value of Bearer [SOME-ACCESS-TOKEN]. The Access Token will be provided to you by Google's OAuth2 endpoint.
https://developers.google.com/gmail/api/reference/rest/v1/users.messages/list?apix_params=%7B%22userId%22%3A%22me%22%7D&apix=true
I was unable to find the link to a good overview of API keys and why you should avoid using them to authenticate. API keys are easily obtained and are a bearer credential (i.e. you don't want them to be easily obtained). Generally (!) an API key is used to associate requests with a specific Google project for billing purposes. They should not be used to authenticate users. See API keys
For anyone else who might stumble upon this, it turns out there really is no easy way to do this with GMail. This post does describe a way to do it, however with the OAuth2 authentication, you will need to login using a browser popup which means no headless API tests..
https://www.swtestacademy.com/gmail-api-test-automation/
I ended up using this product, https://testmail.app/, which is super affordable and lightweight and works great if you are just trying to do some simple email validations.
EDIT
This question was for a workaround. Getting a successful login by using HttpWebRequests. Not on how to use the api.
Question
I noticed in the API there was no way to get a usernames password.
This is what I have now as a test. I thought I could just get the "Wrong user/pass" response first and go from there. All I get is the pages source code.
Anyone have any pointers or advice?
I am definitively logging in. In Account Admin and Login History, it shows me logging in. But the server is not serving any useful response text for the login. And now, I locked myself out using wrong passwords to sort through the streamreader lol.
public string DoVerification(string email, string password)
{
var request = (HttpWebRequest)WebRequest.Create("https://app.smartsheet.com/b/home");
var postData = "loginEmail=" + email;
postData += "&loginPassword=" + password;
postData += "&action=login";
var data = Encoding.ASCII.GetBytes(postData);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
var response = (HttpWebResponse)request.GetResponse();
return new StreamReader(response.GetResponseStream()).ReadToEnd();
}
private void btnLogin_Click(object sender, EventArgs e)
{
string response = DoVerification("test#test.com", "12345");
MessageBox.Show(response.ToString());
}
It's unclear what you are trying to achieve or why you expect it to work.
There is no way to retrieve a password through the API. That would be a bad idea.
You aren't actually using the API. API endpoints start with https://api.smartsheet.com/2.0 and are documented here: http://smartsheet-platform.github.io/api-docs/
After comparing both sources from the returned response.
I have these two js functions.
Failed attempt
function loggedFailures() {
logExternalGTMEvent({'event': 'app-login-failure','method': 'onsite','error': 'AUTH_NO_MATCHING_USER'}); return true
}
Successful attempt
function loggedFailures() {
return false
}
I just simply check for one or the other.
And for the record, putting the users password in the api is not a bad idea.... Smartsheets lets us delete any user through the API, so I don't see what it would matter.
I am trying to implement a very basic system from my C# .NET application that sends the IP address of the machine to authenticate.php on my web server. The php page will check this IP address against a database and either respond back with "yes" or "no".
It has been a long time since I worked with PHP, and I am a little bit confused. Here is what my .NET function looks like.
public static bool IsAuthenticated()
{
string sData = getPublicIP();
Uri uri = new Uri("http://www.mysite.com/authenticate.php");
if (uri.Scheme == Uri.UriSchemeHttp)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = WebRequestMethods.Http.Post;
request.ContentLength = sData.Length;
request.ContentType = "application/x-www-form-urlencoded";
// POST the data to the authentication page
StreamWriter writer = new StreamWriter(request.GetRequestStream());
writer.Write(sData);
writer.Close();
// Retrieve response from authentication page
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string sResponse = reader.ReadToEnd();
response.Close();
if (sResponse == "yes")
{
Console.WriteLine("Authentication was Successful.");
return true;
}
else
{
Console.WriteLine("Authentication Failed!");
return false;
}
}
}
So would the POST variable be $_POST['sData']; and how do I respond back to my application with the result?
Assuming the value of sData is (say) "10.1.1.1" then you're currently not posting proper form data in the first place. The name of the variable isn't part of the text written by
writer.Write(sData);
You need to do something like:
string postData = "ipaddress=" + sData;
and then use the ipaddress form parameter within your PHP.
Note also that you should be giving the binary content length, which may not be the same as the string length in characters. Of course it's okay if the string here is entirely ASCII, which I'd expect if it's an IP address... but it's worth bearing in mind for other uses. (Likewise you would normally need to bear in mind any characters which need special encoding.)
Also note that it would be better to use using statements for the StreamWriter, HttpResponse etc, to make sure that everything gets closed even if an exception is thrown.
This code is for an outlook plugin. We're trying to POST to a page and are getting this error:
The remote server returned an error: (422) Unprocessable Entity.
The C# code is here:
webClient.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
ASCIIEncoding asciiEncoding = new System.Text.ASCIIEncoding();
Byte[] postData = asciiEncoding.GetBytes("email=e2#email.com&password=hunter2");
char[] resultHTML = asciiEncoding.GetChars(webClient.UploadData("http://url", "POST", postData));
string convertedResultHTML = new string(resultHTML);
Any idea what could be causing this?
POST data must be encoded prior to be sent out on the wire as ASCII, if you are sending character not in the ASCII range. You should try something like:
Byte[] postData = asciiEncoding.GetBytes(HttpUtility.UrlEncode("email=e2#email.com&password=hunter2"));
Because of its limited functionality, I avoid using WebClient and use WebRequest instead. The code below:
does not expect an HTTP 100 status code to be returned,
creates a CookieContainer to store any cookies we pick up,
sets the Content Length header, and
UrlEncodes each value in the post data.
Give the following a try and see if it works for you.
System.Net.ServicePointManager.Expect100Continue = false;
System.Net.CookieContainer cookies = new System.Net.CookieContainer();
// this first request just ensures we have a session cookie, if one exists
System.Net.WebRequest req = System.Net.WebRequest.Create("http://localhost/test.aspx");
((System.Net.HttpWebRequest)req).CookieContainer = cookies;
req.GetResponse().Close();
// this request submits the data to the server
req = System.Net.WebRequest.Create("http://localhost/test.aspx");
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
((System.Net.HttpWebRequest)req).CookieContainer = cookies;
string parms = string.Format("email={0}&password={1}",
System.Web.HttpUtility.UrlEncode("e2#email.com"),
System.Web.HttpUtility.UrlEncode("hunter2"));
byte[] bytes = System.Text.Encoding.ASCII.GetBytes(parms);
req.ContentLength = bytes.Length;
// perform the POST
using (System.IO.Stream os = req.GetRequestStream())
{
os.Write(bytes, 0, bytes.Length);
}
// read the response
string response;
using (System.Net.WebResponse resp = req.GetResponse())
{
if (resp == null) return;
using (System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream()))
{
response = sr.ReadToEnd().Trim();
}
}
// the variable response holds the results of the request...
Credits: Hanselman, Simon (SO Question)
This is the RoR application telling you that you have not formed a request that it can handle; the destination script exists (otherwise you'd see a 404), the request is being handled (otherwise you'd get a 400 error) and it's been encoded correctly (or you'd get a 415 error) but the actual instruction can't be carried out.
Looking at it, you seem to be loading some email information. The RoR application could be telling you that the username and password is wrong, or that the user doesn't exist, or something else. It's up to the RoR application itself.
I think the code itself is good; it's just that the app at the other end isn't happy about doing what you ask it. Are you missing something else in the request information, like a command? (eg command=getnetemails&email=e2#email.com&password=hunter2) Are you sure the email/password combination you are passing is good?
see here for more on the 422 error.
Add the below line above your code.
System.Net.ServicePointManager.Expect100Continue = false;
Are you trying to access an authentication required page?
it was solved by returning xml instead of just unstructured text on the RoR side
Bounty Question
I am using c# 3.5 Window Forms Application. I am using the code mentioned in the accepted answer. and I am getting below error
The remote server returned an error: (401) Unauthorized.
Sample code to verify the UserName and Password will be really appreciated
Bounty Question Ends
I have an application with the following use-case: when the user first starts using the application, he inputs his username and password. Then, at a much later stage, the application may update his status.
Currently I'm using Twitterizer, but I believe the question is beyond the scope of the specific library I'm using. Following are the two relevant lines of code:
Twitter twitter = new Twitter("username", "password", "source");
twitter.Status.Update("update");
The construction of the Twitter object does not throw an exception if the username/password are incorrect. This is probably because nothing is sent at this point. On the other hand, the status update does throw an exception if the username/password are invalid.
My problem is that I want to validate the username/password at the point of user input, not when trying to post the update.
How can I validate the username/password without posting anything (in Twitterizer or otherwise)?
Taking a quick look at the verify_credentials API as mentioned by peSHIr, I wrote a little routine which seems to do the trick. It's late, but I was able to test it a couple of times and seems to work.
In my function, I am just returning true if I I get an HttpResponseCode.OK, and false if I get anything else or an exception is thrown. If twitter does not like the uid/password an exception will be thrown with a 401 error (not authorized.)
public bool CheckTwitterCredentials(string UserName, string Password)
{
// Assume failure
bool Result = false;
// A try except block to handle any exceptions
try {
// Encode the user name with password
string UserPass = Convert.ToBase64String(
System.Text.Encoding.UTF8.GetBytes(UserName + ":" + Password));
// Create our HTTP web request object
HttpWebRequest Request =
(HttpWebRequest)WebRequest.Create("http://twitter.com/account/verify_credentials.xml");
// Set up our request flags and submit type
Request.Method = "GET";
Request.ContentType = "application/x-www-form-urlencoded";
// Add the authorization header with the encoded user name and password
Request.Headers.Add("Authorization", "Basic " + UserPass);
// Use an HttpWebResponse object to handle the response from Twitter
HttpWebResponse WebResponse = (HttpWebResponse)Request.GetResponse();
// Success if we get an OK response
Result = WebResponse.StatusCode == HttpStatusCode.OK;
} catch (Exception Ex) {
System.Diagnostics.Debug.WriteLine("Error: " + Ex.Message);
}
// Return success/failure
return Result;
}
You could try to use the API call account/verify_credentials. Hopefully the API library you use already supports this.. Twitter is notorious now for really hating third party programmers, so unless you have good reason to do something with Twitter, just stay away...
I have used other Twitter Libraries but none of them support checking the username and password for validitity. This might be because Twitter API does not have the facility to validate the username and password unless we try to do something which requires authentication.
One thing you can do is try to get friend list or any other methods that requires authentication.
Twitter API hasn't supported username/password in years. Instead, you have OAuth, which lets the user authorize your application to act on their behalf. Twitter has an account/verify_credentials endpoint you can use to verify whether the user who's tokens you have still authorizes your app. Here's an example of how you could call this endpoint with LINQ to Twitter:
var accounts =
from acct in twitterCtx.Account
where acct.Type == AccountType.VerifyCredentials
select acct;
You can visit Account/VerifyCredentials documentation for more details:
as #joe-mayo informed, you have to switch to OAuth. twitter expired v1 of their API and they documented that in following url https://dev.twitter.com/docs/faq#17750.
Here's a function i wrote that will verify twitter username and password in C# :
public bool isTwitterValid(string username, string password)
{
try
{
string user = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(username + ":" + password));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("https://twitter.com/statuses/verify.xml");
request.Method = "POST";
request.ServicePoint.Expect100Continue = false;
request.Headers.Add("Authorization", "Basic " + user);
request.ContentType = "application/x-www-form-urlencoded";
WebResponse response = request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
string responseString = reader.ReadToEnd();
reader.Close();
}
catch (Exception ex)
{
if (ex.Message.Contains("404")) { return true; }
}
return false;
}