#Passport NestJS on Microservice

109 messages · Page 1 of 1 (latest)

normal valve
vagrant summit
#

Why are you using passport on a microservice?

normal valve
#

for authentication of JWT

vagrant summit
#

But why? Shouldn't your microservice be behind a tunnel the only you can access so if the request is authenticated at the gateway it should be fine for the microservice?

normal valve
#

I just ask it to authenticate at the api_gateway

#

it will be ok to validate at input

normal valve
#

any suggestions for me

#

🥲

vagrant summit
#

What kind of suggestion are you looking for?

normal valve
#

I don't understand from where does passport.authenticate get its info?

vagrant summit
#

Info as in the info variable, or just the information it uses in general?

normal valve
#

info variable

vagrant summit
#

Third parameter of the callback. I'd have to dig through passport's code to tell you exactly how it gets too the

normal valve
#

Is this info the user's info ?

vagrant summit
#

No, that should be the user parameter, IIRC

normal valve
#

So what is it i'm getting undefined there and giving error.

vagrant summit
#

Where are you getting undefined?

normal valve
#

here function canActivate

#

has data user and undefined info

vagrant summit
#

Again, I'd have to dig through passport's code to remember what info is supposed to be. Are you getting an error at all, or is the value just undefined she you want to know why?

normal valve
#

Yes, please. Thanks

vagrant summit
#

That wasn't a yes or no question

normal valve
#

I'm getting the above error because no one can help me so I'm looking for a suggestion

#

if you could see my code and give me the answer that would be great

vagrant summit
#

What error. The unauthorized error?

normal valve
#

the errors

vagrant summit
#

This is still in your microservice, right?

normal valve
#

yes

vagrant summit
#

Can you show your jwt strategy?

normal valve
#

here men'

vagrant summit
#

What transport are you using for your microservice? More than likely, what is becoming the req object doesn't have a headers property and thus passport can't read the authorization header to get the jwt for verification.

#

That's why I said authentication should be done at the gateway rather than in the microservice itself

normal valve
#

What transport are you using for your microservice => I use Redis

vagrant summit
#

Yeah pretty sure redis requests don't have headers

normal valve
#

More than likely, what is becoming the req object doesn't have a headers property and thus passport can't read the authorization header to get the jwt for verification. => I don't know if I misunderstood the question but my request still has header

vagrant summit
#

Is that inside the gateway or the microservice? How are you checking the request?

#

All of that looks like express's request object, which wouldn't exist inside the microservice

normal valve
#

That's why I said authentication should be done at the gateway rather than in the microservice itself => i'm validating it on the outermost layer
request at api_gateway =>. redis => app_service and returned api_gateway as proof that my channels pay data and right at JwtStrategy I get user login information

vagrant summit
#

Okay so you are authenticating at the gateway? That's good.

normal valve
#

Is that inside the gateway or the microservice? How are you checking the request? => it's inside of gateway, i'm checking its log when requesting 1 JWT at gateway's JWTGuand

vagrant summit
#

Can you show me your JwtGuard? It's just a simple extends AuthGuard('jwt') right?

normal valve
vagrant summit
#

If you're doing this inside your gateway, then why are you switching to rpc?

normal valve
#

it's just naming RPCAuthGuard it's no different from Auth.guard and I'm applying the mechanism of RPC

vagrant summit
#

You're doing authentication in your gateway, right?

normal valve
#

please wait update authenticate

#

@vagrant summit i'm updated

#

can your run docker in local and check it authentication in gateway

vagrant summit
#

How would I hit your server? I don't see any endpoints, just @RpcMethod()s

normal valve
#

