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);
});
}
}
});
Related
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
}
})
Currently, when the users logs in, the login page does't redirect to the homepage.
'use strict';
angular.module('myapp').service('auth', function auth($http, API_URL, authToken, $state, $window, $q) {
function authSuccessful(res) {
authToken.setToken(res.token);
$state.go('main');
}
this.login = function (email, password) {
return $http.post(API_URL + 'login', {
email: email,
password: password
}).success(authSuccessful);
}
this.register = function (email, password) {
return $http.post(API_URL + 'register', {
email: email,
password: password
}).success(authSuccessful);
}
However, I have set my $state.go to redirect to main. Where is the problem? why is it not redirecting?
annex
here is my login.js controller, how it looks:
angular.module('myapp').controller('LoginCtrl', function ($scope, alert, auth, $auth) {
$scope.submit = function () {
$auth.login({
email: $scope.email,
password: $scope.password
})
.then(function(res) {
var message = 'Thanks for coming back ' + res.data.user.email + '!';
if (!res.data.user.active)
message = 'Just a reminder, please activate your account soon :)';
alert('success', 'Welcome', message);
})
.catch(handleError);
}
// i forgot to include this error handler in my code:
function handleError(err) {
alert('warning', 'oops there is a problem!', err.message);
}
});
Since this is an async action, angular doesn't know when the action finishes and thus when to update the $scope. For this to work you'll need to manually call $scope.apply(), but since you don't have access to the $scope in your service, you need to move the redirection logic (i.e. $state.go('main')) inside a controller, and call it like this:
angular.module('myapp').controller('LoginCtrl', function($scope, auth, $state) {
function redirect(res) {
$state.go('main');
// manually update the scope
$scope.$apply();
}
auth.login(email, password)
.success(redirect);
});
EDIT: Integrate with the given controller
angular.module('myapp').controller('LoginCtrl', function ($scope, alert, auth, $auth) {
$scope.submit = function () {
$auth.login({
email: $scope.email,
password: $scope.password
})
.then(function(res) {
var message = 'Thanks for coming back ' + res.data.user.email + '!';
if (!res.data.user.active) {
message = 'Just a reminder, please activate your account soon :)';
}
alert('success', 'Welcome', message);
return null;
})
.then(function() {
$state.go('main');
// manually update the scope
$scope.$apply();
})
// google replacement of the above commented out code bit
.catch(handleError);
}
});
EDIT 2: Use $timeout instead of $scope.$apply so you don't get $digest error.
angular.module('myapp').controller('LoginCtrl', function ($scope, alert, auth, $auth, $timeout) {
...
.then(function() {
// $timeout calls $scope.$apply() by default,
// but it does it in a safely manner - never throws a '$digest already in progress' exception
$timeout(function() {
$state.go('main');
});
})
...
so I am calling Login function when the user logs in. This function calls UserService.GetByEmail, which does a GET HTTP request that fetches User from database and returns the User as a response if there's a User with email typed in login. After that, I do the authentication with if (user !== null && user.password === password) { part. However, when I look at console output, I do have an Object for user variable, but I have nothing for user.password to compare with password. How do I put the User password from response into user.password?
(function () {
'use strict';
angular
.module('app')
.factory('AuthenticationService', AuthenticationService);
AuthenticationService.$inject = ['$http', '$cookieStore', '$rootScope', '$timeout', 'UserService'];
function AuthenticationService($http, $cookieStore, $rootScope, $timeout, UserService) {
var service = {};
service.Login = Login;
service.SetCredentials = SetCredentials;
service.ClearCredentials = ClearCredentials;
return service;
function Login(email, password, callback) {
$http.post('/user/authenticate', { username: username, password: password })
.success(function (response) {
callback(response);
});
}
Then here is part of my UserController in the backend.
#RequestMapping(value = "/user/authenticate", method = RequestMethod.POST)
public ResponseEntity<Void> authenticateUser(#RequestBody User user, UriComponentsBuilder ucBuilder) {
}
I'm not sure how I should authenticate in the backend. What are the steps needed to do this?
You shouldn't be sending password in any form to the client. If you're using Spring Security, you need to call the login handler on the server.
You should look into using something like JWT instead (read more here https://www.toptal.com/java/rest-security-with-jwt-spring-security-and-java) or if you really need to be using form based security for some reason you can login to the server by using this block of code.
this.login = function (username, password, rememberMe) {
if (rememberMe === undefined) rememberMe = false;
return $http.post(
'/j_spring_security_check',
$.param({
j_username: username,
j_password: password,
j_remember: rememberMe
}),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'X-Requested-With': 'XMLHttpRequest'
}
}
).then(_.bind(function (response) {
if (response.data.success === true) {
//something to happen on login
}
return $q.reject(response);
}, this));
};
this.logout = function () {
$http.get('/j_spring_security_logout').then(_.bind(function () {
//something to happen on logout
}, this));
};
There's a few things:
This cannot be all the code involved: it's not clear what the UserService object or AuthenticationService factory function are.
Moreover one expects that you would not have a password to compare with anyway (that be a bit of a security hole).
Instead, authentication should be considered successful if the HTTP status code is 200 (or other 2xx codes depending on the backend). Ordinarily, this means that if you enter the then() clause of the promise the login must have been successful, because 4xx codes would have been mapped to failures and reported through catch() instead.
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.