Hi I followed a tutorial on the web. Everything work but I would encode bas64 with a secret or jwt but I don't know how. Can you help me please ?
(function () {
'use strict';
angular
.module('app')
.factory('AuthenticationService', Service);
function Service($http, $localStorage) {
var service = {};
service.Login = Login;
service.Logout = Logout;
return service;
function Login(username, password, callback) {
$http.post('/api/authenticate', { username: username, password: password })
.success(function (response) {
// login successful if there's a token in the response
if (response.token) {
// store username and token in local storage to keep user logged in between page refreshes
$localStorage.currentUser = { username: username, token: response.token };
// add jwt token to auth header for all requests made by the $http service
$http.defaults.headers.common.Authorization = 'Bearer ' + response.token;
// execute callback with true to indicate successful login
callback(true);
} else {
// execute callback with false to indicate failed login
callback(false);
}
});
}
function Logout() {
// remove user from local storage and clear http auth header
delete $localStorage.currentUser;
$http.defaults.headers.common.Authorization = '';
}
}
})();
and my service :
function run($rootScope, $http, $location, $localStorage) {
// keep user logged in after page refresh
if ($localStorage.currentUser) {
$http.defaults.headers.common.Authorization = 'Bearer ' + $localStorage.currentUser.token;
}
// redirect to login page if not logged in and trying to access a restricted page
$rootScope.$on('$locationChangeStart', function (event, next, current) {
var publicPages = ['/login'];
var restrictedPage = publicPages.indexOf($location.path()) === -1;
if (restrictedPage && !$localStorage.currentUser) {
$location.path('/login');
}
});
}
and the nodeJs :
function setupFakeBackend($httpBackend) {
var testUser = { username: 'test', password: 'test', firstName: 'Test', lastName: 'User' };
// fake authenticate api end point
$httpBackend.whenPOST('/api/authenticate').respond(function (method, url, data) {
// get parameters from post request
var params = angular.fromJson(data);
// check user credentials and return fake jwt token if valid
if (params.username === testUser.username && params.password === testUser.password) {
return [200, { token: 'fake-jwt-token' }, {}];
} else {
return [200, {}, {}];
}
});
$httpBackend.whenGET(/^\w+.*/).passThrough();
}
Thank you for your answer :)
JSON Web Tokens are composed of three JSON objects encoded to base 64 seperated by a . character.
header.payload.signiture
The example found at jwt.io eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ decodes to three JSON strings separated by .
If you wish to pull out the seperate componets you must first split the String
// es6
let myJwt = getToken();
let jwtParts = myJwt.split('.').map(part => btoa(part));
console.log(`header: ${jwtParts[0]}, payload: ${jwtParts[1]}, sig: ${jwtParts[2]}`)
On the server side you should be using the signing (for login) and verifying functions (for subsequent authentication) found in your JWT library i.e https://github.com/auth0/node-jsonwebtoken
Let me know if that was not quite what you are looking for
Related
I have an angularJS web application which uses the web api and jwt. I followed tutorial on the internet here > angularjs-jwt-auth everything is working fine when I login using credentials from my own api,returns token on console as it should.
But the issue comes when I try to register a new user, nothing happens and console is throwing me an error Failed to load resource: the server responded with a status of 401 (Unauthorized). When I use api from tutorial is working fine so am little bit lost, please help!!
My code
(function () {
function authInterceptor(API, auth) {
return {
// automatically attach Authorization header
request: function (config) {
config.headers = config.headers || {};
var token = auth.getToken();
if (config.url.indexOf(API) === 0 && token) {
config.headers.Authorization = 'Bearer ' + token;
}
return config;
},
response: function (res) {
if (res.config.url.indexOf(API) === 0 && res.data.token) {
auth.saveToken(res.data.token);
}
return res;
},
}
}
// Services
function authService($window) {
var srvc = this;
srvc.parseJwt = function (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace('-', '+').replace('_', '/');
return JSON.parse($window.atob(base64));
};
srvc.saveToken = function (token) {
$window.localStorage['jwtToken'] = token
};
srvc.logout = function (token) {
$window.localStorage.removeItem('jwtToken');
};
srvc.getToken = function () {
return $window.localStorage['jwtToken'];
};
srvc.saveUsername = function (username) {
$window.localStorage['username'] = username;
}
srvc.getUsername = function () {
return $window.localStorage['username'];
}
srvc.isAuthed = function () {
var token = srvc.getToken();
if (token) {
var params = srvc.parseJwt(token);
return Math.round(new Date().getTime() / 1000) <= params.exp;
} else {
return false;
}
}
}
function userService($http, API, auth) {
var srvc = this;
srvc.register = function (first_name, last_name, email, password, role, gender, phone_number) {
return $http.post(API + '/api/v1/users/', { // <-- Registration link here
first_name: first_name,
last_name: last_name,
email: email,
password: password,
role: role,
gender: gender,
phone_number: phone_number
});
}
srvc.login = function (username, password) {
return $http.post(API + '/api/v1/token/auth/', { // <-- Login link here
username: username,
password: password
});
};
return srvc;
}
// We won't touch anything in here
function MainCtrl(user, auth, $location, $state, $rootScope) {
var self = this;
function handleRequest(res) {
var token = res.data ? res.data.token : null;
if (token) {
$location.path('/portfolio');
console.log('Bearer:', token);
auth.saveUsername($scope.username);
$rootScope.username = auth.getUsername();
}
// self.message = res.data.message;
}
self.login = function () {
user.login(self.username, self.password)
.then(handleRequest, handleRequest)
}
self.register = function () {
user.register(self.first_name, self.last_name, self.username, self.email, self.password, self.role, self.gender, self.phone_number)
.then(handleRequest, handleRequest)
}
self.logout = function () {
auth.logout && auth.logout();
$location.path('/login');
}
self.isAuthed = function () {
return auth.isAuthed ? auth.isAuthed() : false
}
}
angular
.module('App', ['ui.router'])
.factory('authInterceptor', authInterceptor)
.service('user', userService)
.service('auth', authService)
.constant('API', 'link-to-my-api') // <-- API Link here
.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
Since JWT verification is working for the tutorial's API, the fault lies in your authentication server i.e. creation/verification of JWT token) and not in the snippet above (the client handling)
You are getting 401 due to an incorrect JWT token created or incorrect validation of the JWT token
A brief explaination of how JWT authenticates.
In the example, the user first signs into the authentication server using the authentication server’s login system.
The authentication server then creates the JWT and sends it to the user.
When the user makes API calls to the application, the user passes the JWT along with the API call.
In this setup, the application server would be configured to verify that the incoming JWT are created by the authentication server
I put together the following controller/service in Angularjs to contact an authentication service. I want to leverage this to restrict other pages of the Angular application to users who have authenticated via this service, but I am not sure how to do that. I am hoping someone can point me in the right direction. Below is my controller and service.
angular.module('App').controller('LoginController', function ($scope, $rootScope, $window, AuthenticationService) {
//function that gets called when the form is submitted.
$scope.submit = function () {
var promise = AuthenticationService.login($scope.user, $scope.password);
promise.then(function () {
var success = AuthenticationService.isAuthenticated();
if (success === true) {
$window.location.href = "/welcome.html";
}
});
}
});
app.service('AuthenticationService', function ($http) {
//Used to track if the user was able to successfully authenticate.
var auth = false;
var token = undefined;
//Had to put this in because I am running into weird behavior when attempting to retrieve a value from response.headers('value');.
//When assiging this to a var, it would always come out as a null value. If you send the value into a function without assigning
//it to a var, the value would be there as expected. For instance, console.log(response.headers('Authorization')); would work, but
//token = response.headers('Authorization'); would result in a null value. The function below is a workaround.
var getResponseHeader = function (x) { return x; }
//Makes a call to the WEB API Authentication service with the username/password and attempts to authenticate.
var login = function (user, pass) {
var input = { UserName: user, Password: pass };
return $http({
url: "/api/Authentication/Login/{credentials}",
method: "POST",
data: JSON.stringify(input),
dataType: "json"
}).then(function (response) {
if (response.status === 200) { //Call to the service was successful.
//This makes no sense. See comment for getResponseHeader function.
token = getResponseHeader(response.headers('Authorization'));
//If the authentication was successful, 'token' will have a value. If it was not successful, it will be null.
if (token) {
auth = true;
}
}
//There was an error when attempting to authenticate. Alert(response) is there for debugging purposes.
//This will be replaced with a user-friendly error message when completed.
}, function (response) {
alert(response);
});
}
//Logs the user out by removing the token and setting auth back to false
var logout = function (sessionid) {
auth = false;
token = undefined;
}
//Accessor for the 'auth' variable.
var isAuthenticated = function () { return auth; }
//Accessor for the token.
var getToken = function () { return token; }
return {
login: login,
logout: logout,
isAuthenticated: isAuthenticated,
token: getToken
};
});
The service/controller work to authenticate, however anyone can browse to 'welcome.html' or any other page in the application. How do I limit this to users who have successfully authenticated?
I would recommend to use angular-permissions
you set up your states like so:
.state('home', {
url: '/',
templateUrl: 'templates/home.html',
parent: 'parent',
data: {
permissions: { //section for permissions
only: 'isAuthorized', // only allow loged in users
redirectTo: 'login' // if not allowed redirect to login page
}
},
})
.state('login', {
url: '/login',
templateUrl: 'templates/login.html',
parent: 'login-parent',
data: {
permissions: {
only: 'anonymous',
redirectTo: 'home'
}
}
})
and set up two groups for authentification:
//A Group for not logged in users
.run(function(PermPermissionStore) {
PermPermissionStore
.definePermission('anonymous', function() {
// Do your authentification here
// you can sett up your funtions elsewere and call them here
// return true if unauthorized
var auth = auth_function()
if (auth === true) {
return false
} else { return true }
})
})
//group for authentificated users
.run(function(PermPermissionStore) {
PermPermissionStore
.definePermission('isAuthorized', function() {
// Do your authentification here
// return true if authorized
var auth = auth_function()
if (auth === true) {
return true
} else { return false }
})
})
On your root app js file run method check for auth value and redirect if not authenticated, the run method on your app.js is a good place to set this watch:
SEE FIDDLE EXAMPLE OF YOUR CODE
.run(['AuthenticationService', '$location', '$rootScope', function (AuthenticationService, $location, $rootScope) {
$rootScope.$watch(function () {
if (!AuthenticationService.isAuthenticated())
$location.path("/login");
return $location.path();
});
}])
This simple watch will check the auth value by calling your isAuthenticated method in the AuthenticationService, so you can be certain no unauthed user will access any page, you should change the logic by adding another condition to check the routes so to only limit access to specifyed pages.
I create a jwt token and stored in database. when login successfully done i want to redirect to home page with token. how could i do that.
My Nodejs file.
app.post('/authenticate', function(req, res, next) {
User.findOne({name: req.body.name}, function(err, user) {
if (err) {throw err;}
if (!user) {
res.json({ success: false, message: 'Authentication failed. User not found.' });
} else if (user) {
// check if password matches
if (user.password !== req.body.password) {
res.json({ success: false, message: 'Authentication failed. Wrong password.' });
} else {
// if user is found and password is right
// create a token
var token = jwt.sign(user, app.get('superSecret'), {
expiresIn: 1440 // expires in 24 hours
});
// return the information including token as JSON
res.json({
success: true,
message: 'Enjoy your token!',
token: token
});
}
}
});
});
My angularjs file
var app = angular.module('loginApp', []);
// Controller function and passing $http service and $scope var.
app.controller('loginCtrl', function($scope, $http) {
// create a blank object to handle form data.
$scope.user = {};
// calling our submit function.
$scope.submitForm = function() {
// Posting data to file
$http.post('/tokken/login/', $scope.user).then(function (response) {
//$http.defaults.headers.common['Token'] = token
if (response.errors) {
// Showing errors.
$scope.errorName = response.errors.name;
$scope.erroPassword = response.errors.password;
} else {
$scope.message = response.data;
}
});
};
});
I print token value in same login page. I want to redirect to another page with the token
I'm very new to angular, so my knowledge is based on tutorials and even then I don't succeed.
I need to authenticate using a google account. That works, I get a token where my api calls could be authorized with. But after login the pop up window should dismiss and I should be redirected to the homepage. This doesn't work.
this is my controller
angular.module('MyApp').controller('loginController', ['$scope', '$auth', '$location','loginService', loginController]);
function loginController($scope, $auth, $location, loginService) {
$scope.authenticate = function(provider) {
$auth.authenticate(provider).then(function(data) {
loginService.saveToken(data.data.token);
console.log('You have successfully signed in with ' + provider + '!');
$location.path('http://localhost/#/home');
});
};
};
in app.js I have my configuration. this is not my work but a friend who is an intern as wel as me, he is responsible for a mobile application, where he uses the same function to get his token, and it works.
authProvider.google({
clientId: CLIENT_ID,
redirectUri: 'http://localhost:3000/api/users/signIn'
});
$authProvider.storage = 'localStorage'; // or 'sessionStorage'
$authProvider.loginRedirect = 'http://localhost/#/home';
This is the controller in node where the url is redirected to (google developer console)
router.get('/signIn', function(req, res) {
//console.log(req);
var code = req.query.code;
oauth2Client.getToken(code, function(err, tokens) {
if (!err) {
https.get("https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=" + tokens.access_token, function(response) {
// Continuously update stream with data
var body = '';
response.setEncoding('utf8');
response.on('data', function(d) {
body += d;
});
// Data fetched
response.on('end', function() {
var parsed = JSON.parse(body);
// Check if client_id is from the right app
if (parsed.issued_to == '343234242055-vd082vo0o8r8lmfvp1a973736fd98dht.apps.googleusercontent.com') {
User.getGoogleId(parsed.user_id, function(err, user) {
if (err) {
res.status(500).send({
message: 'not authorized app'
});
}
// No user returned, create one
if (!user) {
// Request user info
oauth2Client.setCredentials(tokens);
plus.people.get({
userId: 'me',
auth: oauth2Client
}, function(err, plusUser) {
if (err) res.status(500).send({
message: 'not authorized app'
});
else {
// Create new user
User.create(plusUser.name.givenName, plusUser.name.familyName, (plusUser.name.givenName + "." + plusUser.name.familyName + "#cozmos.be").toLowerCase(), parsed.user_id, function(err, newUser) {
if (err) res.status(500).send({
message: 'not authorized app'
});
else {
res.statusCode = 200;
return res.send({
response: 'Success',
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
token: tokens.access_token
});
}
});
}
});
} else {
// Return user
res.statusCode = 200;
return res.send({
response: 'Success',
id: user._id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
token: tokens.access_token
});
}
});
}
// if not right app, return unauthorized response
else {
res.status(500).send({
message: 'not authorized app'
});
}
});
});
}
});
});
So I login, I get asked to give permission to the application to use my account info, I get a json response where I can see my name, email and token, and that's it
Even within the company where I work, no one could find an answer. So I came with a solution myself. I don't use satellizer anymore.
.when('/access_token=:access_token', {
template: '',
controller: function($window, $http, $location, $rootScope) {
var hash = $location.path().substr(1);
var splitted = hash.split('&');
var params = {};
for (var i = 0; i < splitted.length; i++) {
var param = splitted[i].split('=');
var key = param[0];
var value = param[1];
params[key] = value;
$rootScope.accesstoken = params;
}
console.log(params.access_token);
var json = {
Token: params.access_token
};
$window.localStorage['token'] = params.access_token;
$http.post('http://localhost:3000/api/users/signIn', json).success(function(data, status) {
console.log(data);
}).error(function(err) {
console.log(err);
});
$location.path("/home");
}
/*controller: 'createNewsFeed',
templateUrl: 'homepage.html'*/
}).
So redirect the page by itself. Because the authentication works on the backend side, I can get a access token, which is the only thing I really need for future use of my rest api. I defined a route where, after receiving the json with the token, my browser is manually redirected to with $window.location. So when that page is loaded (not visible for the user, it goes too fast to notice) I analyse the token, save the token, analyse authentication, when that is successful I manually redirect to the homepage.
I have a SPA app that uses Angular and Breeze, I need to implement the login functionality and I am new to Angular/Breeze. My architecture/code structure is as mentioned below:
login.html --> login.js -->datacontext/Service.js--->entityManagerFactory-->breezecontroller.cs -->repository->dbcontext-->database.
I am facing following challenges:
I am unable to show the login page as default, I am always getting Dashboard as a default page. I am looking for where I can route to login page.
2.breezecontroller -- This is inside controller, do I need to write my login method here?
All in all, I am looking for a complete login functionality implementation which following my architecture/code structure.
Here is a description of an approach that can be used in an Angular-based SPA. This particular example uses token-based OAuth authentication, but could be adapted to other authentication schemes. It is loosely based on the approach described at Authentication in AngularJS (or similar) based application
Some highlights are:
Authentication is managed through an auth service.
HTTP requests are intercepted, and:
When a 401 (access denied) error is detected and no user is logged in, an auth:login event is emitted (note - not broadcasted) on $rootScope
If a 401 error is detected while a user is logged in and an OAuth refresh token is available, an attempt is made to get a new access token based on the refresh token. An auth:login event is only emitted if the token cannot be refreshed.
Once a user has logged in, an Authorization header containing the user's access token is inserted onto each HTTP request so that the server can authenticate the user.
The application should watch for auth:login events and prompt the user for credentials. (I use an Angular-UI Bootstrap modal dialog for doing this.) Once credentials have been provided, the auth service's login function must be called to complete the login. After login is called, all pending HTTP requests that initially failed with a 401 error are retried. Alternatively, the auth service's loginCancelled function can be called to cancel the login, which will reject all pending HTTP requests.
For example:
angular.module('app', ['auth'])
.run(['$rootScope', 'auth', function ($rootScope, auth) {
$rootScope.$on(auth.options.loginRequiredEvent, function (event, details) {
// Display login dialog here, which will ultimately
// call `auth.login` or `auth.loginCancelled`
});
auth.restoreAuthDataFromStorage();
}]);
Here is an example of calling auth.login once the user has provided credentials:
auth.login(userName, password, isPersistent)
.success(function () {
// Dismiss login dialog here
})
.error(function (data, status) {
if (status === 401 || (data && data.error === 'invalid_grant')) {
failureMessage = 'Log in failed: Bad username or password';
} else {
failureMessage = 'Log in failed: Unexpected error';
}
});
Details of the logged in user are stored in window.sessionStorage or window.localStorage (based on whether a persistent login has been requested) to be able to be accessed across page loads.
Finally, here is the auth service itself.
var module = angular.module('auth');
module.provider('auth', function () {
var authOptions = {
tokenUrl: '/OAuthToken',
loginRequiredEvent: 'auth:loginRequired',
logoffEvent: 'auth:logoff',
loginEvent: 'auth:login',
authTokenKey: 'auth:accessToken'
};
this.config = function (options) {
angular.extend(authOptions, options);
};
// Get the auth service
this.$get = ['$rootScope', '$http', '$q', function ($rootScope, $http, $q) {
var authData = {
// Filled as follows when authenticated:
// currentUserName: '...',
// accessToken: '...',
// refreshToken: '...',
};
var httpRequestsPendingAuth = new HttpRequestsPendingAuthQueue();
// Public service API
return {
login: login,
refreshAccessToken: refreshAccessToken,
loginCancelled: loginCancelled,
logoff: logoff,
currentUserName: function () { return authData.currentUserName; },
isAuthenticated: function () { return !!authData.accessToken; },
getAccessToken: function () { return authData.accessToken; },
restoreAuthDataFromStorage: restoreAuthDataFromStorage,
_httpRequestsPendingAuth: httpRequestsPendingAuth,
options: authOptions,
};
function isAuthenticated() {
return !!authData.accessToken;
};
function restoreAuthDataFromStorage() {
// Would be better to use an Angular service to access local storage
var dataJson = window.sessionStorage.getItem(authOptions.authTokenKey) || window.localStorage.getItem(authOptions.authTokenKey);
authData = (dataJson ? JSON.parse(dataJson) : {});
}
function accessTokenObtained(data) {
if (!data || !data.access_token) {
throw new Error('No token data returned');
}
angular.extend(authData, {
accessToken: data.access_token,
refreshToken: data.refresh_token
});
// Would be better to use an Angular service to access local storage
var storage = (authData.isPersistent ? window.localStorage : window.sessionStorage);
storage.setItem(authOptions.authTokenKey, JSON.stringify(authData));
httpRequestsPendingAuth.retryAll($http);
}
function login(userName, password, isPersistent) {
// Data for obtaining token must be provided in a content type of application/x-www-form-urlencoded
var data = 'grant_type=password&username=' + encodeURIComponent(userName) + '&password=' + encodeURIComponent(password);
return $http
.post(authOptions.tokenUrl, data, { ignoreAuthFailure: true })
.success(function (data) {
authData = {
currentUserName: userName,
isPersistent: isPersistent
};
accessTokenObtained(data);
$rootScope.$emit(authOptions.loginEvent);
})
.error(function () {
logoff();
});
}
function refreshAccessToken() {
if (!authData.refreshToken) {
logoff();
return $q.reject('No refresh token available');
}
// Data for obtaining token must be provided in a content type of application/x-www-form-urlencoded
var data = 'grant_type=refresh_token&refresh_token=' + encodeURIComponent(authData.refreshToken);
return $http
.post(authOptions.tokenUrl, data, { ignoreAuthFailure: true })
.success(function (data) { accessTokenObtained(data); })
.error(function () { logoff(); });
}
function loginCancelled() {
httpRequestsPendingAuth.rejectAll();
}
function logoff() {
// Would be better to use an Angular service to access local storage
window.sessionStorage.removeItem(authOptions.authTokenKey);
window.localStorage.removeItem(authOptions.authTokenKey);
if (isAuthenticated()) {
authData = {};
$rootScope.$emit(authOptions.logoffEvent);
}
}
// Class implementing a queue of HTTP requests pending authorization
function HttpRequestsPendingAuthQueue() {
var q = [];
this.append = function (rejection, deferred) {
q.push({ rejection: rejection, deferred: deferred });
};
this.rejectAll = function () {
while (q.length > 0) {
var r = q.shift();
r.deferred.reject(r.rejection);
}
};
this.retryAll = function ($http) {
while (q.length > 0) {
var r = q.shift();
retryRequest($http, r.rejection.config, r.deferred);
}
};
function retryRequest($http, config, deferred) {
var configToUse = angular.extend(config, { ignoreAuthFailure: true });
$http(configToUse)
.then(function (response) {
deferred.resolve(response);
}, function (response) {
deferred.reject(response);
});
}
}
}];
});
module.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(['$injector', '$rootScope', '$q', function ($injector, $rootScope, $q) {
var auth;
return {
// Insert an "Authorization: Bearer <token>" header on each HTTP request
request: function (config) {
auth = auth || $injector.get('auth');
var token = auth.getAccessToken();
if (token) {
config.headers = config.headers || {};
config.headers.Authorization = 'Bearer ' + token;
}
return config;
},
// Raise a "login required" event upon "401 access denied" responses on HTTP requests
responseError: function(rejection) {
if (rejection.status === 401 && !rejection.config.ignoreAuthFailure) {
var deferred = $q.defer();
auth = auth || $injector.get('auth');
auth._httpRequestsPendingAuth.append(rejection, deferred);
if (auth.isAuthenticated()) {
auth.refreshAccessToken().then(null, function () {
$rootScope.$emit(auth.options.loginRequiredEvent, { message: 'Login session has timed out. Please log in again.' });
});
} else {
// Not currently logged in and a request for a protected resource has been made: ask for a login
$rootScope.$emit(auth.options.loginRequiredEvent, { rejection: rejection });
}
return deferred.promise;
}
// otherwise, default behaviour
return $q.reject(rejection);
}
};
}]);
}]);