I have implemented user session with redis and passport, works fine when I use it on a monolith
But I don't know how to implement it on a microservices, I want only put a guard in the api gateway and it send a request to validate user session to auth microservice
This is my guard to validate user session:
#Injectable()
export class CookieAuthGuard implements CanActivate {
async canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
return request.isAuthenticated();
}
}
I don't know how to validate it on my provider to call it in my api gateway
And I don't know how to put my login into the provider too, because i have a guard for that:
#Injectable()
export class LogInWithCredentialsGuard extends AuthGuard('local') {
async canActivate(context: ExecutionContext): Promise<boolean> {
await super.canActivate(context);
const request = context.switchToHttp().getRequest();
await super.logIn(request);
return true;
}
}
Any idea to implement validation of session working on the api gateway, and how to put login into provider?
Thanks!
I have applied authentication in NestJS middlewares and decoding all the information from user token by the third party API.
Now I have got the information in my middleware, how can I send that information to controllers and services?
It is normal for middlewares to add information to the request object (eg. adding a user object).
For a clean way to extract information from the request object and inject it into the controller you can make a custom route decorator
For example, extracting the user:
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
And then in your controller
#Get()
async findOne(#User() user: UserEntity) {
console.log(user);
}
From there you can just ensure that your service methods receive the User as a regular method parameter
I'm writing an Express + Typescript application that, among other things, has an API endpoint which allows the user to search for a track. The endpoint receives a track name as a URL parameter, requests an access token from Spotify, uses the access token to query the Spotify Web API for matching tracks, then returns the data in JSON format. My problem is that I'm using the spotify-web-api-node library to interact with the API and so far I've only been able to make it work by requesting a new access token each time the route is accessed. I've tried adding middleware to request the access token elsewhere so it doesn't need to be done in this specific route, but it seemingly executes the route before the middleware, thus executing the API call without first getting the access token. Here is my working solution so far
import { Router, Request, Response, NextFunction } from 'express';
import SpotifyWebApi from 'spotify-web-api-node';
const spotifyRouter = Router();
const spotifyApi: SpotifyWebApi = new SpotifyWebApi({
clientId: /* My client ID */,
clientSecret: /* My client secret */
})
spotifyRouter.get('/search/track/:trackName', (req: Request, res: Response) => {
spotifyApi.clientCredentialsGrant()
.then(data => spotifyApi.setAccessToken(data.body['access_token']))
.then(() => { return spotifyApi.searchTracks(req.params.trackName) })
.then(data => res.json(data.body))
.catch(error => res.json(error));
})
export default spotifyRouter;
So to reiterate, I'm wondering how I can improve the management of the access token so it doesn't have to get a new one each time the above route is used.
Thanks.
EDIT: I forgot to mention that this is using Spotify's Client Credentials Flow
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
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);