8

I am using Angular+Nest to develop a website. I have created a service(Angular) so that client can get user's information from server when project start up(the same as fresh). Some actions don't need to login, so the login is optional.

What I want is if user has logined, then client should send a request to get user's information.

Server code as below:

export const RequestUser = createParamDecorator((data, req): RequestUserDTO => {
    return req.user;
});

@Controller('auth')
export class AuthController {
  @Get('getUserInfoByToken')
  async getUserInfoByToken(@RequestUser() user: User): Promise<any> {
    if (user) {
      return {
        nickname: user.nickname,
        level: user.level
      };
    }
  }
}

Howerver, I find there's nothing be return if I don't add @UseGuards(AuthGuard()) as decorator. But if I add it, when project start, this request return 401 as status code. Then the web will turn to login page.

What should I do to avoid this situation? Not every action need login.

Eve-Sama
  • 2,176
  • 4
  • 20
  • 32

3 Answers3

9

If you got totally different approach in mind, let me know - will try to help.

Will try to serve a bit more detailed example, which includes passport under the hood. It assumes that passport is used and that Authorization token is being sent.

  • const RegisteredPassportModule = PassportModule.register({ defaultStrategy: 'bearer' })
  • adding HttpStrategy to some AuthModule
  • adding PassportModule.register({ defaultStrategy: 'bearer' }) to imports to AuthModule

Then:

AuthService is a service (and a part of AuthModule) that allows to find given user via token sent via token passed via Authorization header, directly from the database.

import { Strategy } from 'passport-http-bearer';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class HttpStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super()
  }

  async validate(token: string) {
    const user = await this.authService.findUserByToken(token);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

Usage is after all quite simple (you can put the guard for either method of controller):

@UseGuards(AuthGuard())
@Get()
someMethod(@Req() request: RequestWithUser, ...) {
  // ...
}

Where RequestWithUser is just:

import { User as UserEntity } from '../../models/user.entity'

export type RequestWithUser = Request & { user: UserEntity }

and the /user endpoint would be just returning request.user

I hope this helps!

kamilg
  • 693
  • 4
  • 10
  • If I want to get user's information , I have to add `@UseGuards(AuthGuard())`. But sometimes user don't need to login. However, the client still send a request with token to get user's information. And the server will return 401. The web receive this status code will turn to login page. But this is not what I want. – Eve-Sama Sep 14 '19 at 04:38
  • 1
    I mean, How to do that server return user's information if token is valid. Otherwise, return null or something else status code is 200 – Eve-Sama Sep 14 '19 at 04:39
  • 1
    This would be strange. If you are not logged in, you don't have access to the resource of `your profile` or any other `user profile`, thus 401 is desired option. See [this](https://stackoverflow.com/a/36522727/4319037). Please really consider that frontend just shouldn't query for that data - it is its job to use API properly. – kamilg Sep 14 '19 at 06:08
  • This service's target is to get user info when project start. For example, when user refresh the page, every data is clear. Only token is setted in localstorage. But idk whether the token is valid. So, every time page is refresh, the request is sendded. – Eve-Sama Sep 14 '19 at 07:34
3

If you really insist on this way (see comments), you can use Interceptors:


@Injectable()
export class GetUserInterceptor implements NestInterceptor {
  constructor(private readonly authService: AuthService) {
  }

  async intercept(context: ExecutionContext, next: CallHandler) {
    const request = context.switchToHttp().getRequest()
    const item = await this.authService.getByToken(/* extract me from headers*/)
    request.user = item
    return next.handle()
  }
}

so AuthGuard is not needed.

kamilg
  • 693
  • 4
  • 10
-1

Write a conditional logic in the AuthGuard to check if a user is provided in the request.