I have an angular app that uses Restangular and ui.router.state.
This is what I am currently doing
I have an Endpoint /Token that accepts a username/pass and gives
back a bearer token and some user info.
On successful login I save off the userinfo and token into a global var, user.current and I also set Restangular's default headers to include the bearer token:
Restangular.setDefaultHeaders({Authorization: "Bearer " + data.access_token});
When a user wants to access a route that has requiredAuth = true (set in the routeprovider as custom data like Access routeProvider's route properties) I check the user.current to see if its set.
a. If user.current is set, take them to the route.
b. If user.current is null or if the token would be expired (based on time) send them to /login
Problems/Concerns
If I Ctrl+R I lose my user info and the user has to log in again.
a. Should I be saving off the bearer token or credentials into a cookie or something and have a user service try to grab that in the event that user.current == null?
Am I even approaching this right? Seems like something that literally 100% of people using AngularJS would want to do, yet, I can't find an example that aligns with my situation. Seems like Angular would have mechanisms built in to handle some of this auth routing business...
When do I need to be getting a new token/verifying the current one? Do I just let anyone with devtools set something like isAuthorized = true so they can get to /admin/importantThings but then let the calls to /api/important things fail because they don't have a valid bearer token or should I be verifying that they have a valid token before I even let them get to that route?
You could put it in localStorage (always kept) or sessionStorage (cleared when browser is closed). Cookies are technically also a possibility, but don't fit your use case that well (your back end checks a separate header and not a cookie)
I guess there are many ways to skin a cat.
Always depend on server-side checks. Client-side checks might offer some increased usability, but you can never depend on them. If you have a lot of buttons that result in going to a login screen, it will be faster if you keep the client-side check. If this is more the exception than the rule, you could instead redirect to the login page when you get a 401 Unauthorized when calling your back end.
Here is an example of how you can manage your token:
/* global angular */
'use strict';
(function() {
angular.module('app').factory('authToken', ['$window', function($window) {
var storage = $window.localStorage;
var cachedToken;
var userToken = 'userToken';
var authToken = {
setToken: function(token) {
cachedToken = token;
storage.setItem(userToken, token);
},
getToken: function() {
if (!cachedToken) {
cachedToken = storage.getItem(userToken);
}
return cachedToken;
},
isAuthenticated: function() {
return !!authToken.getToken();
},
removeToken: function() {
cachedToken = null;
storage.removeItem(userToken);
}
};
return authToken;
}]);
})();
As you can see I use "$window.localStorage" to store my token. Like "Peter Herroelen" said in hist post.
Related
I've been trying to accomplish this task for quite some time but haven't got any breakthrough yet. I would be really thankful if anyone can help me out in this.
Current Situation:
I've two applications that I'm running in two sub-domains as
st.localhost:8080 and acm.localhost:8080
When a user tries to access either of the URLs, I search for a cookie named 'auth' using Angular $cookies service. If the cookie is defined or present, the user is redirected to original application. However, if the cookie is undefined, user is redirected to a login page(the login page resides in both the applications).
From the login page, after successful credentials check, I set the 'auth' cookie again with a random value. This cookie is supposed to be shared between the two sub-domains.
Express:
var express = require('express');
var httpProxy = require('http-proxy');
var vhost = require('vhost');
var app = express();
var proxy = httpProxy.createProxyServer();
app.get('/login', function(req, res) {
var randomNumber=Math.random().toString();
randomNumber=randomNumber.substring(2,randomNumber.length);
var expireDate = new Date();
expireDate.setDate(expireDate.getDate() + 1);
res.cookie('auth', randomNumber, { maxAge: 90000000, domain: 'localhost', httpOnly: false });
console.log('cookie created successfully');
res.send('Login successful');
});
app.use('/api', function(req, res) {
req.headers[ 'Authorization' ] = 'Basic dXNlcjpwYXNzd29yZA==';
console.log("Request cookies: " + req.cookie);
proxy.web(req, res, { target: 'restApiTarget' });
});
// ST application
app.use(vhost('st.localhost', express.static('./st')));
// ACM application
app.use(vhost('acm.localhost', express.static('./acm')));
app.listen(8080, function () {
console.log('Fweb server running on port 8080!');
});
Angular
Below login function is shared by both ST and ACM applications
$scope.login = function(formValid){
$scope.incorrectCredentials = false;
if(formValid){
$http.get('/login',
{
params: {
username: $scope.username,
password: $scope.password
},
headers : {
'Accept' : 'application/json'
}
}
).then(function(response){
$scope.incorrectCredentials = false;
var obj = $cookies.getObject('auth');
console.log("auth is: " + obj);
$state.go($stateParams.origin);
}, function(response){
$scope.incorrectCredentials = true;
});
}
}
Express is able to successfully create the cookie 'auth' as I can see the Set-Cookie header in the /login service response. However, the browser is not attaching this cookie to the subsequent API requests that I'm making from my application(say st.localhost). I'm not able to read this cookie through Angular as well.
var obj = $cookies.getObject('auth');
console.log("auth is: " + obj);
results in obj being undefined.
I've a feeling that there is something wrong in the way I'm setting the domain of the cookie as 'localhost' from one of the sub-domains.
Any suggestions on what I may be doing wrong?
Thanks in advance.
This is the second time I'm providing an answer to my own question. I guess I need to be more patient next time onwards before posting a question. Anyways, I hope this answer is helpful for people who are stuck in a similar situation like I was.
Firstly, I found out that it is possible to share a cookie between subdomains even if you create it in one of the subdomains. However, there were some posts/answers which said otherwise.
What one needs to do while creating a cookie in one of the subdomains is that the parameter 'domain' needs to be set as the parent domain value. For example, if you are creating a cookie in say st.testserver.com then while setting a sharable cookie in it, the 'domain' parameter must be set as '.testserver'.
However, if your parent domain is also the Top Level Domain(TLD), then you won't be able to create a shared cookie in the subdomain. This is exactly what was happening to me earlier when I posted this question.
When I was using st.localhost and trying to create a cookie with 'domain' as '.localhost', it wasn't allowing me to do so because localhost here is the TLD. But when I renamed my domain name to st.testserver.com, I was able to create the cookie with 'domain' as '.testserver.com' because it wasn't the TLD anymore.
I hope someone can validate this answer once and let me know if I provided any incorrect information.
Thanks.
Cookies is domain specific , if you want access across domain, you need to use some cross store like cross-storage etc.
I am storing authentication token in $rootScope . This token will be sent as part of header in every request via interceptor.
<code>
$rootScope.jwtToken=successfulResponse.data.body;
</code>
Interceptor code is as below :-
var bpInterceptor = function($q,$rootScope){
return {
request : function(config){
if($rootScope.jwtToken !== undefined){
config.headers.Authorization = $rootScope.jwtToken.token;
}
return config;
}
}
};
</code>
Q) Does $rootScope have different object for two browser sessions?
Angular code is executed client side only, so any state will disappear once you reload the page.
If you want to keep information between two user session, you have many options:
Keep that info in the URL using $location or location
Store that info in localStorage and retrieve it next time
Persist the information server side and query your server to get it back
Follow-up:
Once you get your token you can do:
localStorage.setItem('myToken', $rootScope.jwtToken);
And when you load your application, check if a token has been stored:
$rootScope.jwtToken = localStorage.getItem('myToken');
I have an angularjs application, in this application I have a login form when I submit it I call a rest service to authenticate the user to my server application, as following :
$http.get('http://localhost:8080/user', {
headers : credentials ? {
authorization : "Basic "
+ btoa(credentials.username + ":"
+ credentials.password)
} : {};
}).then(function(response) {
if (response.data.name) {
$rootScope.authenticated = true;
$rootScope.username=response.data.name;
$rootScope.roles=response.data.authorities;
$rootScope.sessionid=response.data.details.sessionId;
} else {
$rootScope.authenticated = false;
}
}, function() {
$rootScope.authenticated = false;
});
So the $rootScope will have all the informations about the authenticated user, but when I refresh my application, all those informations I attached to $rootScope are removed.
Notice that http://localhost:8080/user will always maintain the session.
How can I solve that ?
You can either store it in sessionStorage or just get the current user logged from server side. Then in order to retrieve them use an angular.run
angular.run(...function($http, $rootScope){
// either use session storage or $http to retrieve your data and store them in $rootScope.
// if you use $http i suggest you to store the promise of $http to be sure in your controller/route to wait that it has been resolved.
});
The fact that you're loosing what you store when using f5 is normal, you lose all javascript context when doing so. The usage of angular.run permit to use the request before any controller is called However with $http you may need to wait the end of the promise. So it's better to have a reference to the promise store in $rootScope to be able to use it in the javascript part. You can reference directly the data in the templates as they will get refresh as soon they will be loaded.
Check for Local and Session storage service. You can easily attach informations to variables with getters and setters, and retrieving them through page refreshing.
Example: You can set a variable like this:
localStorageService.set('myVar', data);
And then retrieve it in another controller, after refreshing, or elsewhere in your application with:
localStorageService.get('myVar');
It is rather well documented and easy to use.
So I made my site live and I am entering into the public realm where people aren't always nice. I just started learning about CSRF and saw that it was something I needed when I made my cakephp 3 site live. As seen here!
I added the csrf component and the security component to my site, but I have 1 major problem. Now, when users want to sign up they can't. I use a custom form for stripe to send payment, but also add a user using ajax to my database. The user gets added first and then the payment is processed and saves the order to the database as well.
According to stripe docs I add the token in a hidden value to the form after I click the submit button and can't help but notice that my new security is not allowing this to happen.
Since I am using ajax to send the post data to my users controller and adding a form input on submit,
How do I check the csrf token and make sure there isn't a security leak without disabling the security for the actions involved?
An example of how this is to be done would be greatly appreciated since examples seem to be lacking for doing this in cakephp 3. It is also hard for me to figure out how everything works since the cakephp 3 automagic adds the tokens to the forms and cookie. I am unsure how/where/what to check.
For pass X-CSRF-Token, use beforeSend parameter in your Ajax request, and define csrfToken value of cookie.
$.ajax({
url: '/foo/bar',
type: 'POST',
dataType: 'HTML',
data: data,
beforeSend: function(xhr){
xhr.setRequestHeader('X-CSRF-Token', csrfToken);
},
})
.done(function(data) {
alert('done !');
});
According to stripe docs I add the token in a hidden value to the form after I click the submit button and can't help but notice that my new security is not allowing this to happen.
Cake's CSRF token would have no effect when POSTing to another site.
Since I am using ajax to send the post data to my users controller and adding a form input on submit,
How do I check the csrf token and make sure there isn't a security leak without disabling the security for the actions involved?
The CSRF token is available in cookie named csrfToken, so read that token in your javascript and set X-CSRF-Token header for your AJAX request. The CsrfCompoment will do the checking.
using js function:
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
...
then
$.ajax
({
type: "Post",
url: "URL_HERE",
data: {some_data},
beforeSend: function(xhr){
xhr.setRequestHeader('X-CSRF-Token', getCookie('csrfToken'));
},
success: function (e) {
},
errors: function () {
}
});
I have implemented the authentication/authorization using AngularJS, Jersey REST and Spring Security. After logged in, call the following "create" method to store the user information:
.factory('Session', function () {
this.create = function (user) {
this.id = user.sessionId;
this.username = user.username;
this.userRoles = user.roles;
};
... ...
return this;
})
But, the problem is, every time I do one of the following 2 things, the stored information is lost and I have to login again:
Reload the whole page by pressing F5 or reload icon of browser
OR
Access the same URL from browser address bar
Could you please help me on how to reserve this information to guarantee login only once?
Thanks,
Check out sessionStorage. Not sure what the rest of your code looks like, but presumably your controller could save the Session created by your factory into sessionStorage.