Logout of all devices, NodeJS and Angular 6 - node.js

I'm developing an application, the NodeJs (along with Express) will be server side which will provide RESTful service and Angular 6 will utilize the RESTful API.
Now the problem is when a user change the password, then how to logout the user from all devices.
I'm using JWT for authentication.

After a long of search I have found one solution,
You should have an updated_at field in user table which stores the time user last updated his/her details,
You have to use this field as Encryption String for JWT, so when the user updates info, the tokens previously issued are considered as invalid so user wouldn't be able to perform any action with that, and he/she must have renew the token

It is bad practise to logout the user when it changes password unless user voluntarily opts to logout from all the devices:-
Once the user logout , then clears localstorage and redirect to login as:-
Just change the authorization token when user updates the password and check that token is still valid for each request. If not valid, gives 401 error and then redirect to login page.
logout(){
/* clear your localStorage token /*
// redirect to login page
}
interceptor.ts
import { Injectable } from '#angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '#angular/common/http';
import { Observable } from 'rxjs';
#Injectable()
export class JwtInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// add authorization header with jwt token if available
let currentUser = JSON.parse(localStorage.getItem('currentUser'));
if (currentUser && currentUser.token) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${currentUser.token}`
}
});
}
return next.handle(request).do((event: HttpEvent<any>) => {
if (event instanceof HttpResponse) {
// do stuff with response if you want
}
}, (err: any) => {
if (err instanceof HttpErrorResponse) {
if (err.status === 401) {
// redirect to the login route
// or show a modal showing, we are redirecting to you login page.
}
}
});
}
}
Refer Link:- https://medium.com/#ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8
http://jasonwatmore.com/post/2018/05/23/angular-6-jwt-authentication-example-tutorial

Related

How to check session in angular 8

I have created login form with angular 8 and node js. I have set the session using node js in back end. i couldnt check session set or not in angular for avoid access dashboard without logged in. Kindly suggest the way to use login system using angular 8 and node js. Thanks.....
A very popular method is to use JWT (JSON Web Tokens) npm package to authenticate.
The process would be:
Send credentials to the server
Server generates and sends back JWT or a Bearer Token
FrontEnd would store it in browser cookies or localStorage
localStorage.setItem('TOKEN', tokenReceivedFromServer);
In subsequent Api Calls the token would be sent to the server in a Header (Authorization).
Authorization: `JWT ${localStorage.getItem('TOKEN')}`
FYI: JWT keyword is removed from string on the server before parsing token
The frontend can check if the token is set in storage to show login page / dashboard
First we need to check the login credentials valid or not in application.
In angular application component typescript file, we have send the data service in argument, the service send the values to backend using httpclient. If credentials valid we set the value in localstorage.
submitLogin(data:any)
{
this.LoginService.loginData(data).subscribe(data =>{
if(data.body.status_code == 404)
{
Swal.fire({
icon: 'warning',
title: 'Invalid E-Mail/Password!',
}).then(function(){
});
}else if(data.body.status_code ==200)
{
localStorage.setItem("user_id",data.body.token);
this.router.navigate(['/Dashboard']);
}else
{
Swal.fire({
icon: 'error',
title: 'Process Failed!',
}).then(function(){
});
}
});
}
In service.ts file make sure about those packages import
import { HttpClient } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import {Login} from './login';
in loginData function
url = "http://localhost:3000/loginCheck";
loginData(Login:Login):Observable<any>
{
return this.http.post(this.url,Login,{observe: 'response'});
}
in backend i have used node.js
in file app.js
first install jsonwebtoken package and include in the file.
npm install jsonwebtoken
then set the jsonwebtoken when where condition satisfies
let payload = {subject:employee_id}
let token = jwt.sign(payload,'secretKey')
var response = {
'token': token,
"status_code":200,
}
res.send(response);
res.end();
Whenever we use the login in angular we must use the authguard routing its helps to access dashboard without logged in.
ng generate guard auth
in auth.guard.ts file we must include the package and service
import { CanActivate, Router } from '#angular/router';
import {LoginService} from './login.service';
export class AuthGuard implements CanActivate {
constructor(private LoginService:LoginService,private router:Router) {}
canActivate(): boolean
{
if(this.LoginService.loggedIn())
{
return true
}else
{
this.router.navigate(['/login']);
return false;
}
}
}
In this file we just checking the localstorage value set or not in boolean datatype.
in service file
add the following code for get and return in boolean type
loggedIn()
{
return !!localStorage.getItem('user_id')
}
getToken()
{
return localStorage.getItem('user_id')
}
if its returns true we can access the dasboard, else its redirected to login page.
We must use this canActive function in routing otherwise it will not working
In app-routing.module.ts file
import { AuthGuard } from './auth.guard';
const routes: Routes = [
{path:'Dashboard',component:DashboardComponent},
{path:'receipt',component:ReciptComponentComponent,canActivate:[AuthGuard]},
];
It will helpus to access dashboard without loggedin but we need to check the token valid or not in backend, we can do that using angular interceptors
we should create the new service with interceptors name
ng g service token-interceptor
In interceptor file we need to import the following
import { Injectable,Injector } from '#angular/core';
import { HttpInterceptor } from '#angular/common/http';
import { LoginService } from './login.service';
In interceptors services inject in different way compared to component.
export class TokenInterceptorService implements HttpInterceptor{
constructor(private Injector:Injector) { }
intercept(req:any,next:any)
{
let loginService = this.Injector.get(LoginService);
let tokenzedReq = req.clone({
setHeaders:
{
Authorization: `Bearer ${loginService.getToken()}`
}
});
return next.handle(tokenzedReq)
}
}
we need to create a function in interceptors with the name intercept, then we need to inject the service as per injector.
In backend we need to create the helper function to verify the jsonwebtoken
if the authorization not set we can send the response 401 not found and can redirected to login page
function verifyToken(req,res,next)
{
if(!req.headers.authorization)
{
return res.status(401).send('Unauthorized request');
}
var token = req.headers.authorization.split(' ')[1];
if(!token)
{
return res.status(401).send('Unauthorized request');
}
if(token === 'null')
{
return res.status(401).send('Unauthorized request');
}
//let payload = jwt.verify(token,'secretKey');
let payload = jwt.decode(token,'secretKey');
if(!payload)
{
return res.status(401).send('Unauthorized request');
}
req.userId = payload.subject;
next();
}
then we can use this middleware function wherever we need
for example
app.get('/dashboard',verifyToken,function(req,res){
let events = [];
res.json(events);
});
In dashboard component ts file
this.dashboardService.getData().subscribe(data=>this.dashboardData=data,
err=>{
if(err instanceof HttpErrorResponse)
{
if(err.status===401)
{
this.router.navigate(['/login']);
}
}
})
in dashboard service ts file
url = "http://localhost:3000/dashboard";
getData()
{
return this.http.get<any>(this.url);
}
in app.module.ts file
import { AuthGuard } from './auth.guard';
import { ReciptComponentComponent } from './recipt-component/recipt-component.component';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { TokenInterceptorService } from './token-interceptor.service';
import { DashboardServiceService } from './dashboard-service.service';
in providers
providers: [AuthGuard,{provide:HTTP_INTERCEPTORS,useClass:TokenInterceptorService,multi:true},DashboardServiceService],

http-only cookie + token : double job?

Dears,
I'm trying to find how managing authentification on client side using the hhtp-only cookie sent by the server.
What I don't understand is that since the HTTP only cookie can't be accessed by the front end, how the front end knows that the user is (still) authenticated ?
So far, the only solution if found is to send to the client a token when the authentication succeed. And keep this token in a second cookie created by the client.
But it seems to me that I'm doing the same job twice.
1- managing the HTTP only cookie on server side, especially the expiration date
2- managing also on client side the expiration date of the second cookie.
How can avoid this ? I'd like to manage the authentification on client side based on the HTTP only server cookie. If there is a server cookie, then go on, else redirect to login page.
I'm using node/express on server side and react on client one. The session is stored in redis, both sides are HTTPS using certificates.
Thks
You don't need to store another cookie.
I suppose you use token based authentication on your endpoint, eg. JWT. Then you think about this scenario:
User send username/password to server.
Check user credentials and if there are valid, create http-only cookie with the token
const user = await getUser({ where: { email } });
const valid = await bcrypt.compare(password, user.password);
if (!valid) {
throw new UserInputError('Form Arguments invalid', {
invalidArgs: {
'password': 'Invalid password!',
},
});
}
const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
/
res.cookie('token', token, {
httpOnly: true,
maxAge: 1000 * 60 * 60 * 24 * 365,
});
Write auth middlerware to put the userId onto the req for future requests to access
const jwt = require('jsonwebtoken');
const { AuthenticationError } = require('apollo-server');
module.exports = async function(req, res, next) {
const { token } = req.cookies;
if (token) {
try {
const { userId } = jwt.verify(token, process.env.APP_SECRET);
if (!userId) return next();
req.userId = userId;
} catch (e) {
console.log(e);
}
}
next();
};
Check on each request the userId. If there is no userId, user doesn't logged in
if (!req.userId) {
throw new AuthenticationError('Log in!');
}
If user's token is invalid/expired you will get AuthenticationError. Catch it and redirect to login page.
If your UI depends on user status, you can create easy-to-use component (i am using React) to check it.
User Component:
import { Query } from 'react-apollo';
import gql from 'graphql-tag';
import PropTypes from 'prop-types';
const CURRENT_USER_QUERY = gql`
query CURRENT_USER_QUERY {
me {
userId
firstName
lastName
profilePictureUrl
}
}
`;
const User = props => (
<Query {...props} query={CURRENT_USER_QUERY} fetchPolicy={'cache-first'}>
{payload => props.children(payload)}
</Query>
);
User.propTypes = {
children: PropTypes.func.isRequired,
};
export default User;
If we get me object from server, you know, there is a logged in user, so you can render depends on user's status:
import { Link } from 'react-router-dom';
import React from 'react';
<User>
{({ loading, error, data: { me } }) => {
if (loading || error || !me) return (
<Button component={Link} to={'/login'}>Login</Button>
);
if(me) return (
<Button component={Link} to={'/dashboard'}>Go to dashboard</Button>
)
}}
</User>

Auth0-js Custom Login - 401 Unauthorized

I've been working off of this React Quickstart on auth0 https://auth0.com/docs/quickstart/spa/react/02-custom-login , trying to implement a custom login. When I try to Login I get a 401 Unauthorized Error and when I try to Sign Up I get the same alert error but the user does get created and I get redirected to the home page. Mind you everything works fine using the Lock Widget but when I try to do it with the custom login it doesn't.
Here's the AuthService code which is the most relevant I feel. The Login component simply calls the login and signup methods.
export default class SocialAuthService extends EventEmitter {
constructor(clientId, domain) {
super()
// Configure Auth0
this.auth0 = new auth0.WebAuth({
clientID: 'clientID',
domain: 'domain',
responseType: 'token id_token',
redirectUri: 'http://localhost:3000/login'
})
this.login = this.login.bind(this)
this.signup = this.signup.bind(this)
this.loginWithGoogle = this.loginWithGoogle.bind(this)
this.loginWithTwitter = this.loginWithTwitter.bind(this)
this.loginWithFacebook = this.loginWithFacebook.bind(this)
}
login(username, password) {
this.auth0.client.login({
realm: 'Username-Password-Authentication',
username,
password
}, (err, authResult) => {
if (err) {
alert('Error: ' + err.description)
return
}
if (authResult && authResult.idToken && authResult.accessToken) {
this.setToken(authResult.accessToken, authResult.idToken)
browserHistory.replace('/home')
}
})
}
signup(email, password){
this.auth0.redirect.signupAndLogin({
connection: 'Username-Password-Authentication',
email,
password,
}, function(err) {
if (err) {
alert('Error: ' + err.description)
}
})
}
parseHash(hash) {
this.auth0.parseHash({ hash }, (err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setToken(authResult.accessToken, authResult.idToken)
browserHistory.replace('/home')
this.auth0.client.userInfo(authResult.accessToken, (error, profile) => {
if (error) {
console.log('Error loading the Profile', error)
} else {
this.setProfile(profile)
}
})
} else if (authResult && authResult.error) {
alert('Error: ' + authResult.error)
}
})
}
loggedIn() {
// Checks if there is a saved token and it's still valid
const token = this.getToken()
return !!token && !isTokenExpired(token)
}
setToken(accessToken, idToken) {
// Saves user access token and ID token into local storage
localStorage.setItem('access_token', accessToken)
localStorage.setItem('id_token', idToken)
}
setProfile(profile) {
// Saves profile data to localStorage
localStorage.setItem('profile', JSON.stringify(profile))
// Triggers profile_updated event to update the UI
this.emit('profile_updated', profile)
}
getProfile() {
// Retrieves the profile data from localStorage
const profile = localStorage.getItem('profile')
return profile ? JSON.parse(localStorage.profile) : {}
}
getToken() {
// Retrieves the user token from localStorage
return localStorage.getItem('id_token')
}
logout() {
// Clear user token and profile data from localStorage
localStorage.removeItem('id_token')
localStorage.removeItem('profile')
}
loginWithGoogle() {
this.auth0.authorize({
connection: 'google-oauth2'
})
}
loginWithTwitter() {
this.auth0.authorize({
connection: 'twitter'
})
}
loginWithFacebook() {
this.auth0.authorize({
connection: 'facebook'
})
}
}
And this is the error:
Object
code
:
"access_denied"
description
:
"Unauthorized"
original
:
Error: Unauthorized at Request.<anonymous> (http://localhost:3000/static/js/bundle.js:49311:20) at Request.Emitter.emit (http://localhost:3000/static/js/bundle.js:49954:21) at XMLHttpRequest.xhr.onreadystatechange (http://localhost:3000/static/js/bundle.js:49616:11)
statusCode
:
401
statusText
:
"Unauthorized"
Any ideas on why I'm not able to Login ?
Not sure if you got an answer, but I was running into the same problem and it was because the backend wasn't able to properly decode the JWT. Custom Login signs with a RS256 token while it seems Lock signs with HS256. You have to decode these differently in your backend.
Here's a python example
Auth0.js version 8 verifies ID tokens during authentication transactions. Only tokens which are signed with the RS256 algorithm can be verified on the client side, meaning that your Auth0 client must be configured to sign tokens with RS256. See the auth0.js migration guide for more details.
I ran into a similar issue and my fix was switching the "Application Type" setting in Auth0 from "Regular Web Application" to "Single Page Application".
The Auth0 React SDK docs make it clear that you have to register your app as a single-page application in order for Auth0 to configure the appropriate settings, so that your web client can make successful requests to their endpoints.

How to provide frontend with JSON web token after server authentication?

So far I have only dealt with server-rendered apps, where after a user logs in via username/password or using an OAuth provider (Facebook etc.), the server just sets a session cookie while redirecting to the relevant page.
However now I'm attempting to build an app using a more 'modern' approach, with React on the frontend and a JSON API backend. Apparently the standard choice for this is to use a JSON web token for authentication, however I'm having trouble working out how I'm meant to provide the JWT to the client so it can be stored in session/local storage or wherever.
Example to illustrate better:
User clicks link (/auth/facebook) to log in via Facebook
User is redirected and shown Facebook login form and/or permission dialog (if necessary)
Facebook redirects user back to /auth/facebook/callback with an authorization code in tow, the server exchanges this for an access token and some information about the user
Server finds or creates the user in the DB using the info, then creates a JWT containing a relevant subset of the user data (e.g. ID)
???
At this point I just want the user to be redirected to the main page for the React app (let's say /app) with the JWT in tow, so the frontend can take over. But I can't think of an (elegant) way to do that without losing the JWT along the way, other than to put it in the query string for the redirect (/app?authtoken=...) - but that will display in the address bar until I remove it manually using replaceState() or whatever, and seems a little weird to me.
Really I'm just wondering how this is typically done, and I'm almost sure I'm missing something here. The server is Node (Koa with Passport), if that helps.
Edit: To be clear, I'm asking what the best way is to provide a token to the client (so it can be saved) after an OAuth redirect flow using Passport.
I recently ran across this same issue, and, not finding a solution here or elsewhere, wrote this blog post with my in-depth thoughts.
TL;DR: I came up with 3 possible approaches to send the JWT to the client after OAuth logins/redirects:
Save the JWT in a cookie, then extract it on the front-end or server in a future step (eg. extract it on the client with JS, or send a request to the server, server uses the cookie to get the JWT, returns the JWT).
Send the JWT back as part of the query string (which you suggest in your question).
Send back a server-rendered HTML page with a <script> tag that:
Automatically saves the embedded JWT to localStorage
Automatically redirects the client to whatever page you like after that.
(Since logging in with JWTs is essentially equivalent to "saving the JWT to localStorage, my favorite option was #3, but it's possible there are downsides I haven't considered. I'm interested in hearing what others think here.)
Hope that helps!
Client: Open a popup window via $auth.authenticate('provider name').
Client: Sign in with that provider, if necessary, then authorize the application.
Client: After successful authorization, the popup is redirected back to your app, e.g. http://localhost:3000, with the code (authorization code) query string parameter.
Client: The code parameter is sent back to the parent window that opened the popup.
Client: Parent window closes the popup and sends a POST request to /auth/provider withcode parameter.
Server: Authorization code is exchanged for access token.
Server: User information is retrived using the access token from Step 6.
Server: Look up the user by their unique Provider ID. If user already exists, grab the existing user, otherwise create a new user account.
Server: In both cases of Step 8, create a JSON Web Token and send it back to the client.
Client: Parse the token and save it to Local Storage for subsequent use after page reload.
Log out
Client: Remove token from Local Storage
here is a login request from the server side. it's storing the token in the header:
router.post('/api/users/login', function (req, res) {
var body = _.pick(req.body, 'username', 'password');
var userInfo;
models.User.authenticate(body).then(function (user) {
var token = user.generateToken('authentication');
userInfo = user;
return models.Token.create({
token: token
});
}).then(function (tokenInstance) {
res.header('Auth', tokenInstance.get('token')).json(userInfo.toPublicJSON());
}).catch(function () {
res.status(401).send();
});
});
here is the login request on the react side, where I am grabbing the token from the header and setting the token in local storage once the username and password pass authentication:
handleNewData (creds) {
const { authenticated } = this.state;
const loginUser = {
username: creds.username,
password: creds.password
}
fetch('/api/users/login', {
method: 'post',
body: JSON.stringify(loginUser),
headers: {
'Authorization': 'Basic'+btoa('username:password'),
'content-type': 'application/json',
'accept': 'application/json'
},
credentials: 'include'
}).then((response) => {
if (response.statusText === "OK"){
localStorage.setItem('token', response.headers.get('Auth'));
browserHistory.push('route');
response.json();
} else {
alert ('Incorrect Login Credentials');
}
})
}
When you get a token from any passport authentication sites you have to save the token in your browser's localStorage. The Dispatch is Redux's Middleware. Ignore dispatch if you don't use redux in your app. you can just use setState here (A bit weird without redux).
Client-side:
Here's something similar API of mine, which returns token.
saving tokens
axios.post(`${ROOT_URL}/api/signin`, { email, password })
.then(response => {
dispatch({ type: AUTH_USER }); //setting state (Redux's Style)
localStorage.setItem('token', response.data.token); //saving token
browserHistory.push('/home'); //pushes back the user after storing token
})
.catch(error => {
var ERROR_DATA;
try{
ERROR_DATA = JSON.parse(error.response.request.response).error;
}
catch(error) {
ERROR_DATA = 'SOMETHING WENT WRONG';
}
dispatch(authError(ERROR_DATA)); //throw error (Redux's Style)
});
So When you make some authenticated requests,you have to attach the token with the request in this form.
authenticated requests
axios.get(`${ROOT_URL}/api/blog/${blogId}`, {
headers: { authorization: localStorage.getItem('token') }
//take the token from localStorage and put it on headers ('authorization is my own header')
})
.then(response => {
dispatch({
type: FETCH_BLOG,
payload: response.data
});
})
.catch(error => {
console.log(error);
});
Here's my index.js:
The token is checked each and everytime, so even if the browser got refreshed, you can still set the state.
checks if the user is authenticated
const token = localStorage.getItem('token');
if (token) {
store.dispatch({ type: AUTH_USER })
}
ReactDOM.render(
<Provider store={store}>
<Router history={browserHistory}>
<Route path="/" component={App}>
..
..
..
<Route path="/blog/:blogid" component={RequireAuth(Blog)} />
//ignore this requireAuth - that's another component, checks if a user is authenticated. if not pushes to the index route
</Route>
</Router>
</Provider>
, document.querySelector('.container'));
All that dispach actions does is it sets the state.
my reducer file(Redux only) else you can just use setState() in your index route file to provide the state to the whole application. Every time the dispatch is called, it runs a similar reducer file like this which sets the state.
setting the state
import { AUTH_USER, UNAUTH_USER, AUTH_ERROR } from '../actions/types';
export default function(state = {}, action) {
switch(action.type) {
case AUTH_USER:
return { ...state, error: '', authenticated: true };
case UNAUTH_USER:
return { ...state, error: '', authenticated: false };
case AUTH_ERROR:
return { ...state, error: action.payload };
}
return state;
} //you can skip this and use setState() in your index route instead
Delete the token from your localStorage to logout.
caution: Use any different name rather than token to save the token in your browser's localStorage
Server-Side:
considering your passport services file. You must set the header search.
Here's passport.js
const passport = require('passport');
const ExtractJwt = require('passport-jwt').ExtractJwt;
const JwtStrategy = require('passport-jwt').Strategy;
..
..
..
..
const jwtOptions = {
jwtFromRequest: ExtractJwt.fromHeader('authorization'), //client's side must specify this header
secretOrKey: config.secret
};
const JWTVerify = new JwtStrategy(jwtOptions, (payload, done) => {
User.findById(payload._id, (err, user) => {
if (err) { done(err, null); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
});
passport.use(JWTVerify);
In my router.js
const passportService = require('./services/passport');
const requireAuthentication = passport.authenticate('jwt', { session: false });
..
..
..
//for example the api router the above react action used
app.get('/api/blog/:blogId', requireAuthentication, BlogController.getBlog);

Angular 2 proper use of authentication service

I am building web application using NodeJS for the server-side and Angular 2 for the client-side.
In the application I'm using ADFS to authenticate users.
The user browses to the website and automatically redirected to the ADFS authentication server. After the user completes the authentication, he redirects back to my application and I get the user data from the ADFS server.
I used passport-saml package to implement the authentication and it works fine.
The user is now stored at req.user.
Now I need to use user's data on the client side.
After a little research, I found that passing user's data from server to client can be as simple as :
router.get('/user/current', AuthMiddleware.requireLogin, (req: express.Request, res: express.Response) => {
return res.json(req.user);
});
This works as well.
Now for the client-side:
I've created a service to fetch the authenticated user :
#Injectable()
export class AuthService {
private authUrl = 'http://localhost/api/user/current';
private currentUser: User;
constructor(private http: Http) {
this.getUser().subscribe(user => {
this.currentUser = user;
});
}
getUser(): Observable<User> {
return this.http.get(this.authUrl)
.map((res: Response) => res.json())
.catch(error => Observable.throw(error.json().error || 'Server Error'));
}
isAuthenticated(): boolean {
return !!this.currentUser;
}
}
So the getUser method returns an Observable with my user and I can use it in my client-side.
But my question is :
Should I inject the AuthService to each component which uses the authenticated user?
And if so, should I call getUser each time and wait for the Observable to return user's data, or should I use public parameter for the authenticated user?
(for example making the currentUser parameter public in the AuthService and then just use authService.currentUser?)
You don't need to inject the AuthService into each component. What you want to do instead is guard the various routes in your application from activation unless a user has been authenticated. You must implement an AuthGuard that will have the AuthService injected.
Check out https://angular.io/docs/ts/latest/guide/router.html (search the page for "GUARD THE ADMIN FEATURE") for more information.

Resources