I am trying to develop a two-tier web application with MarkLogic-9 employing server side JavaScript and HTTP app servers. I have a simple page that prompts for username/password and sends a GET request via Ajax to the app server (application-level authentication).
My login.sjs script:
//generate object with field names from Request params
var params ={}; //JSON parsed URL parameters
var field_names = xdmp.getRequestFieldNames().toArray();
for(var fname_idx in field_names){
params[field_names[fname_idx]] = String(xdmp.quote(xdmp.getRequestField(String(field_names[fname_idx]))));
}
//get username and password from passed paramters
var username = params.username;
var password = params.password;
var ret = xdmp.login(username,password);
ret;
I have tested this and verified that it works by printing the xdmp.currentUser().
The login page then redirects to a home page that displays basic user info. My problem is that I cannot figure out how to preserve the current user's session after the client-side redirect to the homepage.
The app server has application-level authentication and a default user called Login-User, which is a custom user that has only the privileges necessary to log in (xdmp:login). The app server is hosted on localhost:8601. I have found that when I run login.sjs directly from the browser (i.e. typing localhost:8601/login.sjs?username=test_user&password=test_password), my browser gets a cookie with the sessionID. However, when I run the login.sjs via an Ajax GET request, my browser does not get any cookies. I don't know if this is the issue but I though it might be worth mentioning.
I am still a MarkLogic novice so I may be going about this the completely wrong way. Basically, how do I go about continuing a single user's session after redirecting to a new page? Do I use cookies to save the sessionID? Should I preserve the username and password in local storage and log in every time the website invokes a new .sjs file?
For completeness, here is the client side js I use to make the Ajax call to login. Pretty self-explanatory. The login.sjs file just returns true/false if the login was successful.
function createLoginEar(){
$("#login-button").click(function(event){
var un = $("#username").val();
var pw = $("#password").val();
if(un){
params.username = $("#username").val();
}
if(pw){
params.password = $("#password").val();
}
event.preventDefault(); //prevent form from clearing
console.log("input entered");
$.ajax({
type: "GET",
url: url,
data: params,
success: function(data){
if(data == "true"){
console.log("worked");
window.location.href = "homepage.html";
} else{
invalidLogin();
}
},
error: function(data){
invalidLogin();
}
})
})
}
The problem is that once the page redirects to homepage.html, there seems to be no memory of the user having logged in and when homepage.html calls any .sjs file, the user resets to the default which is "Login-User".
Thanks in advance.
I suggest you look at Chapter 15 of the security guide.
There is a sample of application level authentication using Custom Login Pages.
Lastly, the sample of IP-based login is not what you need, but shows you how to use xdmp.Login to switch users from the default application user.
I think that with all of that covered (not much to it really), you will be able to walk backthrough your setup and re-work it.
The issue was that my browser was not collecting cookies from the login because of issues that are over my head, but I found the answer in another post so this may be a duplicate.
Get and store cookie (from Set-Cookie) from an AJAX POST response.
I just had to include the following line in my ajax request:
xhrFields: { withCredentials: true },
Since this will throw an error if you have a wildcard in you Access-Control-Allow-Origin header, I also had to change this line:
xdmp.addResponseHeader('Access-Control-Allow-Origin', '*');
to this:
xdmp.addResponseHeader('Access-Control-Allow-Origin', 'http://localhost:8010');
And now my browser collects cookies.
Related
On my site, I have some jquery which is making a post request from my login form to my API. Here is the jquery:
function doLogin()
{
var userEmail = $("#email").val();
var userPassword = $("#password").val();
$.post(
"https://api.linkenfest.co.uk/access/login/<?= $referrer ?>",
{
email: userEmail,
password: userPassword
}
).done(function( data ){
var status = data.data.status;
var referrer = data.data.referrer;
var message = data.data.message;
var session = data.data.session;
alert( message );
if( status == 200 ){
window.location.replace( referrer + "?session=" + session );
}
}
);
On returning a success, the response from my api looks like this:
{
"status": 200,
"data":{
"status": 200,
"referrer": "page.php",
"message": "Successful login",
"session": "o3vo1uram0k2mojmbd45pmicr2"
}
}
As you can see from my response, it includes the value obtained from php session_id()
I would like to find out if giving this information to the user opens up for a security vulnerability on the site.
The session ID value is used to start the session after window.location.reload
Thanks.
As others have already pointed out in comments, the client getting the session id is the standard way to manage sessions - how else could it send it in subsequent requests? But it does matter how the client receives it.
Note that the code above is very likely vulnerable.
"https://api.linkenfest.co.uk/access/login/<?= $referrer ?>" - This is reflected XSS if $referrer is the Referer header from the request. As it is in a Javascript context, it is not enough to simply html encode, you need javascript encoding.
window.location.replace( referrer + "?session=" + session ) - This technically is an open redirect to the referrer parameter. It is not easy to exploit though. The best practice would still be to validate it (preferably on the server - which might already be in place).
Weak session management, session id is available to Javascript. While in some cases this is acceptable, accepting this risk should be a conscious decision. A session id should normally (and traditionally) be set in an httpOnly cookie. One exception when this can't be done is when a token needs to be sent to a different origin. But in that case it's not a session id semantically, and a proper SSO solution should be in place.
A session id is sensitive information, and as such, should not be sent in the url. The problem with the url is that it gets logged in multiple places (the user's browser will remember it and save it to disk, it will get logged on intermediate proxies, and in the target server's logs. These are all places from where an attacker might be able to get it. It's best to not send sensitive information in the url, but either in a header (a cookie is also just a header), or the request body is also ok if needed.
The destination url (api.linkenfest.co.uk) is vulnerable to login csrf if it allows you to log in this way.
Also looking at this from a broader perspective, what I guess you are doing is using the api as an identity provider to log your user in. For this purpose you should use a standard protocol like OpenID Connect, and not reinvent the wheel, because as you can see above, it is not at all straightforward.
I am trying to develop a web-app based on girder platform (a data management platform). During development, I get some problem confused me for a long time. Let me briefly explain my work and if anything I understand worry, please point it out coz I just start.
The thing is that,
In the font end I am using AMI(a javascript library) to deal with image virtualization, and the way is to tell (make a request to girder server) AMI the URL address which contains image to display as attachment. (e.g.http://girderDomain:port/api/v1/file/imageID/download?contentDisposition=attachment API of girder)
When this URL does not have any permission, everything works fine. When it needs permission (which is a token generated when authorized user login), purely URL does not work, so I was trying to use ajax to make a request with requestHeader(token) something like below:
$.ajax({
type:'GET',
url:'http://girderDomain:port/api/v1/file/imageWithPermissionID/download?contentDisposition=attachment',
crossDomain:true,
processData: false,
beforeSend:function(xhr){
// xhr.setRequestHeader("Access-Control-Allow-Origin:", "*");
// xhr.setRequestHeader("girderToken", token);
},
success:function (d,s,xhr) {}
});
Although I still get some error not solving yet, but ajax is the only way that in my mind.
and the whole process is like pseudocode below:
//***AMI***//
var t2 = ["imageID",....]; //////No permission need image
files = t2.map(function(v) {
return 'http://girderDomain:port/api/v1/file/' + v+'/download?contentDisposition=attachment';
});
AMI.display(files)
As you can see no permission image display is simple and easy to do, and with permission image through ajax request would be something like:
//***ajax to download permission file first***//
$.ajax({
//url:'http://girderDomain:port/api/v1/file/imageWithPermissionID/download?contentDisposition=attachment'
//set requestHeader("girderToken",token);
//success:function(){
//download this permission file to request domain tempFolder as A
}
});
/***AMI***/
var t2 = ["A"];
files = t2.map(function(v) {
return 'http://requestDomain/tempFolder/A';
});
AMI.display(files);
and that looks stupid, so I am wondering does anyone have any idea to request a file with permission, like maybe save token in cookie or session and using some way to make request with cookie or session, or any other new methods, frameworks.
Since I just start that kind of client-server development, any help truly appreciated.
I am using the request package to create my server side requests. I wrote authentication middleware that checks for a cookie/session id for all requests. Therefore, is there a way I include the user's cookie as part of the request? Here is my current code:
var cookie = parseCookie.parseCookie(req.headers.cookie);
request('http://localhost:3000/users/api', function(error, response, body) {
console.log(body); //this console.logs my login page since requests w/o valid cookies get redirected to login
res.render('../views/admin');
});
Currently, this returns 'no cookie found' in the console. However, if I turn off my authentication middleware, the code above works as intended.
Additional info:
The cookie I want is the end user's cookie located on the browser. The end user's cookie is created by the app whenever the user logs in.
Update - solution attempt 1:
I tried this from the documentation:
var cookie = parseCookie.parseCookie(req.headers.cookie);
var cookieText = 'sid='+cookie;
var j = request.jar();
var cookie = request.cookie(cookieText);
var url = 'http://localhost:3000/users/api';
j.setCookie(cookie, url);
request({url: url, jar: j}, function(error, response, body) {
request('http://localhost:3000/users/api');
});
However, the console is still returning 'no cookie found'
Can someone help?
Thanks in advance!
Let me explain about cookies and that will probably show you why it's hard to get the cookie you want.
When your user's browser logs into http://localhost:3000, that server creates a login cookie and returns it as part of the login response.
When the browser receives that cookie, it saves that cookie persistently within the browser and it associates that cookie with the http://localhost:3000 domain and port.
When the user again makes a request to http://localhost:3000, the browser sends all cookies it has previously saved for that particular domain and port with the request to the server.
When the server receives the request, it can examine any cookies that are sent with the request.
When the browser then makes a request to a different server or even the same server, but on a different port, the browser does NOT send the previously saved cookies with that request because those cookies belong to a different server and port. The browser goes to great security lengths to send cookies only to the servers that the cookies belong to. Since cookies often provide login access, you can clearly see why it's important that things like login credential cookies are not sent to servers they should not be sent to.
Now, on to your node.js code. You show a block of node.js code that is trying to access the same http://localhost:3000 server. But, the cookies are stored in the user's browser. Your node.js code cannot get them from the browser as the browser guards them and will only reveal them when the browser itself sends a request to http://localhost:3000.
If you do actually have the right cookie in your node.js code, then you can set it on your request like this:
request({url: 'http://localhost:3000/users/api', headers: {Cookie: somedataHere}}, function(error, response, body) {
console.log(body); //this console.logs my login page since requests w/o valid cookies get redirected to login
res.render('../views/admin');
});
Relevant documentation for custom headers in the request module is here.
Answer:
var cookie = parseCookie.parseCookie(req.headers.cookie);
var cookieText = 'sid='+cookie;
var options = {
url: 'https://api.github.com/repos/request/request',
headers: {
'User-Agent': 'request'.
'host': 'localhost:3000',
'cookie': cookieText //this is where you set custom cookies
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
var info = JSON.parse(body);
console.log(info.stargazers_count + " Stars");
console.log(info.forks_count + " Forks");
}
}
request(options, callback);
I understand that this question was asked plenty of times before but it seems that none of the responses worked for me so here's my issue:
I have a Sony IP camera that is in the intranet. I am building a site with PHP/MySQL authentication for internal users to be able to view the MJPEG stream but the camera itself requires http authentication. I do not want to have the users enter their username and password to log into the camera page and then have to type the http authentication credentials (in the popup) to see the stream. I tried using jQUERY to change the headers to no avail. Keep in mind that the camera MUST have its own authentication so that users cannot just randomly type the IP and see the stream. I want to be able to control who views what and when.
I am assuming that if I make a correct authentication call when the user logs into the page, that camera will be available to them since they would have "silently" logged in. Also, if I use wget from the terminal with the --headers: "Authorization: blah_Blah" it actually works but I can't do this from jQuery! Here's my code:
$.ajax({
url : "http://some_ip_internally_for_the_cam/some_page_on_cam_that_needs_authentication_to_access_otherwise",
method : 'GET',
beforeSend : function(req) {
req.setRequestHeader('Authorization', "some_base_64_stuff_that_works_in_wget");
},
success: function() {
$("div.log").attr("innerHTML", "ok");
}
});
This gets loaded as soon as the user logs in.
Any suggestions?
I had the same Javascript error in Firefox:
... 0x804b000f (NS_ERROR_IN_PROGRESS) [nsIXMLHttpRequest.setRequestHeader] ...
As I was trying to set a new HTTP header in some XMLHttpRequest object:
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
The error occured because I was trying to set the HTTP header before the XMLHttpRequest.open() method.
Source:
http://www.webmasterworld.com/forum91/4542.htm
If you mean that div.log doesn’t display the “ok” message, it’s probably because your DOM code is incorrect. This:
$("div.log").attr("innerHTML", "ok");
sets a DOM attribute of the div, not its actual innerHTML. Instead, you should do:
$("div.log").html("ok");
Have you tried the username and password fields in .ajax?
$.ajax({
url: "http://mydomain/mypage.php",
username: "myUsername",
password: "myPassword",
error: function () {
}
}).done(function(html) {
//Do Stuff
});
This works for me in a similar scenario.
I'm building a website that makes use of Facebook connect. I'm authenticating users client-side with the javascript SDK and calling an AJAX method on my server every time a user logs in to check if the user is known to my app, and if the user is new to store their FBID in my database to register them as a new user.
My question is: Can the access token returned by Facebook to the Javascript SDK be used server-side (with the PHP SDK for example)? Can I send the access token string to the server via an AJAX call, store it in my database (along with a timestamp so I know how long it's valid for) and then use it to make calls to the graph API server-side? Is this even a logical thing to do?
Yes, this should work. Look at this question: How to properly handle session and access token with Facebook PHP SDK 3.0?
This is a workaround for the old JS and new PHP SDK. In my app I send the access token generated by the JS SDK via a form to my PHP. I have no doubts that this also works by sending the access token via ajax!
Using Jquery:
//Set an error message
var oops = ("Put your something went wrong message here.");
//Function to post the data to the server
function save(uid, accessToken){
$.post("../foo/bar", { uid: uid, access_token: accessToken, etc, etc }, function(data){
alert("Successfully connected to Facebook.");
location.reload();
}, "text");
}
function handler(x){
if (x.authResponse){
var token = x.authResponse.accessToken;
var uid = x.authResponse.id;
FB.api("/me/accounts", {access_token: token},
function(response){
if(response.data.length == 0) {
//Regular facebook user with one account (profile)
save(uid, token);
}else{
//Handle multiple accounts (if you want access to pages, groups, etc)
}
});
}else{
alert(oops);
}
}
FB.login(handler, {scope: 'The list of permissions you are requesting goes here'});
Any improvement suggestions are always appreciated.