I'm trying to add authentication to my golang/angular app. the backend authentication works fine and logs that the user has logged in but the angular part is not working as expected, it doesn't set the username as when it successfully logs in and changes page, the username is not set.
app.js
blog.controller('LoginCtrl', function($scope, $http, $window, authService){
$scope.login = function({
authService.Login($scope.username, $scope.password, function(response, status){
if(status == 200){
authService.setCredentials($scope.username, $scope.password);
$window.location.href="/";
} else {
$scope.invalidLogin = true;
}
});
};
});
blog.factory('authService', function(username, password, callback){
var service = {};
var username = "";
$http.post('/login', {Username : username, Password: password}).
success(function(response, status){
service.setCredentials(username, password);
callback(response, status);
});
service.setCredentials = function(username, password){
username = username;
};
service.getCredentials = function(){
return username;
};
return service;
});
blog.controller('NavCtrl', function($scope, $rootScope, authService){
$scope.isAuth = (authService.getCredentials() != "");
console.log("username: " + authService.getCredentials());
$scope.username = authService.getCredentials();
});
The problem is that your authService doesn't have the Login method you're calling from your controller:
blog.controller('LoginCtrl', function($scope, $http, $window, authService){
$scope.login = function({
// Well there's your problem!
authService.Login($scope.username, $scope.password, function(response, status){
if(status == 200){
authService.setCredentials($scope.username, $scope.password);
$window.location.href="/";
} else {
$scope.invalidLogin = true;
}
});
};
});
Instead, you need to define your Login method within your factory like so:
myApp.factory('authService', function(){
var service = {};
var username = "";
service.setCredentials = function(loginUsername, password){
username = loginUsername;
};
service.getCredentials = function(){
return username;
};
service.Login = function(loginUsername, password, callback){
$http.post('/login', {Username : loginUsername, Password: password}).
success(function(response, status){
service.setCredentials(loginUsername, password);
callback(response, status);
});
}
return service;
});
Note that I've also changed the username function parameters to loginUsername as it was shadowing the variable you where trying to assign to. This was causing the username value to be undefined.
Related
I want to do a very simple registration using AngularJS.
Firstly, I'm getting user with this e-mail and assign to $scope.users. If method "GetUserByEmail" returns more than one user, I try show message "User already exists". And here is problem. Method GetUserByEmail is avoided. Program "jumps" to "if" condition and $scope.users is always empty, I don't know why. Sometimes after adding user to database, the method returns array of object and assign to $scope.users
It's my code with method CreateUser:
var RegisterController = function ($scope, Api, $http) {
$scope.users = {
}
$scope.CreateUser = function () {
var user = {
Password: $scope.password,
Name: $scope.name,
Surname: $scope.surname,
Email: $scope.email,
DateOfBirth: $scope.dateofBirth
}
Api.GetUserByEmail("Users", "GetUserByEmail", $scope.email).then(function (d) {
$scope.users = d;
});
if ($scope.users.length > 0)
{
alert("User already exists!");
$scope.users = {};
}
else
{
Api.PostUser("Users", "PostUser", user).then(function (d) {
alert("Hello");
});
}
};
}
RegisterController.$inject = ['$scope', 'Api', '$http'];
And method GetUserByEmail:
this.GetUserByEmail = function (controllerName, methodName, email) {
var promise = $http({
method: 'GET',
url: 'api/' + controllerName + '/' + methodName + '?email=' + email,
config: {
params: {
"email": email
}
}
})
.then(function onSuccess(response) {
return response.data;
},
function onError(response) {
return response.statusText;
});
return promise;
}
Try this!!!
var RegisterController = function($scope, Api, $http) {
$scope.users = {}
$scope.CreateUser = function() {
var user = {
Password: $scope.password,
Name: $scope.name,
Surname: $scope.surname,
Email: $scope.email,
DateOfBirth: $scope.dateofBirth
}
Api.GetUserByEmail("Users", "GetUserByEmail", $scope.email).then(function(d) {
$scope.users = d;
if ($scope.users.length > 0) {
alert("User already exists!");
$scope.users = {};
} else {
Api.PostUser("Users", "PostUser", user).then(function(d) {
alert("Hello");
});
}
});
};
}
RegisterController.$inject = ['$scope', 'Api', '$http'];
If you used success...error block on your request, eventhough the response is null, request will return into success block and you can validate yor data. I think these are fine you.
var RegisterController = function ($scope, Api, $http) {
$scope.users = {
}
$scope.CreateUser = function () {
var user = {
Password: $scope.password,
Name: $scope.name,
Surname: $scope.surname,
Email: $scope.email,
DateOfBirth: $scope.dateofBirth
}
Api.GetUserByEmail("Users", "GetUserByEmail", $scope.email).success(function (response) {
if (response!=null && dresponse.lenght > 0)
{
alert("User already exists!");
$scope.users = {};
}
else
{
Api.PostUser("Users", "PostUser", user)
.success(function (d) {
alert("Hello");
}).error(function(e){
console.log(e.message);
});
}
}).error(function(error){
console.log(error.message);
});
};
}
RegisterController.$inject = ['$scope', 'Api', '$http'];
Error:In test.js call callback(fetch response) from servie function call in controller
Here there is three files.
In that test.js test the controller.js file using karma-jasmin for unit testing for angular js
controller.js
'use strict';
// Login Angular Module Controller
var loginModule = angular.module('loginModule.controllers'['toaster','vcRecaptcha']);
// Controller Function : loginCtrl
// Parameters : $scope, $rootScope, $cookieStore, $location, AuthenticationServiceLogin, FlashService, toaster, vcRecaptchaService, $http, $controller
loginModule.controller('loginCtrl', function ($scope, $rootScope, $cookieStore, $location, AuthenticationServiceLogin, FlashService, toaster, vcRecaptchaService, $http, $controller)
{
//redirect user to dashboard if already logged in
$rootScope.globals = $cookieStore.get('globals') || {};
if ($rootScope.globals.currentUser) {
$location.path('/home');
}
// Function Name : forget password
$scope.forgetPassword = function(){
$scope.dataLoading = true;
AuthenticationServiceLogin.forgetPassword($scope.email, function (response) {
if (response.success) {
toaster.pop('success', "", "Email sent Successfully. Please check your email.");
$scope.dataLoading = false;
}else{
toaster.pop('error', "", "Email is incorrect / Email not exist in Database");
$scope.dataLoading = false;
}
});
};
//AuthenticationService.ClearCredentials();
// Function Name : login
$scope.login = function (){
$scope.dataLoading = true;
// callback(response);
// Service Calls Function : userLogin
// Parameters : $scope.username,$scope.password AuthenticationServiceLogin.userLogin($scope.username,$scope.password, function (response) {
console.log(response.success);
if(response.success)
{
AuthenticationServiceLogin.SetCredentials(response);
toaster.pop('success', "", "Login Successful");
$location.path('/home');
}
if (response.status ==200) {
//AuthenticationServiceLogin.SetCredentials(response);
//START STOMP to establish connection
var stompCtrl = $rootScope.$new();
$controller('StompController', { $scope: stompCtrl });
//END STOMP to establish connection
toaster.pop('success', "", "Login Successful");
$location.path('/home');
}
else if(response.status ==401){
toaster.pop('error', response.status+" "+response.statusText," Access is denied due to invalid credentials");
$scope.dataLoading = false;
}
else {
toaster.pop('error', "Username / Password", "Username or password is incorrect");
$scope.dataLoading = false;
}
});
};
});
service.js
'use strict';
var loginModule = angular.module('loginModule.services',['vcRecaptcha']);
loginModule.service('AuthenticationServiceLogin', function ($http, $cookieStore,$rootScope,$timeout, UserService, Base64,vcRecaptchaService,ENV) {
var service = {};
var servicePath = ENV.apiEndpoint;
this.forgetPassword = function(email,callback)
{
$timeout(function () {
var response;
if(email == 'admin#belvms.com'){
response = {success: true};
}
else {
response = {success: false};
}
callback(response);
}, 900);
}
this.userLogin = function(username, password,callback) {
$timeout(function () {
var response;
if(username == 'admin' && password =='admin'){
response = {success: true};
}
else {
//response = {success: false, message: 'Username or password is incorrect'};
}
callback(response);
}, 900);
var data1 = "username=" + username + "&password="
+ password + "&grant_type=password&scope=read%20write&" +
"client_secret=belSecret&client_id=vms-bel";
};
this.SetCredentials = function (username, password) {
var authdata = Base64.encode(username + ':' + password);
$rootScope.globals = {
currentUser: {
username: username,
authdata: authdata
}
};
$http.defaults.headers.common['Authorization'] = 'Basic ' + authdata; // jshint ignore:line
$cookieStore.put('globals', $rootScope.globals);
}
};
});
test.js
'use strict';
describe('Login', function () {
var $controller1,AuthenticationServiceLogin,$window,$scope,$location;
beforeEach(module('loginModule.controllers'));
beforeEach(inject(function(_$controller_,_$q_,_$window_,_$location_) {
$controller1 = _$controller_;
$window = _$window_;
AuthenticationServiceLogin = { userLogin: function() {},
forgetPassword: function() {}
}; spyOn(AuthenticationServiceLogin,'userLogin').and.returnValue(response);
spyOn(AuthenticationServiceLogin,'forgetPassword').and.returnValue(response);
}));
describe('Login', function () {
it('To call Login function', function () {
var $scope = {};
var $rootscope = {};
var $location = {};
var $cookieStore = {};
var $http = {};
var $controller = {};
var FlashService = {};
var controller = $controller1('loginCtrl',{
$scope: $scope,
$rootscope: $rootscope,
$location: $location,
$window: $window,
$cookieStore:$cookieStore,
AuthenticationServiceLogin: AuthenticationServiceLogin,
FlashService:FlashService,
$http:$http,
$controller:$controller
});
$scope.login();
});
it('To call services', function() {
var $scope = {};
var $rootscope = {};
var $location = {};
var $cookieStore = {};
var $http = {};
var $controller = {};
var FlashService = {};
var controller = $controller1('loginCtrl',{
$scope: $scope,
$rootscope: $rootscope,
$location: $location,
$window: $window,
$cookieStore:$cookieStore,
AuthenticationServiceLogin:AuthenticationServiceLogin,
FlashService:FlashService,
$http:$http,
$controller:$controller
});
$scope.username="admin";
$scope.password="admin";
$scope.login();
it('should have a getData function', function() {
expect(angular.isFunction(AuthenticationServiceLogin.userLogin)).toBe(true);
});
$scope.email='admin#belvms.com';
var response1=function(response){};
console.log(response1);
expect(AuthenticationServiceLogin.userLogin).toHaveBeenCalledWith('admin','admin',response1);
});
});
});
Here i attached image that display error when run test script.
enter image description here
so give solution why this erroe or why can't fetch reponse from userLogin function in test script
I am trying to achieve user login
and logout using angularJS and web Api
But the server always return badrequest (400)
exception
the error is coming from this bit of code
AuthApp.factory('authService', ['$http', '$q', 'localStorageService', function ($http, $q, localStorageService) {
var authServiceFactory = {};
var _authentication =
{
isAuth: false,
userName: ""
};
// this is the login function
authServiceFactory.login = function (loginData)
{
var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password; //is not working
//var data = { username: loginData.userName, password: loginData.password, grant_type: "password" }; // I try this and is not working too
//data = $.param(data);
// how should I format my data for the web API to understand
var deferred = $q.defer();
// alert(data);
$http.post('/token', data, {
header: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).success(function (response) {
localStorageService.set('authorizationData', { token: response.access_token, userName: response.userName });
_authentication.isAuth = true;
_authentication.userName = loginData.userName;
deferred.resolve(response);
}).error(function (err) {
// _logout();
deferred.reject(err);
});
return deferred.promise;
}
authServiceFactory.logout = function ()
{
localStorageService.remove("authenticationData");
_authentication.isAuth = false;
_authentication.userName = "";
}
return authServiceFactory;
}]);
using postman to further see the error
this appears
{ "error": "unsupported_grant_type" }
I made google search but still no solution; how can I resolve this issue?
thanks in advance!!
First I have a Controller like this:
login.controller.js:
angular.module('app').controller('LoginController',
function($scope,UserService,$location) {
$scope.submit = function() {
UserService.login(email,password).then(function(data){
if(data.result=='success'){
$location.path('/home');
else{
$scope.loginError='Login failed';
}
},function(error){
$scope.loginError='Login failed';
});
};
and a factory service:UserService.js
angular.module('app').factory('UserService', function($http,$q,CONST) {
login: function(username, password) {
var defer =$q.defer();
$http({
method:'POST',
url:CONST.baseUrl+'rest/login',
data:{
userName:username,
password:password
}
}).success(function(data,status,headers,config){
defer.resolve(data);
}).error(function(data){
defer.reject(data);
});
return defer.promise;
},
And my jasmine test like this :
describe('test the userService',function(){
beforeEach(module('app'))
var scope,LoginController,httpBackend;
beforeEach(inject(function(_$rootScope_,$controller,_UserService_,_$httpBackend_){
scope = _$rootScope_.$new();
httpBackend = _$httpBackend_;
LoginController = $controller('LoginController',{
$scope:scope,
UserService:_UserService_
});
}));
it('when login post return success',function(){
httpBackend.expectPOST('rest/login',{
userName:'Jordan',
password:'password'
}).respond(200,{result:'success'});
spyOn(UserService,'login').and.callThrough();
scope.submit();
httpBackend.flush();
expect(UserService.login).toHaveBeenCalled();
expect(location.path()).toBe('/home');
});
afterEach(function(){
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
});
and the result turns out that:
Chrome 43.0.2357 (Windows 7 0.0.0) test the userService when the login post retu
rn success FAILED
Expected spy login to have been called.
at Object.<anonymous> (C:/Users/IBM_ADMIN/desk/workspace/WaterFundWe
b/WebContent/test/unit/userService.js:28:29)
but I am sure that the login() function is invoked,how could it comes to this
First of all, I've refactored your LoginController and UserService:
//I also created CONST factory and it just returns empty string as baseUrl,
//because I don't know what's the logic inside this factory in your code.
angular.module('app').factory('CONST', function() {
return {
baseUrl: ''
};
});
angular.module('app').factory('UserService', function($http, $q, CONST) {
//UserService should return object with login function.
//You've shown code for this factory with incorrect syntax.
return {
login: function (username, password) {
var defer = $q.defer();
$http({
method: 'POST',
url: CONST.baseUrl + 'rest/login',
data: {
userName: username,
password: password
}
}).success(function (data, status, headers, config) {
defer.resolve(data);
}).error(function (data) {
defer.reject(data);
});
return defer.promise;
}
};
});
angular.module('app').controller('LoginController', function($scope, UserService, $location) {
//Here you have to add parameters to $scope.submit function,
//because you're going to use it while calling UserService.login function
$scope.submit = function (email, password) {
UserService.login(email, password).then(
function (data) {
if (data.result == 'success') {
$location.path('/home');
} else {
$scope.loginError = 'Login failed';
}
},
function (error) {
$scope.loginError = 'Login failed';
});
};
});
After refactoring I've found some syntax errors in your tests. I'm showing you whole code with my comments and fixes.
describe('test the userService',function() {
var scope, LoginController, httpBackend,
userService, //You should create variable for UserService to work with it further (creating spies and so on)
location; //You also need variable for $location service to check current path in your test
beforeEach(inject(function(_$rootScope_, $controller, UserService, _$httpBackend_, _$location_) {
scope = _$rootScope_.$new();
httpBackend = _$httpBackend_;
////Don't forget to initialize your 'new' variables
location = _$location_;
userService = UserService;
LoginController = $controller('LoginController', {
$scope:scope,
UserService: userService,
$location: location //Pass $location service to your LoginController
});
}));
it('when login post return success',function(){
httpBackend.expectPOST('rest/login',{
userName:'Jordan',
password:'password'
}).respond(200,{result:'success'});
spyOn(userService, 'login').and.callThrough();
//Here we pass parameters, which are needed for login function.
//You expect that POST request to rest/login url will be sent with 'Jordan' username and 'password' as password.
//So, we pass it to $scope.submit function
scope.submit('Jordan', 'password');
httpBackend.flush();
expect(userService.login).toHaveBeenCalled();
expect(location.path()).toBe('/home');
});
afterEach(function(){
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
});
Now your test passes.
If you have questions or it doesn't work for you, let me know.
My code looks like this:
$scope.login = function (userName, password, rememberMe) {
authentication.authenticating = true;
var config = {
method: 'POST',
url: '/api/Account/Login',
data: { 'userName': userName, 'password': password, 'rememberMe': rememberMe }
};
$http(config)
.success(function (data) {
authentication.authenticating = false;
authentication.isAuthenticated = true;
$scope.template = $scope.templates[1];
$scope.userName = userName;
})
.error(function (data) {
$scope.loginError = "Invalid username/password combination";
authentication.authenticating = false;
});
};
Is there a way I can move the authentication.authenticating = false; into some code block that will always execute after an error or success ?
Yes, since $http returns a promise, you can call .finally(callback) method as described in $q documentation https://docs.angularjs.org/api/ng/service/$q
So you can use it in your case like this
$scope.login = function (userName, password, rememberMe) {
authentication.authenticating = true;
var config = {...};
$http(config)
.success(function (data) {
authentication.isAuthenticated = true;
$scope.template = $scope.templates[1];
$scope.userName = userName;
})
.error(function (data) {
$scope.loginError = "Invalid username/password combination";
})
.finally(function() {
authentication.authenticating = false;
});
};
Try promise ($q)
Use $q.defered to take an object and after you can use resolve or reject in success or error http method
Sorry for short answer, i am on mobile.
You can move that code into another method, like this, but you'd still have to call it manually from your success or failure functions:
var setAuthenticating = function(){
authentication.authenticating = false;
}
$scope.login = function (userName, password, rememberMe) {
authentication.authenticating = true;
var config = {
method: 'POST',
url: '/api/Account/Login',
data: { 'userName': userName, 'password': password, 'rememberMe': rememberMe }
};
$http(config)
.success(function (data) {
setAuthenticating();
authentication.isAuthenticated = true;
$scope.template = $scope.templates[1];
$scope.userName = userName;
})
.error(function (data) {
$scope.loginError = "Invalid username/password combination";
setAuthenticating();
});
};