Introduction
I need to verify if a email address is within a API, if so allow a user to enter a section someone cant if they are not a user.
I have built the back-end node code to verify this.
I have 3 functions build in my back-end (node, express).
The first
Gets data from a front-end form (Angular) post and then uses promise to make the value available to the third function.
The second
This function authenticates and gets my API key and using promise makes this function available to the third function.
The third
This function uses the data from the first (email address) and the API key from the second and post to a API endpoint, If the email is present on the API you get pass, if not fail.
Process
A user enters a email on the front end Angular, hits login then this email is passed to the three back-end functions, then it will pass or fail in the third function.
If this passes I wish to pass $scope.EmailTrue= true; to my Angular controller so that I can ng-hide or ng-show buttons on my front-end.
I was thinking of maybe a simple post to my front-end, but I am new to angular and node so I am unaware if there is a different way of doing this.
Node back-end (3 functions)
//---------------------------------- Grab the packages we need and set variables ---------------------------------------
//----------------------------------------------------------------------------------------------------------------------
var express = require('express');
var request = require('request');
var nodePardot = require('node-pardot');
var bodyParser = require('body-parser');
var rp = require('request-promise');
var app = express();
var port = process.env.PORT || 8080;
// Credential's for pardot API
var password = 'password';
var userkey = 'userkey';
var emailAdmin = 'admin#admin.com';
// Start the server
app.listen(port);
app.use(bodyParser.json()); // support json encoded bodies
app.use(bodyParser.urlencoded({extended: true})); // support encoded bodies
console.log('Test server started! At http://localhost:' + port); // Confirms server start
//---------------------------------- Function to get front end form posted data LOGIN form ----------------------------------------
//----------------------------------------------------------------------------------------------------------------------
var firstFunction = function () {
return new Promise (function (resolve) {
setTimeout(function () {
app.post('/back-end/test', function (req, res) {
console.log(req.body);
var login = req.body.LoginEmail;
res.send(login);
resolve({
data_login_email: login
});
});
console.error("First done");
}, 2000);
});
};
//---------------------------------- Function to get API key from Pardot (AUTHENTICATION) ------------------------------
//----------------------------------------------------------------------------------------------------------------------
var secondFunction = function () {
return new Promise (function (resolve) {
setTimeout(function () {
nodePardot.PardotAPI({
userKey: userkey,
email: emailAdmin,
password: password,
DEBUG: false
}, function (err, client) {
if (err) {
// Authentication failed
console.error("Authentication Failed", err);
} else {
// Authentication successful
var api_key = client.apiKey;
console.log("Authentication successful !", api_key);
resolve({data_api: api_key});
}
});
console.error("Second done");
}, 2000);
});
};
//---------------------------------- Function to post data to Pardot ---------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
function thirdFunction(result) {
return new Promise (function () {
setTimeout(function () {
var headers = {
'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded'
};
// Configure the request
var api = result[1].data_api;
var login_email = result[0].data_login_email;
var options = {
url: 'https://pi.pardot.com/api/prospect/version/4/do/read',
method: 'POST',
headers: headers,
form: {
'email': login_email,
'user_key': userkey,
'api_key': api
},
json: true // Automatically stringifies the body to JSON
};
// Start the request
rp(options)
.then(function (parsedBody) {
console.error("pass");
// $scope.FormLogin = true;
})
.catch(function (err) {
console.error("fail");
});
console.error("Third done");
}, 3000);
}
);
}
// sequence of functions
Promise.all([firstFunction(), secondFunction()])
.then(thirdFunction);
Angular controller
FirstModule.controller('LoginController', function TestController($scope, $http) {
$scope.LoginForm = function () {
var data = {
LoginEmail: $scope.formData.LoginEmail
};
$http({
url: 'http://localhost:8080/back-end/test',
method: "POST",
data: data,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function (data) {
$scope.formData = data; // assign $scope.persons here as promise is resolved here
}).error(function (data, status) {
$scope.formData = status;
});
}
});
Angular view
<form class="col-sm-8 col-sm-offset-2">
<div>
<div class="form-group">
<label>Email*</label>
<input type="email" class="form-control col-sm-12" name="LoginEmail" placeholder="Enter valid E-mail">
</div>
<button class=""
ng-submit="LoginForm()">
Login<span class=""></span>
</button>
</div>
</form>
In Angular you can user the $http service to make an http request and then handle the response.
You can do:
$http.post('/login', {
email: 'user#email.com',
password: 'pwd'
}).then(response => {
// use http status code
if (response.status === 403) {
alert('forbidden')'
} else {
// do something
}
})
Related
I'm trying to make a test bot that, upon being chatted to responds with a (meaningless) string gotten from a JSON object through another API
Code:
var restify = require('restify');
var builder = require('botbuilder');
var request = require('request-promise');
// Setup Restify Server
var server = restify.createServer();
server.listen(process.env.port || process.env.PORT || 3978, function () {
console.log('%s listening to %s', server.name, server.url);
});
// Create chat connector for communicating with the Bot Framework Service
var connector = new builder.ChatConnector({
appId: process.env.MicrosoftAppId,
appPassword: process.env.MicrosoftAppPassword
});
// Listen for messages from users
server.post('/api/messages', connector.listen());
// Receive messages from the user and respond by echoing each message back (prefixed with 'You said:')
var bot = new builder.UniversalBot(connector, function (session) {
var text = await MyRequest()
session.send("%s", text);
});
async function MyRequest() {
var options = {
uri: "https://jsonplaceholder.typicode.com/posts/1",
method: "GET",
json: true
}
try {
var result = await request(options);
return result;
} catch (err) {
console.error(err);
}
}
The problem is the bot var isn't an asynch function, so I can't put await in it. If I remove await, the bot replies with Object Promise. I'm fairly inexperienced in JS overall, so can I get any pointers?
e: The Request part works great, I've tested it alone in a different js program
Have you tried this. If you are using ES6 compatible Node environment this should work
var bot = new builder.UniversalBot(connector, async function (session) {
// Use JSON.stringify() if MyRequest Promise will resolve a object
var text = await MyRequest()
session.send("%s", text);
});
If async/await isn't possible, how about returning a promise? like below:
function MyRequest() {
var options = {
uri: "https://jsonplaceholder.typicode.com/posts/1",
method: "GET",
json: true
}
return request(options);
}
And use Promise.then to act on the result, like so:
var bot = new builder.UniversalBot(connector, function (session) {
MyRequest().then(function(text) {
session.send("%s", text);
}).catch(function(error) {
session.send("%s", error);
});
});
I am working on an Express.js app. The current feature is creating an appointment with a post request and getting and saving data from third party API, then sending updated API data in the subsequent request. The feature is fully working but in the test, the function to get API data is not getting called.
Route to create appointment:
app.post('/schedule', requestHandler.postSchedule);
The request handler for creating appointment:
requestHandler.postSchedule = function (req, res) {
let appointment = {
// posted data
};
new Appointment(appointment)
.save()
.then(newAppointment => {
if(req.body.cityName && req.body.cityName !== '') {
console.log('req.body.cityName', req.body.cityName);
weatherHelper.addNewCityWeatherData(req.body.cityName);
}
return newAppointment;
})
.then(newAppointment => {
// do some other stuff
res.send(newAppointment);
})
.catch(err => {
error(err);
});
};
Function to add weather data:
exports.addNewCityWeatherData = (city) => {
console.log('City in addNewCityWeatherData', city);
getCurrentTrackingCities(cities => {
if(cities.indexOf(city) < 0) {
console.log(city + ' data not in weather');
getWeatherData(city, data => {
console.log('Got weather data');
addWeatherDataToDB(city, data);
});
} else {
console.log('City exists');
}
});
};
Function to get weather data from API:
getWeatherData = (city, callback) => {
console.log('getWeatherData called', city);
let url = `http://api.apixu.com/v1/forecast.json?key=${weatherApiKey}&q=${city}&days=${10}`
request(url, (err, res, body) => {
console.log('Weather data received body');
callback(body);
});
};
When testing, this feature fails and all console logs are printed except the 'Weather data received body' and the logs in consequent functions.
Here is my test:
describe.only('Weather data', function() {
let requestWithSession = request.defaults({jar: true});
let hashedPass = bcrypt.hashSync('testpass', null);
beforeEach((done) => {
new User({
'name': 'Test User',
'email': 'testuser#test.com',
'password': hashedPass
})
.save()
.then(() => {
let options = {
'method': 'POST',
'uri': testHost + '/login',
'form': {
'email': 'testuser#test.com',
'password': 'testpass'
}
};
requestWithSession(options, (err, res, body) => {
done();
});
});
}); // beforeEach
afterEach((done) => {
// remove test stuff from db
}); // afterEach
it('Adds weather data when an appointment with new city is posted', (done) => {
let options = {
'method': 'POST',
'uri': testHost + '/schedule',
'form': {
'title': 'Test title',
'description': 'Test description',
'start_date_time': '2017-07-19 01:00',
'end_date_time': '2017-07-19 02:00',
'cityName': 'New York',
'isTrackingWeather': 'true'
}
};
// post request to add appointment data
requestWithSession(options, (err, res, body) => {
if(err) {
console.log('DatabaseError in Weather Data');
throw {
type: 'DatabaseError',
message: 'Failed to create test setup data'
};
}
let options = {
'method': 'GET',
'uri': testHost + '/allweather'
};
// subsequesnt request to get updated weather data
requestWithSession(options, (err, res, body) => {
let found = false;
weatherData = JSON.parse(body);
// console.log('weatherData in test', weatherData);
weatherData.forEach(weather => {
if(weather.location && weather.location.name === 'New York') {
found = true;
}
});
expect(found).to.be.true;
done();
});
});
});
}); // Weather Data
Here is the terminal output:
Can anyone please tell me what am I doing wrong?
When you run your test is that the test suite make a request to your test server, and the code that handles the request in your test server makes another request to another host.
You do not get to see 'Weather data received body' because the request handled by your test server is not waiting for the request that the test server itself makes. addNewCityWeatherData has no callback and does not return a promise, so the code that calls it goes on its merry way without waiting for it to complete. You should modify it to allow for the calling code to wait for a result.
Also, I'm not seeing how the data from the request initiated by your test server is folded back into the request that comes from your test suite. You may have to add some code for that too, unless addWeatherDataToDB(city, data); is taking care of it automatically somehow.
I am quit new to angularjs, i have created a factory service, which have a authentication service in it. if the authentication succeeded then it generates a token which is required for the dashboard services. i am able to generate the token but not understand how to use it.
Is it a good idea to create a factory service to get token so i should inject in controllers as needed?
Any help highly appreciated. Thanks in advanced.
login.html :
<div ng-app="loginFormApp" ng-controller="loginFormCtrl">
<form method="post" action="" id="login_form" class="row">
<input type="text" placeholder="Login ID" ng-model="loginId" >
<input type="password" placeholder="Password" ng-model="password" >
<input type="button" class="btn btn-theme" ng-click="loginCall()" value="Login">
<input type="button" class="btn btn-theme" ng-click="loginCall()" value="Register Here">
</form>
</div>
my controller and factory service :(authService.js)
var app = angular.module('loginFormApp', []);
app.controller('loginFormCtrl', function ($scope, AuthService) {
$scope.loginCall = function () {
var token= AuthService.authentication($scope.loginId, $scope.password);
alert(token);
};
});
app.factory('AuthService', function ($http) {
return {
authentication: function (UserName, Password) {
$http.post("http://103.19.89.152:8080/ccp-services/authenticate", {
'userName': UserName,
'password': Password
})
.then(function (response) {
window.location.href = "http://192.168.1.144:2000/angular/dashboard.html";
var getToken = response.data.httpHeaders.h5cAuthToken;
// alert(token);
},
// Error Handling
function (response) {
console.log(response.datas);
});
}
}
});
This code does not work, because $http.post returns a promise.
var token = AuthService.authentication($scope.loginId, $scope.password);
First of all, you should return the $http.post method, as shown below.
return $http.post( // rest of code
In the then method after the $http.post, you should return the token.
.then(function (response) {
window.location.href = "http://192.168.1.144:2000/angular/dashboard.html";
return response.data.httpHeaders.h5cAuthToken; //return token
},
And the login call in your controller should be
AuthService.authentication($scope.loginId, $scope.password).then(function(token) {
alert(token);
});
Update 1: reusing the access token
Ofcourse you want to be able to reuse the access token in API calls after the authentication call. This can be done by doing the following. Instead of returning the access token to the calling method, you can 'cache' the token inside the service itself.
app.factory('AuthService', function ($http) {
var cachedToken; // this is where the token will be saved after authentication
return {
authentication: function (UserName, Password) {
$http.post("http://103.19.89.152:8080/ccp-services/authenticate", {
'userName': UserName,
'password': Password
})
.then(function (response) {
window.location.href = "http://192.168.1.144:2000/angular/dashboard.html";
cachedToken = response.data.httpHeaders.h5cAuthToken; // save token to 'cache'
return cachedToken
},
function (response) { // Error Handling
console.log(response.datas);
});
},
getToken: function() { // new method to retrieve the cached token
return cachedToken;
}
}
});
In your dashboard controller, you can retrieve the token with:
AuthService.getToken();
Ofcourse, you need additional code to check if a token is actually retrieved (otherwise you will get undefined).
$http.post invokes asynchronously, so you have to use callback to get token variable, authentication function will not return it.
Use the below factory. I added a return in $http call.
app.factory('AuthService', function ($http) {
return {
authentication: function (UserName, Password) {
return $http.post("http://103.19.89.152:8080/ccp-services/authenticate", {
'userName': UserName,
'password': Password
})
.then(function (response) {
window.location.href = "http://192.168.1.144:2000/angular/dashboard.html";
var getToken = response.data.httpHeaders.h5cAuthToken;
// alert(token);
},
// Error Handling
function (response) {
console.log(response.datas);
});
}
}
});
Node and Angular. I have a MEAN stack authentication application where I am setting a JWT token on successful login as follows, and storing it in a session in the controller. Assigning the JWT token to config.headers through service interceptor:
var token = jwt.sign({id: user._id}, secret.secretToken, { expiresIn: tokenManager.TOKEN_EXPIRATION_SEC });
return res.json({token:token});
authservice.js Interceptor(omitted requestError,response and responseError):
authServices.factory('TokenInterceptor', ['$q', '$window', '$location','AuthenticationService',function ($q, $window, $location, AuthenticationService) {
return {
request: function (config) {
config.headers = config.headers || {};
if ($window.sessionStorage.token) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.token;
}
return config;
}
};
}]);
Now I wanted to get the logged in user details from the token, How can I do that? I tried as follows, not working. When I log the error from Users.js file it's saying "ReferenceError: headers is not defined"
authController.js:
$scope.me = function() {
UserService.me(function(res) {
$scope.myDetails = res;
}, function() {
console.log('Failed to fetch details');
$rootScope.error = 'Failed to fetch details';
})
};
authService.js:
authServices.factory('UserService',['$http', function($http) {
return {
me:function() {
return $http.get(options.api.base_url + '/me');
}
}
}]);
Users.js (Node):
exports.me = function(req,res){
if (req.headers && req.headers.authorization) {
var authorization =req.headers.authorization;
var part = authorization.split(' ');
//logic here to retrieve the user from database
}
return res.send(200);
}
Do i have to pass the token as a parameter too for retrieving the user details? Or save the user details in a separate session variable as well?
First of all, it is a good practice to use Passport middleware for user authorization handling. It takes all the dirty job of parsing your request and also provides many authorization options.
Now for your Node.js code.
You need to verify and parse the passed token with jwt methods and then find the user by id extracted from the token:
exports.me = function(req,res){
if (req.headers && req.headers.authorization) {
var authorization = req.headers.authorization.split(' ')[1],
decoded;
try {
decoded = jwt.verify(authorization, secret.secretToken);
} catch (e) {
return res.status(401).send('unauthorized');
}
var userId = decoded.id;
// Fetch the user by id
User.findOne({_id: userId}).then(function(user){
// Do something with the user
return res.send(200);
});
}
return res.send(500);
}
Find a token from request data:
const usertoken = req.headers.authorization;
const token = usertoken.split(' ');
const decoded = jwt.verify(token[1], 'secret-key');
console.log(decoded);
Your are calling the function UserService.me with two callbacks, although the function does not accept any arguments. What I think you want to do is:
$scope.me = function() {
UserService.me().then(function(res) {
$scope.myDetails = res;
}, function() {
console.log('Failed to fetch details');
$rootScope.error = 'Failed to fetch details';
});
};
Also, note that the $http methods return a response object. Make sure that what you want is not a $scope.myDetails = res.data
And in your Users.js file, you are using the variable headers.authorization directly, whereas it should be req.header.authorization:
var authorization = req.headers.authorization;
According to the documentation https://github.com/themikenicholson/passport-jwt, you could use request.user. Note, I'm supposing that you are using passport with passport-jwt.
It's possible because passport during the context of an authentication is setting the request object and populating the user property. So, just access that property. You don't need to do a middleware.
Anderson anzileiro is correct. If you return the full token in the middleware code, the request is indeed populated with the user property and you can access your profile.
passport.use(
new JWTstrategy(
{
secretOrKey: process.env.ACCESS_TOKEN_SECRET,
// jwtFromRequest: ExtractJWT.fromUrlQueryParameter('secret_token')
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
},
async (token, done) => {
try {
return done(null, token);
} catch (error) {
done(error);
}
}
)
);
req.user will return :
{
"user": {
"username": "admin"
},
"iat": 1625920948,
"exp": 1626007348
}
I have LoginController and securityService.
This is LoginCtrl
// place the message if something goes wrong
$scope.authMsg = '';
$scope.login = function () {
$scope.authMsg = '';
var loginData = {email: $scope.account.email, password: $scope.account.password};
securityService.login(loginData);
};
This is securityService
login: function (logData) {
var _vm = this;
$http
.post('/api-token-auth/', logData)
.then(function (response) {
// assumes if ok, response is an object with some data, if not, a string with error
// customize according to your api
if (!response.data.token) {
_vm.authMsg = 'Incorrect credentials.';
} else {
$cookieStore.put('djangotoken', response.data.token);
$http.defaults.headers.common.Authorization = 'JWT ' + response.data.token;
$http.get('/api/account/restricted/').then(function (response) {
authService.loginConfirmed();
_vm.currentUser = response.data;
$rootScope.currentUser = response.data;
});
}
}, function (x) {
_vm.authMsg = 'Server Request Error';
});
},
This login is working fine but my problem is i don't know how can get the authMesg from service to controller because that is async. Everytime i get blank message in case of invalid login
you need to use promise service of angular to make you controller and service syn
login: function (logData) {
var _vm = this,d= $$q.defer();
$http
.post('/api-token-auth/', logData)
.then(function (response) {
// assumes if ok, response is an object with some data, if not, a string with error
// customize according to your api
if (!response.data.token) {
_vm.authMsg = 'Incorrect credentials.';
} else {
$cookieStore.put('djangotoken', response.data.token);
$http.defaults.headers.common.Authorization = 'JWT ' + response.data.token;
$http.get('/api/account/restricted/').then(function (response) {
authService.loginConfirmed();
_vm.currentUser = response.data;
$rootScope.currentUser = response.data;
});
}
d.resolve(vm.authMsg);
}, function (x) {
_vm.authMsg = 'Server Request Error';
d.reject(vm.authMsg);
});
},
In controller you need to resolve this promise
securityService.login(loginData).then(function(data){
consol.log(data); // get success data
},function(error){
consol.log(data); // get error message data
})
and inject $q in your service.
This will give you authMsg
securityService.login(loginData).authMsg
But follow #Vigneswaran Marimuthu comments, that is best practice.