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!
Related
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 have a microservice setup with an api gateway accepting http requests and passing them to miscroservices (media-service, payment-service, etc.) using message pattern.
I can access the request.user in the api-gateway which gives me the authenticated user from the decoded jwt.
What i would like is, to pass this to the media microservice and access it within the functions in media service.
How do i achieve this?
controller in api-gateway
export class MediasController {
constructor(#Inject('MEDIA_SERVICE') private client: ClientProxy) { }
#Get()
#UseGuards(AuthGuard('jwt'))
async find(#Query() query: QueryArgs): Promise<any> {
const pattern = { cmd: 'media-find' };
return await this.client.send<any>(pattern, query)
}
}
controller & service in media microservice
medias.controller.ts
#Controller({ path: 'medias', scope: Scope.REQUEST })
export class MediasController {
constructor(private readonly mediasService: MediasService) { }
#MessagePattern({ cmd: 'media-find' })
async find(query: any): Promise<any> {
return await this.mediasService.find(query)
}
}
medias.service.ts
export class MediasService {
constructor(#InjectRepository(Media) private readonly mediaRepository: Repository<Media>) {
}
async find(query): Promise<Media[]> {
// access request.user here
return await this.mediaRepository.find(query);
}
}
How can I prevent user 1 to access information of user 2 using passport in a Nesjs app ?
I already have 2 strategies:
the local strategy which validate a user with email/password. The route protected by this strategy return a jwt token.
the jwt strategy which validate the given jwt token.
Now, I want to restrict access to routes such as users/:id to jwt token which actually have the same userId encrypted.
How to do that ?
EDIT
I was mixing Authentication and Authorization: what I want to achieve is about authorization, once the user has been authenticated.
I had to use Guard:
own.guard.ts
#Injectable()
export class OwnGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const req = context.switchToHttp().getRequest();
return req.user.id === req.params.id;
}
}
Then use it in my route:
#Get(':id')
#UseGuards(OwnGuard)
async get(#Param('id') id: string) {
return await this.usersService.get(id);
}
ORIGINAL ANSWER
What I did was to create a third strategy based on the jwt one:
#Injectable()
export class OwnStrategy extends PassportStrategy(Strategy, 'own') {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: SECRET,
passReqToCallback: true
});
}
async validate(req: Request, payload: { sub: string }) {
if (req.params.id !== payload.sub) {
throw new UnauthorizedException();
}
return { userId: payload.sub };
}
}
Note how I pass the custom name 'own' as second parameter of PassportStrategy to differentiate it from the 'jwt' one. Its guard:
#Injectable()
export class OwnAuthGuard extends AuthGuard('own') {}
This works but I wonder if it is the good way of doing it...
What if later I want to able user modification for admin users ?
Should I create a forth strategy which check if role === Role.ADMIN || req.params.id === payload.sub ?
I think I'm missing something. There should be a way to create a strategy which validate only the jwt, another one only the userId, another one only the role, and combine them as I want when applying guards to my routes.
same case. you can use handleRequest method in guard.
here you can access user auth and req, then doing validation for resource appropriate. check out my code
#Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
canActivate(context: ExecutionContext) {
return super.canActivate(context);
}
handleRequest(err, user, info, context: ExecutionContext) {
const request = context.switchToHttp().getRequest<Request>();
const params = request.params;
if (user.id !== +params.id) {
throw new ForbiddenException();
}
return user;
}
}
look more here https://docs.nestjs.com/security/authentication#extending-guards
I'm trying to get access to the jwt payload in a route that is protected by an AuthGuard.
I'm using passport-jwt and the token payload is the email of the user.
I could achieve this by runing the code bellow:
import {
Controller,
Headers,
Post,
UseGuards,
} from '#nestjs/common';
import { JwtService } from '#nestjs/jwt';
import { AuthGuard } from '#nestjs/passport';
#Post()
#UseGuards(AuthGuard())
async create(#Headers() headers: any) {
Logger.log(this.jwtService.decode(headers.authorization.split(' ')[1]));
}
I want to know if there's a better way to do it?
Your JwtStrategy has a validate method. Here you have access to the JwtPayload. The return value of this method will be attached to the request (by default under the property user). So you can return whatever you need from the payload here:
async validate(payload: JwtPayload) {
// You can fetch additional information if needed
const user = await this.userService.findUser(payload);
if (!user) {
throw new UnauthorizedException();
}
return {user, email: payload.email};
}
And then access it in you controller by injecting the request:
#Post()
#UseGuards(AuthGuard())
async create(#Req() request) {
Logger.log(req.user.email);
}
You can make this more convenient by creating a custom decorator:
import { createParamDecorator } from '#nestjs/common';
export const User = createParamDecorator((data, req) => {
return req.user;
});
and then inject #User instead of #Req.
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.