For the past 5 hours I've been trying to get JSON data from an API that requires login using .net C#.
Every combination I try results in "error 401 - unauthorized".
I have a working example in Javascript, maybe this would help.
function signin(){
username = $('#inputUsername').val();
pass = $('#inputPassword').val();
if(pass.length > 0 && username.length > 0){
//Build post request
$.post( source+"/login", {username: username, password:pass}, function( data, textStatus, jqxhr ) {
$("header div h4#header_username").html(username);
token = data.auth;
$('#main-content').show()
$('#form-signin').hide()
populateVehicles();
});
}
}
On c# I've tried many code combinations including:
NetworkCredential netCredential = new NetworkCredential("USERNAME", "PASSWORD");
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Credentials = netCredential;
request.Method = "POST";
String encoded = System.Convert.ToBase64String(System.Text.Encoding.GetEncoding("ISO-8859-1").GetBytes("USERNAME" + ":" + "PASSWORD"));
//request.PreAuthenticate = false;
//request.Proxy.Credentials = CredentialCache.DefaultCredentials;
//request.UseDefaultCredentials = true;
//string base64Credentials = GetEncodedCredentials();
//request.Headers.Add("Authorization", "Basic " + base64Credentials);
try
{
WebResponse response = request.GetResponse();
using (Stream responseStream = response.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, System.Text.Encoding.UTF8);
return reader.ReadToEnd();
}
}
catch (WebException ex)
{
WebResponse errorResponse = ex.Response;
using (Stream responseStream = errorResponse.GetResponseStream())
{
StreamReader reader = new StreamReader(responseStream, System.Text.Encoding.GetEncoding("utf-8"));
String errorText = reader.ReadToEnd();
// log errorText
}
throw;
}
}
Some of the code has "//" because I've tried various variations.
I'm clueless.
Thanks in advance!
Using doetnet core 2.1, here's a basic example. I checked jquery docs and I think your js example sends form encoded data. You can easily check that in your browsers developer tools > network tab.
Form Encoded
using (var client = new HttpClient())
{
var response = await client.PostAsync("https://myaddress.com/path", new FormUrlEncodedContent(new Dictionary<string, string>()
{
{ "username", "user" },
{ "password", "mypass" }
}));
if (response.StatusCode == HttpStatusCode.OK)
{
var responseString = await response.Content.ReadAsStringAsync();
// do something
}
}
"form url encoded" means, it will generate a request that looks like this:
say=Hi&to=Mom
with header Content-Type: application/x-www-form-urlencoded
s.a. https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST
Json Content
using (var client = new HttpClient())
{
var response = await client.PostAsync("https://myaddress.com/path", new StringContent(JsonConvert.SerializeObject(new
{
username = "user",
password = "password"
}), Encoding.UTF8, "application/json"));
if (response.StatusCode == HttpStatusCode.OK)
{
var responseString = await response.Content.ReadAsStringAsync();
// do something
}
}
"json" means, it will generate a request, that looks like this
{ "say": "Hi", "to": "Mom" }
with header Content-Type: application/json
Note that HttpClient is thread safe and you should prefer a single instance if you do multiple requests.
If your're on dotnet core the following article will help you getting started with HttpClient: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.1
See https://stackoverflow.com/a/4015346/2085502 mentioned by #Archer to get an overview over different http client libs in c#.
To troubleshot if your backend working properly, check it by try sumbit the form with real usename and password and wrong email/password by clicking inside the . username and password must be inside the form too. Also in your form must be have action="" attribute with value of login url.
Try to set up, if login success some word. example, simply print 'success'
Then if failed, print another message. example 'failed', 'wrong password', 'email not exist' aor etc.
So after that. use this Javascript code.
function signin(){
var formData = new FormData();
formData.append('userame', document.getElementById('inputUsername').value);
formData.append('password', document.getElementById('inputPassword').value);
var xhr = new XMLHttpRequest();
xhr.open('POST', 'https://www.yoursite.com/login-url-here', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4){
var data = xhr.responseText;
if(data){
if(data == 'success'){
// yes your login page return success, do something below
document.getElementById('main-content').style.display = 'block';
document.getElementById('form-signin').style.display = 'none';
populateVehicles();
alert('login success')
} else {
alert('ops, failed to login, the page print this messages '+ data)
}
} else {
alert('ops your login page not print anything')
}
}
}
xhr.send(formData);
}
Related
Currently, I am trying to use the bcrypt module to compare the hashed password in the db to the password in the request params. My code for the backend is:
router.get('/:username', getAcc, async (req, res) => {
const user = await Account.find(req.body.username)
console.log(req.password)
const check = bcrypt.compare(password, user[0].password);
if (check == false) return res.json('Incorrect Password');
res.json(res.acc)
})
And I do request by:
const url = `http://localhost:3000/config/${username}`
const request = new XMLHttpRequest()
request.open("GET", url+"?password="+password, true);
request.send()
request.onload = function(){
let jsonResponse = `${request.response}`;
if (jsonResponse == 'Invalid password') return alert('Invalid password');
alert(jsonResponse)
let parsedRes = JSON.parse(jsonResponse);
}
Now I want to add a password field to this, either via req.password or a different field, and be able to send a request with the password paramater. Is this possible, and how to do it?
You should not be putting a user's password in the request URL, instead you should put it in the request headers. The reason for this is because putting passwords, and other sensitive information in the headers is considered more secure and safer.
To do exactly what you want but using headers, use the following code for the client side (I have included comments for the lines changed):
const url = `http://localhost:3000/config/${username}`
const request = new XMLHttpRequest()
request.open("GET", url, true); // Got rid of '+ "?password=' part
request.setRequestHeader('password', password) // This is the part where a new header is set
request.send()
request.onload = function(){
let jsonResponse = `${request.response}`;
if (jsonResponse == 'Invalid password') return alert('Invalid password');
alert(jsonResponse)
let parsedRes = JSON.parse(jsonResponse);
}
And use this for backend:
router.get('/:username', getAcc, async (req, res) => {
const user = await Account.find(
req.body.username,
req.headers['password'] // This will get the password from the headers
)
const check = bcrypt.compare(req.headers['password'], user[0].password); // Same thing - gets the password from headers
if (check == false) return 'Incorrect Password'
res.json(res.acc)
})
If you encounter any issues with my answer, feel free to leave a comment.
I have this part of code:
document.querySelector('#form_pizza_order').onsubmit = () => {
// make an ajax request to save the pizza order in the server
const request = new XMLHttpRequest();
request.open('POST', '/order_pizza');
// Callback function for when request completes
request.onload = () => {
const data = JSON.parse(request.responseText);
if (data.success) {
// show in cart new order
show_in_cart(data);
}
else {
alert('failed to save pizza order in server');
}
}
const data = new FormData();
let username = localStorage.getItem('username');
data.append('username', username);
//Send request
request.send(data);
return false;
};
that when used the server returns 403 forbidden response because of csrf_token not sent. how do I add the crsf_token header properly with the javascript above, without using jquery. just javascript.
thanks.
function sendData(){
const XHR = new XMLHttpRequest();
// Set up our request
XHR.open("POST", "{% url 'test:index' %}" );
XHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
// Bind the FormData object and the form element
let FD = new FormData(form);
// append the token
FD.append('csrfmiddlewaretoken', '{{ csrf_token }}');
// The data sent is what the user provided in the form
XHR.send(FD);
}
let form = document.getElementById('<form_id>')
// take over its submit event.
form.addEventListener("submit", function (event) {
console.log('Submited!')
event.preventDefault();
sendData();
})
In your django views, you can test if the request is ajax:
def index(request):
if request.is_ajax() and request.method='POST':
print(request.POST)
# process post data
Django use X-Requested-With to detect an ajax request, take a look of How django detect ajax request
the sendData function is originated from Mozilla Docs
the following code made it happen:
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie !== '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function sendData(){
const XHR = new XMLHttpRequest();
// Set up our request
var csrftoken = getCookie('csrftoken');
XHR.open("POST", "/order_pizza" );
XHR.setRequestHeader('X-Requested-With', 'XMLHttpRequest')
XHR.setRequestHeader('X-CSRFToken', csrftoken)
// Bind the FormData object and the form element
let FD = new FormData();
// append the token
FD.append('csrfmiddlewaretoken', csrftoken);
let username = localStorage.getItem('username');
FD.append('username', username);
// The data sent is what the user provided in the form
XHR.send(FD);
}
obviously we had to retrieve the csrf cookie first before we could use it as a form data.
So, I have a JS script that sends a request to the server and that works fine. However, I want the frontend to recieve a response containing some information and read it on the frontend and execute a function.
uploadButton.addEventListener("click", () => {
var formData = new FormData();
var request = new XMLHttpRequest();
request.open("POST", "/Page/Upload");
request.send(formData);
if (request.response.result == "Success") {
console.log("Result is success")
window.location = request.response.url;
}
}
My controller looks like this.
[HttpPost("/page/upload")]
public IActionResult Upload()
{
*working parts pertaining to reading the request are omitted*
var redirectUrl = Request.Host + "/" + page.PageURL;
return Json(new { result = "Success", url = redirectUrl});
}
What I want is for my JS script to access the returned Json and its contents. How would I do this?
Try using the following code. It will subscribe the readystatechange event and run when API response has been received
uploadButton.addEventListener("click", () => {
var formData = new FormData();
var request = new XMLHttpRequest();
request.open("POST", "/Page/Upload");
request.send(formData);
request.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var responseData = JSON.parse(this.responseText);
if (responseData.result == "Success") {
console.log("Result is success")
window.location = responseData.url;
}
}
});
});
In my backend I authenticate user once inlogging and then store the authenticated sessions at the server. Then after each user's request i check if the session associated with a request is stored as authenticated. The problem is that when I use JavaScript requests a new HTTP session is used each time i send something to my server written in Java.
When I use Postman everything is okay because it stores session through many requests.
//Here is authentication on server side - it works fine
#CrossOrigin
#RequestMapping(value= "/login", method = RequestMethod.POST)
public #ResponseBody String login(#RequestBody Account retrievedAccount,
HttpServletRequest httpServletRequest) {
if (retrievedAccount != null) {
Account account =
accountDAO.getAccountByLogin(retrievedAccount.getLogin());
if (account != null &&
account.getPassword().equals(retrievedAccount.getPassword())) {
this.registeredSessionsContainer.add(httpServletRequest.getSession());
return new ResponseEntity(HttpStatus.OK).toString();
} else {
return new ResponseEntity(HttpStatus.UNAUTHORIZED).toString();
}
} else {
return new ResponseEntity(HttpStatus.UNAUTHORIZED).toString();
}
}
Here is a simple way to check if a session is already authenticated:
#CrossOrigin
#RequestMapping(value= "/checkLogon", method = RequestMethod.GET)
public #ResponseBody String checkLogon(HttpServletRequest
httpServletRequest) {
if(this.registeredSessionsContainer.
contains(httpServletRequest.getSession()))
return new ResponseEntity(HttpStatus.OK).toString();
} else {
return new ResponseEntity(HttpStatus.UNAUTHORIZED).toString();
}
Here is how i login to service in my frontend JavaScript:
performLoggingToService(){
var login = document.getElementById("loginField").value;
var password = document.getElementById("passwordField").value;
var url = "http://localhost:8080/mvc1/login";
var method = "POST";
var crendentialsObject = { "login": login, "password": password };
var crendentialsObjectJSON = JSON.stringify(crendentialsObject);
console.log(crendentialsObjectJSON);
var req = new XMLHttpRequest();
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "application/json");
req.send(crendentialsObjectJSON);
//console.log("Is this undefined: "+(loginComponent==undefined));
var props = this.props;
var thisObjectPointer = this;
req.onload = function (e,thisObject=thisObjectPointer) {
var status = req.status; // HTTP response status, e.g., 200 for "200 OK"
var data = req.responseText; // Returned data
if(data.includes("200 OK")){
console.log("Checking LOGON STATE METHOD#2: ");
thisObject.props.refreshLogonStateInMainBand(login);
} else {
// inform user about wrong credentials
}
}
}
An then when i perform check if i am already logged in one address /checkLogon I use:
checkLogonState(currentUserName) {
console.log("CheckLogonState CALLED!");
var url = "http://localhost:8080/mvc1/checkLogon";
var method = "GET";
var req = new XMLHttpRequest();
var loginData;
req.overrideMimeType("application/json");
req.open('GET', url, true);
req.onload = function() {
}
req.send();
req.onreadystatechange=(e)=>{
if(req.readyState === 4 && req.responseText.length>0) {
if(req.responseText.includes("200 OK")){
console.log("Authenticated!!!");
this.changeMainComponentStateToLogin();
this.currentUserName = currentUserName;
this.oneTimeLogonCheckAction=false;
} else {
console.log("Not logged in!!!")
this.changeMainComponentStateToIdle();
this.currentUserName=undefined;
this.oneTimeLogonCheckAction=true;
}
this.forceUpdate();
}
}
}
As you may expect responseTest includes 404 Unauthorized not 200 OK.
I tried it on InternetExplorer, Microsoft Edge and Chrome. None of them reuses session.
After each of my requests console on server side shows that the requests are sent from other sessions - each request in a new session.
I would like to get to know how can I use same session if i use one the same browser window through many requests.
Set withCredentials to true for all XMLHttpRequest,
var req = new XMLHttpRequest();
req.withCredentials = true;
req.open("POST", url, true);
req.setRequestHeader("Content-Type", "application/json");
req.send(crendentialsObjectJSON);
will help to persist the session across calls.
At server side add this to all your controllers to solve cors issues,
#CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")
To test FOSUser, I'm simulating an AJAX request that send a JSON data containing the username, his email and password.
JavaScript side (far-from-perfect ajax request, but I'll use Angular in real development):
function ajaxPost(url, data, callback, isJson) {
let req = new XMLHttpRequest();
req.open("POST", url);
req.addEventListener("load", function () {
if (req.status >= 200 && req.status < 400) {
callback(req.responseText);
} else {
console.error(req.status + " " + req.statusText + " " + url);
}
});
req.addEventListener("error", function () {
console.error("Network error at " + url);
});
if (isJson) {
req.setRequestHeader("Content-Type", "application/json");
data = JSON.stringify(data);
}
req.send(data);
}
// Simulates an AJAX request to create a user from a form:
user = prompt("Username:");
email = prompt("Email:");
password = prompt("Password:");
data = { user, email, password };
ajaxPost("http://domain.tld/app_dev.php/createuser", data, console.log, true);
Symfony side:
/**
* #Route("/createuser", name="createUser")
*/
public function createUserAction($data, UserManager $userManager)
{
$data = json_decode($data);
if (!$userManager->findUserByUsername($data->username) && !$userManager->findUserByEmail($data->email)) {
$user = $userManager->createUser();
$user->setUsername($data->username);
$user->setEmail($data->email);
$password = password_hash($data->password, PASSWORD_DEFAULT);
$user->setPassword($password);
$userManager->updateUser($user);
return new Response("<html><body>New user created!</body></html>");
} else {
return new Response("<html><body>This user or email already exists.</body></html>");
}
}
When I directly go to /createuser and pass a mock-up data, it works; but when I send my ajax request, it always returns a 500 internal server error:
[2017-06-17 08:31:49] request.CRITICAL: Uncaught PHP Exception
Symfony\Component\Debug\Exception\ContextErrorException: "Notice:
Trying to get property of non-object" at
C:\wamp64\www\MyWebsite\src\AppBundle\Controller\DefaultController.php
line 50 {"exception":"[object]
(Symfony\Component\Debug\Exception\ContextErrorException(code:
0): Notice: Trying to get property of non-object at
C:\wamp64\www\MyWebsite\src\AppBundle\Controller\DefaultController.php:50)"}
[]" That's: if (!$userManager->findUserByUsername($data->userna
Any idea? :)
Ok I'm stupid. $data is nothing; I must use $request->getContent()...
public function createUserAction(Request $request, UserManager $userManager)
{
$data = json_decode($request->getContent());
Thanks all for your comments :)