Socket io socket won't carry jwt token in angular factory - angularjs

I am trying socket.io authorization with jwt in my MEAN stack project. I have problem about socket object won't carry jwt token after I log out and relog in.
when I log in , token will store in local storage
UsersSvc.login($scope.user).success(function(data){
if (data.success) {
store.set('jwt', data.token);
}
then angular socket factory will retrieve jwt from local storage and send to server
.factory('SocketSvc',[ 'store',
function (store) {
this.initSocket = function(){
return io.connect('http://localhost:3000',{ query : 'token=' + store.get('jwt')});
};
var socket = this.initSocket();
return {
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
}
});
})
},
};
however; after I logged out
$scope.logout = function(){
var account = UsersSvc.currentAccount();
SocketSvc.emit('logout', { account : account});
$state.go('anon.login');
};
then jwt toke will be remove from local storage
if (toState.name == "anon.login") {
store.remove('jwt');
when I log in agin, token in socket query is gone while you logged in successfully. I am dealing with this problems several days. I don't know what happened.
The only way let server get token again is that refresh the page agin manually.
or close the tab and open a new page then log in agin.
My assumption is the problem of angular factory since it is singleton that it's can't be modified.
I don't know it is right or wrong or how to solve the problem. If you have any suggestion. please let me know

I solved this by wrapping the initSocket code into a function in the return block
you are right in pointing that token is null as the user is not logged in.
this.initSocket = function(){
return io.connect('http://localhost:3000',{ query : 'token=' + store.get('jwt')});
};
var socket = this.initSocket();
the above should go inside return block and called once in any Controller for initializing the socket with a valid token
My soultion:
angular.module('mean1App')
.factory('socket', function(socketFactory, $rootScope, Auth) {
var socket = null;
return {
socket: socket,
init: function(){
var ioSocket = io.connect('http://localhost:9000', {
// Send auth token on connection, you will need to DI the Auth service above
'query': 'token=' + Auth.getToken(),
path: '/socket.io-client'
});
socket = socketFactory({
ioSocket: ioSocket
});
},
...
...blah
});

Related

Socket.io - angular.js. - always disconnected with "ping timeout"

Trying to connect with Angular js + socket.io to the server (Node.js - nestsJS)
I have both React and Angular app
in react app everything is work
in Angularjs I got always "ping timeout" and then it try to recconect - on the server I saw the connection and it pass the authentication flow.
when I going to the network tab(WS) I got my events after authenticated in the backend so it looks like its a client issue
so every ~30sec the console output disconnect + reason "ping timeout"
Angular code -socket service
.factory('socket', socket);
socket.$inject = ["$rootScope"];
function socket($rootScope) {
const options = {
transports:['websocket'],
// allowUpgrades: false,
query: {
token : "token",
},
forceNew: true
}
var socket = io.connect('/', { ...options, path: `/socket.io` });
return {
on: function(eventName, callback) {
socket.on(eventName, function() {
var args = arguments;
$rootScope.$apply(function() {
callback.apply(socket, args);
});
});
},
emit: function(eventName, data, callback) {
socket.emit(eventName, data, function() {
var args = arguments;
$rootScope.$apply(function() {
if (callback) {
callback.apply(socket, args);
}
});
})
}
};
controller:
socket.on('connect', function (data) {
console.log("connect")
});
socket.on('connection', function (data) {
console.log("connect")
});
socket.on('disconnect', function (data) {
console.log("disconnect")
console.log(data)
});
socket.on('events-test', function (data) {
console.log("test")
console.log(data)
})
in the network tab i can see the "events-test" events and it will create a new "ws" tab evrey reconnection
Solved by change the socket-io client version to 2.3.0
Use socketio version 2 on the client side to match the server. 3 and 4 are incompatible with server v2

Node and Angular socket error in browser console

I am using Node(server) + Angular(client) to implement socket in my application.
Angular bower.json socket components : "angular-socket-io":
"^0.7.0","socket.io-client": "^1.7.2",
Node js socket component in package.json : "socket.io": "^1.7.3",
I am seeing this below web socket error in my chrome browser console :
WebSocket connection to
'wss://ireporter.apple.com/uitracker/socket.io/?EIO=3&transport=websocket&sid=4qBY-qoxEzUQZOvUAACb'
failed: Error during WebSocket handshake: net::ERR_CONNECTION_RESET
WrappedWebSocket # VM43:161
This error happens probably only in a production environment. Cannot remember seeing this error in when running the application in local.
Also posting ONLY the socket related code from both server and client side :
Node js server-side code
start.js file
var express = require('express');
var configure = require("./config/configure");
var logger = require("./config/components/logger")
var app = express();
var server = require('http').Server(app);
server.listen(process.env.PORT || config.port, function() {
logger.info("Express server listening on port", config.port );
});
//Configure with all the basic middlewares and configs
configure(app,server);
configure.js file
var socket = require('./middleware/socket/socket.js');
module.exports = function (app,server) {
app.use(socket(server));
}
socket.js file
"use strict";
var logger = require("../../components/logger");
module.exports = function(server){
var io = require('socket.io')(server, {path: '/appname/socket.io'});
require('./socketServer.js')(io, logger);
return function (req, res, next) {
req.io = io;
next();
};
};
socketServer.js
//export function for listening to the socket
module.exports = function(io, logger) {
io.on('connection', function(socket) {
socket.on('notification:update', function(data) {
io.emit('notification:update', data);
});
});
};
Angular js Client Side code :
Socket.js
MyApp.factory('Socket', function ($rootScope) {
var socket = io.connect('' , {path: '/appname/socket.io'});
return {
on: function (eventName, callback) {
socket.on(eventName, function () {
var args = arguments;
$rootScope.$apply(function () {
callback.apply(socket, args);
});
});
},
emit: function (eventName, data, callback) {
socket.emit(eventName, data, function () {
var args = arguments;
$rootScope.$apply(function () {
if (callback) {
callback.apply(socket, args);
}
});
})
}
};
});
notificationController.js
Socket.on('notification:update', function(data) {
});
-- Could anyone suggest how to resolve the console error?
Turns out there was another reverse proxy in front of your server that I had no control of. Please check your server setings. the problem is not about the code.
Error during WebSocket handshake: net::ERR_CONNECTION_RESET
Also try this one to test your server side.
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.7.2/socket.io.js"></script>
<script>
/*var socket = io('', {
path: '/appname/socket.io'
});*/
var socket = io.connect('' , {path: '/appname/socket.io'});
socket.on('notification:update', function (message) {
console.log('notification:update ', message);
});
setTimeout(function() {
console.log('emit demo');
socket.emit('notification:update', 'DEMO');
}, 1000);
socket.on('connect', function (data) {
console.log('connection');
});
</script>

Pass user messages depending on request response

Introduction
OK, what I have is a app built in Node and Angular. I pass A users email to my backed using a post in Angular, from the backed the order in the backed is:
Get the email
Get API key
Post email and API key to API
I do this by posting email to backed then using node and express get email use promise resolve (first function) to pass the email to my third function as well as the API key retrieved from the second function.
What I need
Angular post to back end Node
Run first function, If first function has retrieved the email then run function 2. if not correct then pass information to the first post (Angular) to display message.
Run second function, if true run function 3
Finally run post with data collected from function 1 and 2, if post correctly pass 200 code to first function or pass to angular post.
Needed
Verification on the front end (Angular) on each step (function 1, 2 and 3 in Node) they can be response code so that I may print a different message depending on response code
Objective
A user post email on front end, then depending on if the email was accepted on the API let the user know, This is where different messages or redirects come in to play depending if it was a wrong or right email.
My Code
Angular side
This is where the first post to the Node back end happens, would be nice if this could get different response request depending on the results on the back-end.
var firstFunction = function () {
return new Promise(function (resolve) {
setTimeout(function () {
app.post('/back-end/controller', 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);
});
};
Node side (all in controler.js)
First function
I would like this to trigger function 2 if success if not send a response code back to the Angular request.
var firstFunction = function () {
return new Promise(function (resolve) {
setTimeout(function () {
app.post('/back-end/controller', function (req, res) {
console.log(req.body);
var login = req.body.LoginEmail;
//Promise.all([firstFunction(), secondFunction()]) .then(thirdFunction);
//res.send(login);
resolve({
data_login_email: login
});
});
console.error("First done");
}, 2000);
});
};
Second function
This function gets API key, if This function is successful trigger function three.
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);
});
};
Third Function
If second function passes then this function should run using the email from the first and the API key from the second, If success then pass pass success back to first function to pass give 200 success to the angular side, or directly send a request response to Angular, If fail then again let the front end know.
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.info(login_email, "Is a user, login pass!");
// router.redirect('/login'); // main page url
// res.send.status(200);
})
.catch(function (err) {
console.error("fail no such user");
// res.status(400).send()
});
console.error("Third done");
}, 3000);
}
);
}
Promise.all([firstFunction(), secondFunction()]) .then(thirdFunction);
If anyone knows how to do this please can you help, this is the last part of my app i need to get working, Thanks.
Summery
In summery I would like different response codes Angular side depending on where and when the function got to on backed or if it passed all three functions.
Eg:
request code for fails to post to backed
Fails to get API key on function 2
Fails to send email to API on third function
Email not present on API
Email present on API and all pass, Your In !!
UPDATE
I found I can pass a message back to my Angular post using the following, but how can I make this message different depending on what function has run ?
var firstFunction = function () {
return new Promise(function (resolve) {
setTimeout(function () {
app.post('/back-end/controller', function (req, res) {
console.log(req.body);
// res.status(500).send({ error: "boo:(" });
res.send('hello world');
var login = req.body.LoginEmail;
res.send(login);
resolve({
data_login_email: login
});
});
console.error("First done");
}, 2000);
});
};
I solved this by merging 2 function into one (the retrieve function and post) then i changed the promise chain
var firstFunction = 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("Success your API key is", api_key);
resolve({data_api: api_key});
}
});
}, 2000);
});
};
var secondFunction = function (result) {
return new Promise(function () {
setTimeout(function () {
app.post('/back-end/controller', function (req, res) {
console.log(req.body);
var login = req.body.LoginEmail;
var api = result[0].data_api;
var headers = {
'User-Agent': 'Super Agent/0.0.1',
'Content-Type': 'application/x-www-form-urlencoded'
};
var options = {
url: 'https://pi.pardot.com/api/prospect/version/4/do/read',
method: 'POST',
headers: headers,
form: {
'email': login,
'user_key': userkey,
'api_key': api
},
json: true // Automatically stringifies the body to JSON
};
if (login.length !== 0) { // maybe use node email validation ?
console.log("Email Accepted, Next posting to API.......");
rp(options)
.then(function (parsedBody) {
console.info(login, "Is a user, login pass!");
res.status(200).send({ user: login });
})
.catch(function (err) {
console.error("fail no such user");
res.status(400).send('fail to login');
});
} else {
console.log("Failed to get email from front end");
res.status(404).send('Incorrect length');
}
});
});
});
};
Promise.all([firstFunction()]).then(secondFunction);

AngularJs authentication jwt

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

Angular/Breeze Login implementation

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);
}
};
}]);
}]);

Resources