{ "jsonrpc": "2.0", "id": 1, "method": "user.register", "params": { "username": "sangvm", "email": "sang.vo@smartdev.com", "password": "123123123", "passwordConfirm": "123123123" }

vagrant summit
#

Where do you define that endpoint?

normal valve
#

on method

vagrant summit
#

Maybe I wasn't clear: where do you define the POST /rpc/v1 endpoint in your server?

normal valve
#

can you open file main.ts I open one path in here. It read on file application.yml

#

and NestJS give me a function connectMicroservice to do it

vagrant summit
#

Okay. I see

#

That wasn't what I meant by "do subscription in the gateway"

#

I meant you should be doing it on an http route handler you have control of

#

Because what your doing now, it doesn't matter if it's in the "gateway" application or some server called "puppies", the authentication is being done in the microservice portion of the request, not the HTTP portion

#

Passport being an express middleware was really only designed for HTTP.

#

If you can wait for a bit, I'm sure I can finagle some sort of solution where authentication can be done with passport and JSON rpc with your example, but I still think that it should be handled by an http route handler you have control over

normal valve
#

If you can wait for a bit, I'm sure I can finagle some sort of solution where authentication can be done with passport and JSON rpc with your example => Yes can you give me example with it

#

I still think that it should be handled by an http route handler you have control over => I will update it if there is no other solution or next version

#

If yes, can you contribute to me? 🙏

#

@vagrant summit do you need me to send you .env to check it.

vagrant summit
#

All I ended up having to do was add this method to your JWTGuard

  getRequest(context: ExecutionContext) {
    return context.switchToRpc().getContext().req;
  }

This way, req.headers is populated for passport, because it reads off of the request object that Nest knows to pass to it. Normally, this is context.switchToHttp().getRequest(), but you have to do some custom work to make it fine in your JSONRPC context

normal valve
#

I think it's the same have you tested it?
return context.switchToRpc().getContext().req; same context.switchToRpc().getContext()['req'];

vagrant summit
#

That would be the same, yes

normal valve
#

handleRequest(err, user, info, context: ExecutionContext, status): TUser { if (err || !user || !context.switchToRpc() || !info || !status) { throw err || new UnauthorizedException(); } return user; }
both things get req and generate error, in my log it says error at

vagrant summit
#

Did you actually add the getRequest

normal valve
#

I tried your getRequest put in the code and test

vagrant summit
#

Well, it worked for me when I just used that added method

normal valve
#

Can you show me screen code?

#

Are you trying this medthod ?
{ "jsonrpc": "2.0", "id": 1, "method": "user.get_info_user", "params": {} }

vagrant summit
normal valve
#

not works from me 🥲

vagrant summit
#
import { CodeErrorRpcException, JsonRpcContext } from "../microservice/rpc";
import { ExecutionContext, HttpStatus, Injectable } from "@nestjs/common";
import { RPCAuthGuard } from "./rpc-auth.guard";
import { LoggerService } from "../logger/logger.service";
import { HttpStatusMessage } from "../message/http-status.message";

@Injectable()
export class JWTGuard extends RPCAuthGuard("jwt") {
  private unauthorizedError = new CodeErrorRpcException(
    HttpStatusMessage.UNAUTHORIZED,
    HttpStatus.UNAUTHORIZED
  );
  canActivate(context: ExecutionContext) {
    try {
      LoggerService.log("#JWTGuard.canActivate", JWTGuard.name);
      const authData = context
        .switchToRpc()
        .getContext<JsonRpcContext>()
        .getMetadataByKey("Authorization");
      if (!authData) {
        LoggerService.error(
          "#JWTGuard.canActivate",
          this.unauthorizedError.stack,
          JWTGuard.name
        );
        throw this.unauthorizedError;
      }
      return super.canActivate(context);
    } catch (e) {
      throw this.unauthorizedError;
    }
  }

  getRequest(context: ExecutionContext) {
    return context.switchToRpc().getContext().req;
  }
}
normal valve
#

getRequest in JWTGuand for what I don't see any function calling it here ?

vagrant summit
#

Getting a 500 might actually be correct here. Microservices work differently when it comes to errors. You need to catch the error that's been serialized by the microservice and deserialize it in the http gateway

vagrant summit
normal valve
vagrant summit
normal valve
vagrant summit
#

Your RPCAuthGuard calls that method

normal valve
normal valve
vagrant summit
normal valve
vagrant summit
#

Well, "works" in that I get a 500 back and a proper log that says what the error is. To get a 403/401 bank you need to deserialize the error, as I already mentioned. I'll make a fork and pull tomorrow most likely

normal valve
#

maybe you will need it.

#

I will debug and see if there is any related information I will send you

vagrant summit
#

Request:

xh :3001/rpc/v1 jsonrpc="2.0" id="1" method="user.get_info_user" Authorization:"Bearer some.token.json"

Log/error (expected)

LOG:  false JsonWebTokenError: invalid token
    at Object.module.exports [as verify] (~/node_modules/.pnpm/jsonwebtoken@9.0.0/node_modules/jsonwebtoken/verify.js:82:17)
    at Function.module.exports [as JwtVerifier] (~/node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/verify_jwt.js:4:16)
    at ~/node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js:104:25
    at JwtStrategy._secretOrKeyProvider (~/node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js:40:13)
    at JwtStrategy.authenticate (~/node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js:99:10)
    at attempt (~/node_modules/.pnpm/passport@0.6.0/node_modules/passport/lib/middleware/authenticate.js:369:16)
    at authenticate (~/node_modules/.pnpm/passport@0.6.0/node_modules/passport/lib/middleware/authenticate.js:370:7)
    at ~/src/common/guard/rpc-auth.guard.ts:118:9
    at new Promise (<anonymous>)
    at ~/src/common/guard/rpc-auth.guard.ts:106:5
[ERROR][12/29/2022, 9:10:52 AM] [RpcExceptionsHandler] Unauthorized +13ms
UnauthorizedException: Unauthorized
    at JWTGuard.handleRequest (~/src/common/guard/rpc-auth.guard.ts:91:22)
    at ~/src/common/guard/rpc-auth.guard.ts:60:16
    at ~/src/common/guard/rpc-auth.guard.ts:110:26
    at allFailed (~/node_modules/.pnpm/passport@0.6.0/node_modules/passport/lib/middleware/authenticate.js:110:18)
    at attempt (~/node_modules/.pnpm/passport@0.6.0/node_modules/passport/lib/middleware/authenticate.js:183:28)
    at JwtStrategy.strategy.fail (~/node_modules/.pnpm/passport@0.6.0/node_modules/passport/lib/middleware/authenticate.js:305:9)
    at ~/node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/strategy.js:106:33
    at Object.module.exports [as verify] (~/node_modules/.pnpm/jsonwebtoken@9.0.0/node_modules/jsonwebtoken/verify.js:82:12)
    at Function.module.exports [as JwtVerifier] (~/node_modules/.pnpm/passport-jwt@4.0.1/node_modules/passport-jwt/lib/verify_jwt.js:4:16)
vagrant summit
#

When I actually went through and created a method to generate a token, then used that token on the get_info_user it worked (besides waiting for a response cause I'm only running one of your servers). I'll have to clean up the diffs because prettier is running automatically and cleaning a lot of your code to its standards so give me a bit. Just wanted to show what the request looks like and what's happening

normal valve
#

Yes thanks, i'm not finding the cause of it either, even though i got the user info

normal valve
vagrant summit
normal valve
vagrant summit
#

Cause I legitimately sent something that wasn't a jwt

#

some.token.json

normal valve
normal valve
#

it's the same as the getRequest you suggested