#What is the call chain for authentication with passport?

55 messages · Page 1 of 1 (latest)

hexed beacon
#

I am trying to figure out how to debug/test passport strategies used with graphql but I am totally confused on what is called when.

I tried passport-ldapauth and throw in the ldap configuration I also set up a guard. the only thing that seems to be called is the authenticate method, and I cannot figure out why.

the default of most of the passport strategies expect some kind of endpoint that accepts { username, password } but when I get the login stuff from a graphql mutation that does not fit, how do I make an adapter or something to make it work with graphql mutations as auth service.

nova schooner
#

In the guard, you meant the activate method, right? Because your guard class implements CanActivate?

hexed beacon
#

in the strategy

#

yes I have a canActivate to call super canActivate and to have the gql context

#

but I don't see where the connection to tthe strategy is at which point

nova schooner
#

Can you show us your code? The strategy, the guard, how you bind the guard and the module holding the guard?

hexed beacon
#

with a bit of elbow grease I can 😄

#
// ldap.guard.ts
import { ExecutionContext, Injectable } from '@nestjs/common';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';

@injectable()
export class LdapAuthGuard extends AuthGuard('ldap') {
  constructor() {
    super();
  }

  getRequest(context: ExecutionContext) {
    const ctx = GqlExecutionContext.create(context);
    return ctx.getContext().req;
  }

  async canActivate(context: ExecutionContext) {
    await super.canActivate(context);
    const ctx = GqlExecutionContext.create(context);
    const request = ctx.getContext().req;
    await super.logIn(request);
    return true;
}
#
// ldap.strategy.ts
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { default as Strategy } from 'passport-ldapauth';

@Injectable()
export class LdapStrategy extends PassportStrategy(Strategy, 'ldap') {
  constructor(
    private configService: ConfigService
  ) {
    super({
      passReqToCallback: true,
      server: {
        url: configService.get('ldapServer.url'),
        bindDN: configService.get('ldapServer.bindDN'),
        bindCredentials: configService.get('ldapServer.bindCredentials'),
        searchBase: configService.get('ldapServer.searchBase'),
        searchFilter: configService.get('ldapServer.searchFilter'),
        reconnect: true,
      }
    },
      (request, user, done) => {
        return done(null, user);
      }
    )
  }

  authenticate(req, options?) {
    console.log('authenticate');
    return req;
  }


  validate(payload) {
    console.log(validate
    return payload;
  }
}
#

and I also have an empty resolver just to run into the guard

// something.resolver.ts
// ...
@UseGuards(LdapAuthGuard)
@Mutation('test')
test() {}
nova schooner
#

Thanks for offering the code. I can't directly see what the issue might be.

What is happening, when you try to do the login? Is that working? Because, isn't ldap for login purposes i.e. authentication and not authorization? In other words, I'm sort of confused as to how you want to use the ldap guard.

hexed beacon
#

Currently I am trying to figure out what I need to feed in where

#

the ldapauth expects some kind of { username, password } in the request object which I do not have but I also do not know where to tranform or return it that the strategy can pick it up and add to the req

#

so I do not know the chain of Resolver->Guard->Strategy

#

and what the strategy does with the configuration because there need to be placeholders for crendentials and so on depending on who is trying to auth

#

If that does not work I guess I have to use ldapjs or something but only in the guard and do the auth from canActivate I think

nova schooner
#

So, if you look at the docs of the ldap strategy, they suggest using basic-auth package, which is just a method to pull out password and username from an object out of the Authorization header in the request.

So, my understanding is, you should be sending a request to a login endpoint with the ldap guard in front. This guard then verifies the user via ldap and allows her through, and I'd imagine Passport will put the user on the request object when verified.

Since the user is a "go" via the guard, the controller would then start your application's login service i.e. setting some sort of session for that user.

#

The rest of your app should use a session guard after that.

hexed beacon
#

do you mean it is REST only?

nova schooner
#

I think it would be better to use a REST endpoint, yes. Simply because it is then very specific for the browser to hit. It might not be necessary, but it just feels smarter to me. 🙂

hexed beacon
#

That might be a general realization when it comes to passport strategies

#

Is it possible to chain strategies? Like if I have excavated the username and password in a local strategy already and pass it to the ldap strategy

nova schooner
#

You can chain guards.

@UseGuards(LocalAuthGuard, LdapAuthGuard)

But this makes little sense to do.

hexed beacon
#

yes, but every direction seems unsatisfying

nova schooner
#

I don't personally understand the setting of credentials in the config objects (if I understand the strategies docs). It might just be better to take the code and make your own service.

hexed beacon
#

the config object has a {{username}} placeholder for ldap

#

I was about to try the same for password but since it seems to be never been hit in the firstplace I didn't made it there yet

nova schooner
#

Hehe...

In my experience to date, people often get frustrated with Passport. 🤷🏻‍♂️

hexed beacon
#

I tried ldapjs before that wasn't so good but maybe ldapauth works better

#

true, mainly because of the callback design and the unknown requirements for the configurations I guess

#

I thought it would make implementation of different auth methods easier

#

built in AuthGuards also do not work with graphql 😄 so I have to write them too

#

so then I call the authservice for ldap auth in the canActivate method, correct?

nova schooner
#

Um, to me, ldap is basically a credentials store (and can offer information about other resources, once access is granted), so your login route should be calling an ldap-login service. And that service calls your ldap server for user verification.

Once verified, then your app should use its own session management.

I've not used ldap personally, so my knowledge is limited.

hexed beacon
#

I would be using the graphql context for that

nova schooner
#

That is also possible, yes.

#

Or rather, use a "login" resolver to call the service.

hexed beacon
#

For now, I only want to check if the user exists in LDAP either with the user credentials itself or that does not works with a proxy user that can search ldap

#

I have a login Mutation

#

and a mutation resolver that has the guard

nova schooner
#

I'd drop the guard idea for now.

hexed beacon
#

thing is to get the input to ldap

nova schooner
#

A login mutation would take in args from a form. Those args are passed into the ldap-login service. Simple enough.

hexed beacon
#

jep, later on maybe from a jwt as well

nova schooner
#

Yeah, like I said. Once the user is verified via ldap, you set your JWTs (i.e. sessions with access and refresh tokens). At that point, you can use JWT guards for your other resolvers.

#

Or have it global.

#

Up to you.

#

It just that, you have to set up your GraphQL client to also do the refresh process. Oh, and the refresh endpoint should be REST too. 🙂

hexed beacon
#

I have that already both in graphql 😄

nova schooner
#

Yeah, not a good idea, because then you must send your refresh token cookie with every request.

#

Well, it's a waste of bits and bytes. 🙂

#

It's simple to make a /refesh REST endpoint.

#

And add that path to your cookie definition.

hexed beacon
#

would be too easy 😉

#

anyway, I get a ECONNRESET when trying to use a client with our ldap