I'm using node server built on https://github.com/passport/express-4.x-local-example(just changed app.get('/login'... to app.post('/login' … in server.js.
In pug, I created a page with a login form based on https://www.w3schools.com/howto/howto_css_login_form.asp and when I submit form (input names changed to username and password, method="post", action "/login") everything works fine. Since I don't want to send passwords in a body without authentification, I need to add basic auth to my post request.
How do I do that?
I tried adding event listener submit to my form and stopping default action with event.preventDefault();. I then created new XMLHttpRequest(), set request header to basic auth and sent the XML request to server. When using console I can verify the request came in, did the job, but the reply from server (which should redirect) returned to my XML request, not actually redirecting the page.
When I try sending the same POST request via POSTMAN, the response is a redirect page.
If I remove event listener the form gets submitted and everything works fine (but without adding auth headers).
doctype html
head
meta(charset='UTF-8')
title Login
link(rel='stylesheet', href='stylesheets/main.css')
link(rel='icon', type="image/png", href='favicon.png')
body
form(action='/login', id='form1' method='post')
.imgcontainer
img.avatar(src='images/img_avatar2.png', alt='Avatar')
.container
label(for='username')
b Username
input(type='text', placeholder='Enter Username', name='username', autofocus, required, id='uname')
label(for='password')
b Password
input(type='password', placeholder='Enter Password', name='password', required, id='pword')
button(type='submit') Login
script.
document.getElementById("form1").addEventListener("submit", submitFunction);
function submitFunction() {
event.preventDefault();
var usr=document.getElementById('uname').value;
var pwd=document.getElementById('pword').value;
var obj = {'username' : usr, 'password' : pwd};
var request = new XMLHttpRequest();
request.open("POST", "/login", false);
request.setRequestHeader('Authorization', 'Basic Y2xpZW50SUQ6c2VjcmV0S2V5');
request.setRequestHeader('Content-Type', 'application/json');
request.send(JSON.stringify(obj));
}
Authentication is not needed and will not make your request secure as without encryption the HTTP request is still plain text.
Encryption will make your request secure, your page and API should both use HTTPS, and when using encryption you do not need the additional authentication.
I found a workaround to include headers. First of all, I was using the wrong passport strategy. I used local and should have used ('passport-http').BasicStrategy. Here is an example
https://github.com/passport/express-3.x-http-basic-example
I added a placeholder for response in my XMLHttpRequest so the script part of my pug looks now like
script.
document.getElementById("form1").addEventListener("submit", submitFunction);
function submitFunction() {
event.preventDefault();
var username = document.getElementById('uname').value;
var password = document.getElementById('pword').value;
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (this.readyState == 4) {
// Typical action to be performed when the document is ready:
// accept and redirect code in here based on the answer from the server
}
};
request.open("POST", "/login", false);
request.setRequestHeader('Authorization', "Basic " + btoa(username + ":" + password));
request.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
request.send();
}
of course, as Absor said ih his answer (thank you) it's still just plain text so maybe it will not add security to my request.
Related
I'm trying to using Airtable, node.js, express.js and jquery to create a simple user authentication functionality but I'm fairly new at this and I'm running into a problem I can't seem to fix and the articles I've read I can't seem to grasp or adapt to my particular situation.
I have this Ajax call in my html doc:
$("#checkUser").submit(function(e) {
var studentID = $('input[name="student"]').val()
e.preventDefault(); // avoid to execute the actual submit of the form.
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(), // serializes the form's elements.
success: function(data) {
$(window).attr("location", window.location.href + 'Dashboard?student=' + studentID);
},
error: function(data){
console.log("User not found. Try again");
}
});
});
This call sends the inputted username and data to the server which then processes it in the following way:
app.post('/checkUser', urlencodedParser, function(request,response){
var user = JSON.stringify(request.body);
user = JSON.parse(user);
base('RegisteredStudents').select({
filterByFormula: '{UserID} = ' + user.student,
view: "Grid view"
}).eachPage(function page(records, fetchNextPage) {
records.forEach(function(record) {
response.sendStatus(200);
});
fetchNextPage();
}, function done(error) {
response.sendStatus(404);
});
});
If the user exists in the database of Airtable, it should send '200' which the Ajax then reacts by redirecting accordingly to the user's profile. Otherwise, if the user does not exist, the server should respond with '404', which the Ajax call should react to by printing a statement in the console. While it does do these two things well, the server breaks down when, after a student puts in the wrong user ID and the Ajax prints the statement, the student tries to put once more a userID. I get the " Can't set headers after they are sent. " message. Please, how can I solve this?
Thank you!
You have two response.send..., you can only send data once. Either make sure only one runs with some conditional or add return before all response.send... so if any of them runs, the program will return and the other response.send.. will not run.
Im having trouble sending "POST" request to stripes token endpoint at the end of creating connect express account. Currently, my application(ios) is opening the OAuth Link (which contains a redirect uri to my server(Firebase functions)) to the express account creation web page made by stripe. Once the user completes their account set up I receive the authorization code in the backend and then use the authorization code and client_secret_key to send a POST request to "https://connect.stripe.com/oauth/token". I'm using XMLHttpRequest npm to send a POST request within firebase functions and setting my parameters like this
var params = "client_secret=" + CLIENT_SECRET + "&code=" + AUTHORIZATION_CODE + "&grant_type=authorization_code"
and my request header like this
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
Yet I get back this error after sending POST request
"error": {
"type": "invalid_request_error",
"code": "unsupported_content_type",
"message": "Invalid request: unsupported Content-Type text/plain;charset=UTF-8. If error persists and you need assistance, please contact support#stripe.com."
}
I thought I was properly setting request header content-type to "application/x-www-form-urlencoded" but still getting back error as if the content-type was "Text/plain".
Here is full backend code
exports.CreateUsersPayoutAccount = functions.https.onRequest((req, res) =>{
const userID = req.query.state
const AUTHORIZATION_CODE = req.query.code
console.log(userID)
console.log(AUTHORIZATION_CODE)
//console.log(req);
var request = new XMLHttpRequest()
request.addEventListener('load', getStripeConnectUserID)
var params = "client_secret=" + CLIENT_SECRET + "&code=" + AUTHORIZATION_CODE + "&grant_type=authorization_code"
//var params = JSON.stringify({client_secret : CLIENT_SECRET, code : AUTHORIZATION_CODE, grant_type : "authorization_code"})
console.log(params);
request.open("POST", "https://connect.stripe.com/oauth/token", true)
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded")
//request.setRequestHeader("Content-length", params.length)
request.send(params)
})
If anyone could point me in the right direction it would be much appreciated, I am somewhat new to writing backend HTTP calls.
In the backend cloud function, rather than attempt to send the POST request from there, write the auth code to a spot in firebase database.
Back in Xcode have a listener function that executes if there's a change in that database spot. The function should then read the change (the auth code) and simply send an easy swifty POST request with Alamofire.
I understand why you made the uri direct to your specific cloud function (since its an http trigger), but isn't the point of sending a uri so that stripe can redirect the user back to a specific app page, does the user still get directed back to the app in your case???
The similar question was asked by someone else (here) but got no proper answer. Since this is basic and important for me (and maybe for someone else as well), I'm trying to ask here. I'm using Node.js+Express+EJS on the server side. I struggled to make the token authentication succeeded by using jsonwebtoken at the server and jQuery's ajax-jsonp at the web browser. Now after the token is granted and stored in the sessionStorage at the browser side, I can initiate another ajax request with the token included in the request header, to get the user's profile and display it somewhere in the 'current' page. But what I want is to display a new web page to show the user's profile instead of showing it in the 'current' page (the main/index page of the website). The question is:
How to initiate such an HTTP GET request, including the token in the HTTP header; and display the response as a new web page?
How the Node.js handle this? if I use res.render then where to put the js logic to verify the token and access the DB and generate the page contents?
Or, should we say the token mechanism is more suitable for API authentication than for normal web page authentication (where the web browser provides limited API)?
I think the answer to this question is important if we want to use the token mechanism as a general authentication since in the website scenario the contents are mostly organized as web pages at the server and the APIs at the client are provided by the browser.
By pure guess, there might be an alternative way, which the ajax success callback to create a new page from the current page with the response from the server, but I have no idea of how to realize that as well.
By calling bellow code successfully returned the HTML contents in customer_profile.ejs, but the client side ajax (obviously) rejected it.
exports.customer_profile = function (req, res) {
var token = req.headers.token;
var public_key = fs.readFileSync(path.resolve() + '/cert/public_key.pem');
var decoded = jwt.verify(token, public_key);
var sql = 'SELECT * FROM customer WHERE username = "' + decoded.sub + '"';
util.conn.query(sql, function (err, rows) {
if (!err) {
for (var i = 0; i < rows.length; i++) {
res.render('customer_profile', {customer_profile: rows[i]});
break;
}
}
});
};
I am trying to find a solution to this as well. Please note, I am using Firebase for some functionality, but I will try to document the logic as best as I can.
So far what I was able to figure out is the following:
Attach a custom header to the HTTP request client-side
// landing.js - main page script snippet
function loadPage(path) {
// Get current user's ID Token
firebase.auth().currentUser.getIdToken()
.then(token => {
// Make a fetch request to 'path'
return fetch(`${window.location.origin}/${document.documentElement.lang}/${path}`, {
method: 'GET',
headers: {'X-Firebase-ID-Token': token} // Adds unverified token to a custom header
});
})
.then(response => {
// As noted below, this part I haven't solved yet.
// TODO: Open response as new webpage instead of displaying as data in existing one
return response.text();
})
.then(text => {
console.log(text);
})
.catch(error => {
console.log(error);
});
}
Verify the token according to your logic by retrieving the corresponding header value server-side
// app.js - main Express application server-side file
// First of all, I set up middleware on my application (and all other setup).
// getLocale - language negotiation.
// getContext - auth token verification if it is available and appends it to Request object for convenience
app.use('/:lang([a-z]{2})?', middleware.getLocale, middleware.getContext, routes);
// Receives all requests on optional 2 character route, runs middleware then passes to router "routes"
// middleware/index.js - list of all custom middleware functions (only getContext shown for clarity)
getContext: function(req, res, next) {
const idToken = req.header('X-Firebase-ID-Token'); // Retrieves token from header
if(!idToken) {
return next(); // Passes to next middleware if no token, terminates further execution
}
admin.auth().verifyIdToken(idToken, true) // If token provided, verify authenticity (Firebase is kind enough to do it for you)
.then(token => {
req.decoded_token = token; // Append token to Request object for convenience in further middleware
return next(); // Pass on further
})
.catch(error => {
console.log('Request not authorized', 401, error)
return next(); // Log error to server console, pass to next middleware (not interested in failing the request here as app can still work without token)
});
}
Render and send back the data
// routes/index.js - main router for my application mounted on top of /:lang([a-z]{2})? - therefore routes are now relative to it
// here is the logic for displaying or not displaying the page to the user
router.get('/console', middleware.getTranslation('console'), (req, res) => {
if(req.decoded_token) { // if token was verified successfully and is appended to req
res.render('console', responseObject); // render the console.ejs with responseObject as the data source (assume for now that it contains desired DB data)
} else {
res.status(401).send('Not authorized'); // else send 401 to user
}
});
As you can see I was able to modularize the code and make it neat and clear bu use of custom middleware. It is right now a working API returning data from the server with the use of authentication and restricted access
What I have not solved yet:
As mentioned above, the solution uses fetch API and result of the request is data from server (html) and not a new page (i.e when following an anchor link). Meaning the only way with this code now is to use DOM manipulation and setting response as innerHTML to the page. MDN suggests that you can set 'Location' header which would display a new URL in the browser (the one you desire to indicate). This means that you practically achieved what both, you and I wanted, but I still can't wrap my head around how to show it the same way browser does when you follow a link if you know what I mean.
Anyways, please let me know what you think of this and whether or not you were able to solve it from the part that I haven't yet
I want to get some data that are available after authentication. I pass through a post login and password on the page http://site.domain.com/auth.html and I want to get html from another page http://site.domain.com/anotherpage.html
request.post({followAllRedirects: true, url:'http://site.domain.com/auth.html', form:{user:'login#domain.com', pass:'password'}},
function (error, response, body) {
if (!error) {
request('http://site.domain.com/anotherpage.html', function(error, response, html){
fs.appendFileSync('log.txt', html, encoding='utf8');
});
}
});
Authentication takes place normally (there is a message in the html with greeting), after request I get the data as if the authentication is not passed.
fixed result:
var j = request.jar(); var request = request.defaults({jar:j});
and then my code
Most often than not, in the web, authentication information is store as cookies in the user's browser. Because this is a server request, I don't think two "unrelated" requests is going to cut it, as no header information pertaining to the first request is being sent along with the second request. Perhaps you could try this strategy or some other similar procedure to mimic that header interaction.
I found a solution, I put in top of my code, these lines
var j = request.jar(); var request = request.defaults({jar:j});
jar - If true, remember cookies for future use (or define your custom cookie jar;
I have been struggling with performing simple authentication in my angular node application. I am well aware that there are ready to use angular-express yeoman kits out there. But I want to understand this fuly and hence the attempt.
What I have been able to do so far is create a login form which connects to node server. It sens login name and password and receives a response back.
Where I am stuck is how to convert this simple interaction into a authentication process.
My Directory structure is as below
--ParentDirectory/
-client/
--css/
--lib/ /*all angular and jquery library files*/
--src/ /* All other angular modules, directives etc */
--app.js
--index.html /* default page associated with app.js
--login.js /*module login is independent of app.js module */
--login.html
-server/
--server.js /*restify code sits here */
app.js is where the main app resides.
So far it looks like :
angular.module('app',['']);
angular.module('app').controller('mainCtrl',function($scope){
$scope.hello = "Hello World";
});
Now First things first.. when a user visits my website i.e index.html page.. they will end up in this app and I would like for them to be re-directed to login.html page if they are not authenticated.
Any clues on how to achieve that ?
Moving on..
Login.html simply asks for a username and password (not showing the code here to keep things compact)
Login.js looks like this:
angular.module('loginApp',['common.webservice'])
.controller('loginCtrl',['$scope','WSLogin','$location','$window','Authen',function($scope,WSLogin,$location,$window,Authen){
$scope.message;
$scope.submit = function(){
var temp = {logonID: $scope.username,password: $scope.password};
WSLogin.save(temp,function(result){
Authen.isLogged = true;
$window.sessionStorage.token = result.token;
$scope.message = result.token;
$location.path("/main");
},function(err){
$scope.message = "Authentication failed. Pls retry";
});
};
$scope.logout = function(){
if (AuthenticationService.isLogged){
Authen.isLogged = false;
delete $window.sessionStorage.token;
$location.path("/");
}
}
}])
.factory('Authen', function() {
var auth = { isLogged :false };
return auth;
});
WSLogin is a resource that connects to the node serve on the path user/authenticate. This webservice is working fine so far.
[not showing the code here to keep things compact]
The server.js file looks like this:
var restify = require('restify');
var server = restify.createServer({
log: log,
name: 'demo'
});
server.listen(12345, function(){
console.log('%s listening at %s', server.name,server.url);
});
server.post('/user/authenticate',function(req,res,next){
if (!(req.params.logonID === "test" && req.params.password === "test")) {
res.send(401, 'Wrong user or password');
return;
}
var profile = {
first_name: 'John',
last_name: 'Doe',
email: 'john#doe.com',
id: 123
};
// we are sending the profile inside the token
res.json({token:profile);
});
How can I patch things up so that this somewhat resembles a authentication system.
I have picked up tips from various blogs, sites etc.. but no luck implementing them.
I understand you want to know how things are built, but I highly recommend picking up Passport to use on the Node/server side for authentication. With that said, in your implementation of login, it might be easier to include a cookie with the token in the response, rather than returning the token in the response. The code can then return a 200 (or 201) response code to indicate the login was successful, but by moving the token to a cookie, the client code doesn't have to deal with sending the token on future requests -- it's sent automatically in a cookie.
Moving on to your client side questions, keep in mind that you would enable security to protect resources on the server side. Anything you put in your client side JavaScript code can be read by anyone. So in the end these APIs that return protected data are the ones that need to be protected. Again, Passport provides an easy way to protect an API, but you could validate that each API request contains this token (or cookie) prior to providing the data.
In the case that the request is not authorized (it doesn't contain the token, or the token is invalid), you can return a 401 (Unauthorized) response. In the client side code, you can check for this and automatically route the user to the Login page.
Using Angular, one pattern to accomplish this is to use httpProvider Interceptors which allow you to hook into each HTTP request. You could check for a responseError with a status of 401, and route them to the Login page.