I'm working on a C# Windows Form application and I would like to have the ability to test a users' credentials against Jira. Basically the user would input their username and password, click OK and the program will tell them if their credentials are accepted or not.
I already have working code (see below) that uses basic authentication via HttpWebRequest to create new tickets (aka issues), close tickets, add watchers, etc - so I figured this would be easy but I'm struggling with it.
As an analog, you can do a credentials check against Active Directory very easily using the System.DirectoryServices.AccountManagement namespace. Basically the method authenticateAD() will simply return true or false:
private bool authenticateAD(string username, string password)
{
PrincipalContext pc = new PrincipalContext(ContextType.Domain, "example.com");
bool isValid = pc.ValidateCredentials(username,password);
return isValid;
}
This is exactly the kind of thing I want to do with Jira.
For reference, here's the code I'm using to add/close/update tickets in jira - maybe it can be modified to do what I want?
private Dictionary<string, string> sendHTTPtoREST(string json, string restURL)
{
HttpWebRequest request = WebRequest.Create(restURL) as HttpWebRequest;
request.Method = "POST";
request.Accept = "application/json";
request.ContentType = "application/json";
string mergedCreds = string.Format("{0}:{1}", username, password);
byte[] byteCreds = UTF8Encoding.UTF8.GetBytes(mergedCreds);
request.Headers.Add("Authorization", "Basic " + byteCreds);
byte[] data = Encoding.UTF8.GetBytes(json);
try
{
using (var requestStream = request.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Close();
}
}
catch(Exception ex)
{
displayMessages(string.Format("Error creating Jira: {0}",ex.Message.ToString()), "red", "white");
Dictionary<string, string> excepHTTP = new Dictionary<string, string>();
excepHTTP.Add("error", ex.Message.ToString());
return excepHTTP;
}
response = (HttpWebResponse)request.GetResponse();
var reader = new StreamReader(response.GetResponseStream());
string str = reader.ReadToEnd();
var jss = new System.Web.Script.Serialization.JavaScriptSerializer();
var sData = jss.Deserialize<Dictionary<string, string>>(str);
if(response.StatusCode.ToString()=="NoContent")
{
sData.Add("code", "NoContent");
request.Abort();
return sData;
}
else
{
sData.Add("code", response.StatusCode.ToString());
request.Abort();
return sData;
}
}
Thanks!
How about attempting to access the root page of JIRA and see if you get an HTTP 403 error?
try
{
// access JIRA using (parts of) your existing code
}
catch (WebException we)
{
var response = we.Response as HttpWebResponse;
if (response != null && response.StatusCode == HttpStatusCode.Forbidden)
{
// JIRA doesn't like your credentials
}
}
The HttpClient would be simple and best to use check credentials with GetAsync.
The sample code is below
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(JiraPath);
// Add an Accept header for JSON format.
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string mergedCreds = string.Format("{0}:{1}", username, password);
byte[] byteCreds = UTF8Encoding.UTF8.GetBytes(mergedCreds);
var authHeader = new AuthenticationHeaderValue("Basic", byteCreds);
client.DefaultRequestHeaders.Authorization = authHeader;
HttpResponseMessage response = client.GetAsync(restURL).Result; // Blocking call!
if (response.IsSuccessStatusCode)
{
strJSON = response.Content.ReadAsStringAsync().Result;
if (!string.IsNullOrEmpty(strJSON))
return strJSON;
}
else
{
exceptionOccured = true;
// Use "response.ReasonPhrase" to return error message
}
}
Related
I have 2 programs on my localhost, one with REST API and another program that calls the api.
I'm trying to authenticate user(https://localhost:44301/token) with below function and getting badRequest.
the same is working when testing with postman.
baseUrl is "https://localhost:44301/";
static async Task<Uri> AddCartRecordAsync(CartsTable cartsTable)
{
string ResponseString = "";
HttpWebResponse response = null;
var request = (HttpWebRequest)WebRequest.Create(Utility.baseUrl + "token");
request.Accept = "application/json";
request.Method = "POST";
//Get credentials from config.
var username = "kkk#gmail.com";
var password = "Test123!";
Credentials cred = new Credentials()
{
username = username,
password = password,
grant_type = "password"
};
var myContent = JsonConvert.SerializeObject(cred);
var data = Encoding.ASCII.GetBytes(myContent);
request.ContentType = "application/json";
request.ContentLength = data.Length;
using (var stream = request.GetRequestStream())
{
stream.Write(data, 0, data.Length);
}
try
{
using (response = (HttpWebResponse)request.GetResponse())//BadRequest Here
{
ResponseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
}
}
catch (Exception e)
{
string i = "df";
}
The common issues are :
1-Params Body Type
2-Authentication Type : if u have bearer type you should include bearer keyword before your token in the header before sending request like "token":"bearer AuthToken"
3-method type : get put post delete
How would I convert this to HttpClient? What I'm looking to do is submit a Tweet to the Twitter api and get the response as Json. The HttpWebRequest is working fine but I just want to port it to HttpClient. I made an attempt at it in the second code example, but it's not actually sending or receiving the response.
HttpWebRequest request = null;
WebResponse response = null;
string responseCode = String.Empty;
try
{
string postBody = "status=" + EncodingUtils.UrlEncode(status);
request = (HttpWebRequest)HttpWebRequest.Create(resource_url);
request.ServicePoint.Expect100Continue = true;
request.UseDefaultCredentials = true;
request.PreAuthenticate = true;
request.Credentials = CredentialCache.DefaultCredentials;
request.ServicePoint.ConnectionLimit = 1;
request.Headers.Add("Authorization", authHeader);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (Stream stream = request.GetRequestStream())
{
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(postBody);
}
}
using (response = request.GetResponse())
{
response.ContentType = "application/json";
responseCode = ((HttpWebResponse)response).StatusCode.ToString();
}
}
catch (WebException ex)
{
if (ex.Status != WebExceptionStatus.NameResolutionFailure)
{
request.Abort();
request = null;
}
throw ex;
}
return responseCode;
This is what I've tried to get it work:
private async Task<string> MakeWebRequest1(string status, string resource_url, string authHeader)
{
HttpClientHandler clientHandler = new HttpClientHandler();
clientHandler.Credentials = CredentialCache.DefaultCredentials;
clientHandler.PreAuthenticate = true;
clientHandler.AllowAutoRedirect = true;
string responseCode = "";
string postBody = "status=" + EncodingUtils.UrlEncode(status);
var request = new HttpRequestMessage()
{
RequestUri = new Uri(resource_url),
Method = HttpMethod.Post,
};
request.Headers.Add("Authorization", authHeader);
// request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
request.Content = new StringContent(postBody, Encoding.UTF8,"application/x-www-form-urlencoded");//CONTENT-TYPE header
using (HttpClient client = new HttpClient(clientHandler))
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/x-www-form-urlencoded"));
// Stream stuff = await client.GetStreamAsync(resource_url);
using (HttpResponseMessage response = await client.SendAsync(request))
{
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
if(response.StatusCode == HttpStatusCode.OK)
{
responseCode = "OK";
}
}
}
clientHandler.Dispose();
return responseCode;
}
enter code here
I've tried to add another parameter to the request and it's always coming back as 401 unauthorized. I'm trying to create a Twitter thread. If I remove the in_reply_to_status_id then it's fine.
data = new Dictionary<string, string> {
["status"] = "#username + status,
["in_reply_to_status_id"] = "1167588690929115136"
};
The Twitter API describes it here https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/post-statuses-update
Reference You're using HttpClient wrong to understand why a static client is being used.
static Lazy<HttpClient> client = new Lazy<HttpClient>(() => {
HttpClientHandler clientHandler = new HttpClientHandler {
Credentials = CredentialCache.DefaultCredentials,
PreAuthenticate = true,
AllowAutoRedirect = true
};
return new HttpClient(clientHandler);
});
private async Task<string> PostStatusRequestAsync(string status, string resource_url, string authHeader) {
using (var request = new HttpRequestMessage(HttpMethod.Post, resource_url)) {
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
request.Headers.Accept.Clear();
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var data = new Dictionary<string, string> {
["status"] = status
};
request.Content = new FormUrlEncodedContent(data);
using (HttpResponseMessage response = await client.Value.SendAsync(request)) {
return response.StatusCode.ToString();
}
}
}
Note the use of the FormUrlEncodedContent for the request body, which will encode and concatenate the data as well as take care of the mime type header
...but it's not actually sending or receiving the response.
Ensure that the above is not invoked as a synchronous blocking call, like .Result, which could cause a deadlock.
For example, an async event handler can be used to make the async call
public async void onButtonClick(object sender, EventArgs args) {
//Non-blocking call
var tweetRequestCode = await PostStatusRequestAsync(TweetText, AuthUtils.GetResourceUrl(), AuthUtils.GetWebRequestHeader()));
//back on UI thread
//...
}
Reference Async/Await - Best Practices in Asynchronous Programming
I have looked up all the questions regarding basic authorization in web api using httpweb request and none of them solved my problem. I have a web api (written in C#) and I want to establish basic authorization for the api. I also have a web page that I am using to call the api. However, it keeps returning "(401)Unauthorized" and I don't know what I'm doing wrong. Right now I'm using the username and password in the code but I want there to be a pop up asking for credentials.
This is the code for my web page, calling the api:
string url = String.Format("http://example.com");
HttpWebRequest requestObj = (HttpWebRequest)WebRequest.Create(url);
requestObj.Method = "Get";
requestObj.Credentials = new NetworkCredential("testing", "123456");
HttpWebResponse responseObj = null;
responseObj = (HttpWebResponse)requestObj.GetResponse();
string strresult = null;
using (Stream stream = responseObj.GetResponseStream())
{
StreamReader sr = new StreamReader(stream);
strresult = sr.ReadToEnd();
sr.Close();
}
In my api, I opened a class called BasicAuthenticationAttribute and wrote this:
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public static bool IsAuthorizedUser(string Username, string Password)
{
return Username == "testing" && Password == "123456";
}
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
if (actionContext.Request.Headers.Authorization != null)
{
var authToken = actionContext.Request.Headers.Authorization.Parameter;
var decodeauthToken = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
var arrUserNameandPassword = decodeauthToken.Split(':');
if (IsAuthorizedUser(arrUserNameandPassword[0], arrUserNameandPassword[1]))
{
Thread.CurrentPrincipal = new GenericPrincipal(new GenericIdentity(arrUserNameandPassword[0]), null);
}
else
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
else
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
}
And in my controller, I have this:
[BasicAuthentication]
public class EventsController : ApiController
{
}
This is the error I'm receiving:
The remote server returned an error: (401) Unauthorized.
Try adding credentials to credential cache with type of authorization and instead of using credentials directly use cache.
string url = String.Format("http://example.com");
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri(url), "Basic", new NetworkCredential("testing", "123456"));
HttpWebRequest requestObj = (HttpWebRequest)WebRequest.Create(url);
requestObj.PreAuthenticate = true;
requestObj.Method = "Get";
requestObj.Credentials = **credentialCache**;
HttpWebResponse responseObj = null;
responseObj = (HttpWebResponse)requestObj.GetResponse();
string strresult = null;
using (Stream stream = responseObj.GetResponseStream())
{
StreamReader sr = new StreamReader(stream);
strresult = sr.ReadToEnd();
sr.Close();
}
As mentioned in the messages above, I believe the issue is because you are not removing "Basic" from the authentication header. https://learn.microsoft.com/en-us/aspnet/web-api/overview/security/basic-authentication
What I'd do...
string authToken = actionContext.Request.Headers.Authorization.Parameter;
authToken = authToken.Replace("Basic", string.Empty);
var decodeauthToken = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
And try this when you split. About the only thing different in your implementation than what I've done multiple times.
var arrUserNameandPassword = decodeauthToken.Split(':'c);
I have a web app hosted in azure. When I use postman to make the request I get a
json result, which is the correcet response. When I try to make the same request via C# using the same token I receive a errpr - The remote server returned an error: (401) Unauthorized.
here is the code I use to make the request.
public string RequestData(string queryString, string token)
{
var request = (HttpWebRequest)WebRequest.Create(queryString);
request.Proxy = GetProxy();
request.Credentials = CredentialCache.DefaultCredentials;
request.PreAuthenticate = true;
request.UseDefaultCredentials = true;
request.Method = "GET";
request.ContentType = "application/json";
request.ContentLength = 0;
request.CookieContainer = new CookieContainer();
request.Headers.Add("authorization", "Bearer " + token);
using (var webresponse = request.GetResponse())
{
if (webresponse.GetResponseStream() == Stream.Null)
{
throw new Exception("Response stream is empty");
}
var response = (HttpWebResponse)webresponse;
if (response.StatusCode != HttpStatusCode.OK)
{
return response.StatusCode.ToString();
}
else
{
return response.StatusCode.ToString();
}
}
}
I have double checked the token to ensure it is correct and it is.
Another point I wanted to mention is that it did not work initially in
Postman without enabling Interceptor. This goes for Advanced Rest Client.
The request did not work until I enabled "XHR" and installed ARC cookie exchange.
I have checked the request headers in Fiddler and noticed there are no additional headers except for the authorization one (which I add as well).
UPDATE:
I got a successfull response in Postman (https://www.getpostman.com/)
and ran the code it generated for c# using RestSharp. In the response
the error thrown was
"You do not have permission to view this directory or page."
Which points to the token not being correct. Which is confusing since it works
in Postman and Advanced Rest Client. Also I must mention I retrieve the token
on each call using the clientid and secret using the following code:
public async static Task<AzureAccessToken> CreateOAuthAuthorizationToken(string clientId, string clientSecret, string resourceId, string tenantId)
{
AzureAccessToken token = null;
var oauthUrl=string.Format("https://login.microsoftonline.com/{0}/oauth2/token", tenantId);
var reqBody = String.Format("grant_type=client_credentials&client_id={0}&client_secret={1}",clientId, clientSecret);
var 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;
}
return null;
}
}
after checking the log in azure, I saw the following error message:
JWT validation failed: IDX10214: Audience validation failed. Audiences: '00000002-0000-0000-c000-000000000000'. Did not match: validationParameters.ValidAudience: 'f50a9d02-b8f4-408f-aaf8-0046e6cbf7a6' or validationParameters.ValidAudiences: 'null'.
I resolved the issue by adding '00000002-0000-0000-c000-000000000000' to the "Allowed Token Audiences" under Azure Active Directory Settings.
I have called third party API. When I use postman to make the request I get a json result, which is the correct response. When I try to make the same request via C# using the same token I receive a error - The remote server returned an error: (401) Unauthorized. Finally I got the solution.
When I make the login request some cookies will send by the server and that cookie will store in postman. If you see code snippet you will see information about request that is raised by postman.
When I call the Login method I stored the cookies like below:
public ResponseData OnGetResponseFromAPI(string URL, string Method, string PostData = null, Dictionary<string, string> Headers = null, string body = null, string ContentType = "application/json")
{
ResponseData response = new ResponseData();
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var webRequest = (HttpWebRequest)WebRequest.Create(URL);
CookieContainer cookieJar = new CookieContainer();
webRequest.CookieContainer = cookieJar;
webRequest.Method = Method;
webRequest.ContentType = ContentType;
if (Method == "GET")
{
var type = webRequest.GetType();
var currentMethod = type.GetProperty("CurrentMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(webRequest);
var methodType = currentMethod.GetType();
methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentMethod, false);
}
if (Headers == null)
Headers = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> header in Headers)
{
webRequest.Headers.Add(header.Key, header.Value);
}
if (!string.IsNullOrEmpty(PostData))
{
var RequestStream = new StreamWriter(webRequest.GetRequestStream());
RequestStream.Write(PostData);
RequestStream.Close();
}
if (!string.IsNullOrEmpty(body))
{
byte[] byteArray = Encoding.UTF8.GetBytes(body);
webRequest.ContentLength = byteArray.Length;
Stream dataStream = webRequest.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
var ResponseStream = new StreamReader(webRequest.GetResponse().GetResponseStream());
string cookie = string.Empty;
CookieCollection allCookies = cookieJar.GetCookies(webRequest.RequestUri);
foreach (Cookie c in allCookies)
{
cookie = cookie + c.Name + "=" + c.Value+";";
}
cookie = cookie.Substring(0, cookie.LastIndexOf(';'));
var ResponseData = ResponseStream.ReadToEnd();
response.response=ResponseData.ToString();
response.cookie=cookie;
return response;
}
catch (WebException webException)
{
if (webException == null || webException.Response == null)
return null;
var responseStream = webException.Response.GetResponseStream() as MemoryStream;
if (responseStream == null)
return null;
var responseBytes = responseStream.ToArray();
var responseString = Encoding.UTF8.GetString(responseBytes);
response.response = responseString;
return response;
}
}
Whenever I am calling any api method I am sending token and cookie in header like below:
public string DownLoadDocument( string FilePath, string FileName, string token,string cookie)
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
HttpWebRequest webRequest;
webRequest = (HttpWebRequest)WebRequest.Create(URL);
webRequest.Method = "GET";
webRequest.ContentType = "application/octet-stream;charset=UTF-8";
webRequest.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.0.1) Gecko/2008070208 Firefox/3.0.1";
webRequest.Headers.Add("Cookie", cookie);
webRequest.Headers.Add("Authentication", "Bearer "+token);
webRequest.Headers.Add("Content-Disposition", "attachment");
Stream responseReader = webRequest.GetResponse().GetResponseStream();
using (var fs = new FileStream(FilePath, FileMode.Create))
{
responseReader.CopyTo(fs);
}
}
catch (Exception ex)
{
throw;
}
return FilePath;
}
I need to implement last.fm authentication in my Windows Phone 8 application. Last.fm requires a POST request with https for successful login. Here is what I'm doing.
public async void GetMobileSession(string userName, string password, Action<LastFmAuthResponse> onCompletion)
{
string CORE_URL = "https://ws.audioscrobbler.com/2.0/";
var parameters = new Dictionary<string, string>();
parameters.Add("username", userName);
parameters.Add("password", password);
parameters.Add("method", "auth.getMobileSession");
parameters.Add("api_key", api_key);
string signature = GetSignature(parameters);
string comboUrl = string.Concat("method=auth.getMobileSession", "&api_key=", api_key,
"&username=", userName, "&password=", password, "&api_sig=", signature);
LastFmAuthResponse response = null;
byte[] pendingPostContent = Encoding.UTF8.GetBytes(comboUrl);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(CORE_URL);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
using (Stream requestStream = await request.GetRequestStreamAsync())
{
await requestStream.WriteAsync(pendingPostContent, 0, pendingPostContent.Length);
}
request.BeginGetResponse(new AsyncCallback(n =>
{
HttpWebResponse rawResponse = (HttpWebResponse)request.EndGetResponse(n);
string rawData = string.Empty;
using (StreamReader reader = new StreamReader(rawResponse.GetResponseStream()))
{
rawData = reader.ReadToEnd();
}
try
{
if (!string.IsNullOrEmpty(rawData))
{
response = CommonFunctions.GetObjectFromString<LastFmAuthResponse>(rawData);
}
}
catch
{
}
onCompletion(response);
}), null);
}
However the code is failing at request.BeginGetResponse It returns an error saying remote server could not be found. Can someone please point out what I'm doing wrong here?