Is there any way to manage user session using Angularjs?, I mean::
Session timeout - when system is idle.
Alerts(popup with message do you want to continue (yes or no ) ) when session is near to expire with option to resume session.
Redirect (or any other action) when trying to make a request if session has expired.
and after session timeout user has to be loggedout automatically.
Thanks in advance.
Try ng-idle. It's simple component where you can set the timeout and warning time before the timeout is reached. Then you can query server for user logout or something similar.
myApp.config(function(IdleProvider, KeepaliveProvider) {
IdleProvider.idle(900); // 15 min
IdleProvider.timeout(60);
KeepaliveProvider.interval(600); // heartbeat every 10 min
KeepaliveProvider.http('/api/heartbeat'); // URL that makes sure session is alive
});
myApp.run(function($rootScope, Idle) {
Idle.watch();
$rootScope.$on('IdleStart', function() { /* Display modal warning or sth */ });
$rootScope.$on('IdleTimeout', function() { /* Logout user */ });
});
In the above configuration, when user is idle for 900s (does not move mouse, press any key or button etc), warning is being displayed. It will then wait 60s and log out user (send request to a server that possibly destroys server session).
In order to make sure server session does not expire (even if everything user is doing is moving mouse) the Keepalive service will send a request to the server every 10 minutes. This time has to less than server session expiration time.
Demo Link : http://hackedbychinese.github.io/ng-idle/
Related
Using the Watson Assistant V2 API it is necessary to create a session handle first (create_session(assistantid)) which returns the session ID to use in the individual call to message(assistantid,sessionid,request). The session maintains the conversation state and therefore is the equivalent to the context id parameter of the V1 API.
Unfortunately it seems that there's a 5 minute session timeout by default. The response includes the following header attribute:
{...,"x-watson-session-timeout": [
"x-watson-session-timeout",
"session_timeout=300"
],...}
Any attempt to change this parameter by using the set_default_headers() method of the assistant object or by adding the optional header parameter to the create_session() call seems to have no effect. As I have not found any documentation of how to update this parameter correctly I just tried several alternatives:
1) self.assistant.set_default_headers({'x-watson-session-timeout':"['x-watson-session-timeout','session_timeout=3600']"})
2) self.assistant.set_default_headers({'x-watson-session-timeout':"'x-watson-session-timeout','session_timeout=3600'"})
3)self.assistant.set_default_headers({'x-watson-session-timeout':"session_timeout=3600"})
4)self.assistant.set_default_headers({'x-watson-session-timeout':"3600"})
5)self.assistant.set_default_headers({'session_timeout':"3600"})
Nothing is effective. The value of the parameter in the header of the response is still 300.
Do I use incorrect dict pairs to update the parameter? Is there another way to maintain the conversation state longer than 5 minutes using the V2 API? Is it not possible to change it at all?
The value of the session timeout is not under the control of the caller, and is in fact related to the Assistant plan you are using. For the free and standard the timeout is indeed 5 minutes. For the other plans the timeout is larger.
See Retaining information across dialog turns
The current session lasts for as long a user interacts with the assistant, and then up to 60 minutes of inactivity for Plus or Premium plans (5 minutes for Lite or Standard plans).
You can call watson assistant for an other session and resend your message. Keep your context...
Or just increase timeout limit in assistant setting on IBM Cloud with the right plan.
function createSession(end) {
assistant.createSession({
assistantId: watsonID }).then(res => {
sessionId=res.result.session_id;
if(end){
console.log("\x1b[32m%s\x1b[0m","new session "+sessionId);
}else{
console.log("session id :"+ sessionId);
console.log("http://"+host+":"+port);
}
});
}
createSession();
function callWatsonClient(payload,res) {
assistant.message(payload,function(err, data) {
if(data == null){
createSession(true);
//this not keep the context
var data ={result:{context:"",output:{generic:[{text:"session expirée, renvoyez le message"}]}}};
res.send(data);
}else{
//normal job
console.log("\x1b[33m%s\x1b[0m" ,JSON.stringify(data.result.output));
}
I have a setup with redux-oidc authenticating against an identity server.
I can log in, and I can see that silenRenew works as expected when the token expires.
There is one problem though.
If I open my site and let the computer go to sleep, when I get back after the expiration period, silent renew has failed with this error:
Frame window timed out
It does not try again once i wake up the computer. Not even when I reload the page.
Is this the expected behavior?
If so, what is the correct way of handling this so the site is not left dead?
If not, does anyone have any idea what I might be doing wrong?
I had faced similar issue , so i did a work-around which looks ugly but still works fine for me, look for comments in the code
this.userManager = new Oidc.UserManager(oidcSettings);
this.userManager.events.addAccessTokenExpiring(() =>
{
this.userManager.signinSilent({scope: oidcSettings.scope, response_type: oidcSettings.response_type})
.then((user: Oidc.User) =>
{
this.handleUser(user);
})
.catch((error: Error) =>
{
// Currently as a dirty work around (looks like problem with gluu server. Need to test with other IDP's as well)
// in order to get the new issued token I just calling getUser() straight after signinSilent().then() promise failing with frame time out
// https://github.com/IdentityModel/oidc-client-js/issues/362
this.userManager.getUser()
.then((user: Oidc.User) =>
{
this.handleUser(user);
});
});
});
Take a look at the logs. It usually tells you what's wrong. On all the situations I faced this error it was due I missed redirect uris on the server. Everything you setup on the client needs to be reflected on the server, otherwise, any callback (callback.html, popup.html, and silent.html for instance from the IS examples), session renewal will fail.
I've been reading answers about this problem for some time now but none of the solutions seem to work for my setup.
I have a nodeJS server in conjunction with express. I use Socket.io to send notifications to individual users. (frontend is Angular)
When a user logs in, he joins a room named after his email address (unique).
io.on('connection', function (socket) {
socket.on('join', function(user) {
//list of connected users
connected_users.push({socket_id: socket.id, email: user.email});
socket.join(user.email);
});
...
The join event is broadcasted from angular when a user logs in.
This way I can send notifications like so simply by using email addresses:
io.sockets.in(to).emit('notification', {
message: msg,
source: from,
destination: to,
event: data
});
When a user manually logs out I register the following event listener:
socket.on('leave', function(user) {
//remove the user from the list
var index = findUserConnected(socket.id);
if(index != null) {
connected_users.splice(index, 1);
}
socket.leave(user.email);
});
And finally there's the disconnect handler for whenever a user logs out or refreshes the page:
socket.on('disconnect', function() {
//if the user refreshes the page, he is still in the connected users list
var email = findEmailUserConnected(socket.id);
if(email != null) {
//we make him join back his room
socket.join(email);
}
});
Technically this works. On page refresh, the user joins back his room.
The problem is only on page refresh, notifications sent using io.sockets.in(email).emit('notification', {}); are not received even though the user is in his room.
Apparently a page refresh calls socket.disconnect() which generates a new socket_id. I'm not sure if there's a way to reassign a socket_id to a room or something similar.
Ok first of all receiving a 'disconnect' event on server means that connection on that socket is going to terminate. So, there is no use for making that same socket join back in a room as you are doing right now.
socket.on('disconnect', function() {
var email = findEmailUserConnected(socket.id);
if(email != null) {
socket.join(email); // this would never work because this socket connection is not going to exist anymore.
}
});
My suggestion would be to make sure that the user always joins back into the room(email) every time a new connection is made. It can be easily done by adding sending the join event on every new connection.
In your client side code do
var socket = io();
socket.on('connect', function() { // 'connect' event is received on client on every connection start.
socket.emit('join', user); // where 'user' is your object containing email.
})
This way it ensures that whenever a new connection is established the join event is sent to server and the 'socket.on('join',...)' listener in your server will add the new socket to the room. Hope this helps :)
I am trying to use the Channel API to push updates from server to the client. The flow is the user presses a button which triggers a server side action that generates a lot of logs. I want to display the logs to the user "in real time".
When I first load the page it I get all the messages, no problem. If I trigger the action a second time without refreshing the page in my browser, then all messages appear twice. Here is the set up portion of the channel that is tied to the page onLoad event. With resulting console logs I gathered that the onMessage() method is being invoked more than once when the page is not refreshed. Looks like I need to "kill" earlier sockets in some way, but could not find a way in the official documentation. Can someone point me in the right direction to get rid of the spurious messages?
// First fetch a token for the async communication channel and
// create the socket
$.post("/app/channels", {'op':'fetch', 'id' : nonce},
function (data, status, xhr) {
if (status == "success") {
data = JSON.parse(data);
token = data["token"];
console.log("Cookie: " + get_mp_did() + "; token: " + token);
var channel = new goog.appengine.Channel(token);
var handler = {
'onopen': onOpened,
'onmessage': onMessage,
'onerror': function() {
$("#cmd_output").append('Channel error.<br/>');
},
'onclose': function() {
$("#cmd_output").append('The end.<br/>');
$.post("/app/channels", {'op':'clear'});
}
};
var socket = channel.open(handler);
socket.onopen = onOpened;
socket.onmessage = onMessage;
}
});
onOpened = function() {
$("#cmd_output").empty();
};
onMessage = function(data) {
message = JSON.parse(data.data)['message'];
$("#cmd_output").append(message);
console.log('Got this sucker: ' + message);
}
If I understand your post and code correctly, the user clicks on a button which calls the $.post() function. The server code is responsible to create the channel in GAE as response to a /app/channels request. I think that your server in fact creates a new channel client ID / token with every subsequent request. Since the page is not reloaded, any subsequent request would add a new channel to this client. And all these channels would be still connected (hence, no page refresh).
I assume your server code has all channels associated to a user, and you send the message to a user utilizing all channels? Such pattern would result in this behavior. You can verify my assumption by clicking 3 or four times on the button with-out page refresh. The log output would be multiplied by the factor of 3 or 4.
I suggest, that you store the token in the client and on the server. Then make a modification to your client JS. If a channel is already created store the token value and provide it to any subsequent request to /app/channels. Modify the server so it will not create a new channel, if a token is provided with the request. If the token links to an existing valid channel, re-use the channel and return the same token in the response. You may need to add some more details for disconnected or expired channels, maybe also a cron-job to delete all expired channels after a while.
I understand the flow of JWT and a single page application in terms of login and JWT issuance. However, if the JWT has a baked in expiry, AND the server isn't issuing a new JWT on each request, what is the best way for renewing? There is a concept of refresh tokens, but storing such a thing in a web browser sounds like a golden ticket.
IE I could easily go into a browsers local storage and steal a refresh token. Then I could go to another computer and issue myself a new token. I feel like there would need to be a server session in a db that's referenced in the JWT. Therefore the server could see if the session ID is still active or invalidated by a refresh token.
What are the secure ways to implement JWT in a SPA and handling new token issuance whilst the user is active?
Renewing the token every 15 minutes (if it lives for 30) works if you don't have another restriction in your server in which you need to check for 1 hour inactivity to log the user out. If you just want this short lived JWT and keep on updating it, it'd work.
I think one of the big advantages of using JWT is to actually NOT need a server session and therefore not use the JTI. That way, you don't need syncing at all so that'd be the approach I'd recommend you following.
If you want to forcibly logout the user if he's inactive, just set a JWT with an expiration in one hour. Have a $interval which every ~50 minutes it automatically gets a new JWT based on the old one IF there was at least one operation done in the last 50 minutes (You could have a request interceptor that just counts requests to check if he's active) and that's it.
That way you don't have to save JTI in DB, you don't have to have a server session and it's not a much worse approach than the other one.
What do you think?
I think for my implementation I'm going to go with, after a bit of search, is...
Use case:
JWT is only valid for 15 minutes
User session will timeout after 1 hour of inactivity
Flow:
User logs in and is issued a JWT
JWT has a 15 minute expiration with claim 'exp'
JWT JTI is recorded in db has a session of 1 hour
After a JWT expires (after 15 min):
Current expired JWT will be used # a /refresh URI to exchange for a new one. The expired JWT will only work at the refresh endpoint. IE API calls will not accept an expired JWT. Also the refresh endpoint will not accept unexpired JWT's.
JTI will be checked to see if its been revoked
JTI will be checked to see if its still within 1 hour
JTI session will be deleted from DB
New JWT will be issued and new JTI entry will be added to the db
If a user logs out:
JWT is deleted from client
JTI is deleted from db so JWT cannot be refreshed
With that said, there will be database calls every 15 minutes to check a JTI is valid. The sliding session will be extended on the DB that tracks the JWT's JTI. If the JTI is expired then the entry is removed thus forcing the user to reauth.
This does expose a vulnerability that a token is active for 15 minutes. However, without tracking state every API request I'm not sure how else to do it.
I can offer a different approach for refreshing the jwt token.
I am using Angular with Satellizer and Spring Boot for the server side.
This is the code for the client side:
var app = angular.module('MyApp',[....]);
app.factory('jwtRefreshTokenInterceptor', ['$rootScope', '$q', '$timeout', '$injector', function($rootScope, $q, $timeout, $injector) {
const REQUEST_BUFFER_TIME = 10 * 1000; // 10 seconds
const SESSION_EXPIRY_TIME = 3600 * 1000; // 60 minutes
const REFRESH_TOKEN_URL = '/auth/refresh/';
var global_request_identifier = 0;
var requestInterceptor = {
request: function(config) {
var authService = $injector.get('$auth');
// No need to call the refresh_token api if we don't have a token.
if(config.url.indexOf(REFRESH_TOKEN_URL) == -1 && authService.isAuthenticated()) {
config.global_request_identifier = $rootScope.global_request_identifier = global_request_identifier;
var deferred = $q.defer();
if(!$rootScope.lastTokenUpdateTime) {
$rootScope.lastTokenUpdateTime = new Date();
}
if((new Date() - $rootScope.lastTokenUpdateTime) >= SESSION_EXPIRY_TIME - REQUEST_BUFFER_TIME) {
// We resolve immediately with 0, because the token is close to expiration.
// That's why we cannot afford a timer with REQUEST_BUFFER_TIME seconds delay.
deferred.resolve(0);
} else {
$timeout(function() {
// We update the token if we get to the last buffered request.
if($rootScope.global_request_identifier == config.global_request_identifier) {
deferred.resolve(REQUEST_BUFFER_TIME);
} else {
deferred.reject('This is not the last request in the queue!');
}
}, REQUEST_BUFFER_TIME);
}
var promise = deferred.promise;
promise.then(function(result){
$rootScope.lastTokenUpdateTime = new Date();
// we use $injector, because the $http creates a circular dependency.
var httpService = $injector.get('$http');
httpService.get(REFRESH_TOKEN_URL + result).success(function(data, status, headers, config) {
authService.setToken(data.token);
});
});
}
return config;
}
};
return requestInterceptor;
}]);
app.config(function($stateProvider, $urlRouterProvider, $httpProvider, $authProvider) {
.............
.............
$httpProvider.interceptors.push('jwtRefreshTokenInterceptor');
});
Let me explain what it does.
Let's say we want the "session timeout" (token expiry) to be 1 hour.
The server creates the token with 1 hour expiration date.
The code above creates a http inteceptor, that intercepts each request and sets a request identifier. Then we create a future promise that will be resolved in 2 cases:
1) If we create for example a 3 requests and in 10 seconds no other request are made, only the last request will trigger an token refresh GET request.
2) If we are "bombarded" with request so that there is no "last request", we check if we are close to the SESSION_EXPIRY_TIME in which case we start an immediate token refresh.
Last but not least, we resolve the promise with a parameter. This is the delta in seconds, so that when we create a new token in the server side, we should create it with the expiration time (60 minutes - 10 seconds). We subtract 10 seconds, because of the $timeout with 10 seconds delay.
The server side code looks something like this:
#RequestMapping(value = "auth/refresh/{delta}", method = RequestMethod.GET)
#ResponseBody
public ResponseEntity<?> refreshAuthenticationToken(HttpServletRequest request, #PathVariable("delta") Long delta, Device device) {
String authToken = request.getHeader(tokenHeader);
if(authToken != null && authToken.startsWith("Bearer ")) {
authToken = authToken.substring(7);
}
String username = jwtTokenUtil.getUsernameFromToken(authToken);
boolean isOk = true;
if(username == null) {
isOk = false;
} else {
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
isOk = jwtTokenUtil.validateToken(authToken, userDetails);
}
if(!isOk) {
Map<String, String> errorMap = new HashMap<>();
errorMap.put("message", "You are not authorized");
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(errorMap);
}
// renew the token
final String token = jwtTokenUtil.generateToken(username, device, delta);
return ResponseEntity.ok(new JwtAuthenticationResponse(token));
}
Hope that helps someone.