Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject repository in auth provider #1835

Closed
ericalves opened this issue Oct 10, 2018 · 12 comments
Closed

Inject repository in auth provider #1835

ericalves opened this issue Oct 10, 2018 · 12 comments
Assignees

Comments

@ericalves
Copy link

ericalves commented Oct 10, 2018

Related: #1826

I am trying to validate if the token passed in the header of the request is valid, for this I need to find in the database.

I'm trying to inject the repository, but the same gets as undefined. How should I proceed?

import { Provider, inject, ValueOrPromise } from '@loopback/context';
import { Strategy } from 'passport';
import { AuthenticationBindings, AuthenticationMetadata, UserProfile, } from '@loopback/authentication';
import { BasicStrategy } from 'passport-http';
import { AccesstokenRepository } from '../repositories';
import { repository } from '@loopback/repository';

export class MyAuthStrategyProvider implements Provider<Strategy | undefined> {

  constructor(
    @repository(AccesstokenRepository) public accesstokenRepository: AccesstokenRepository,
    @inject(AuthenticationBindings.METADATA) private metadata: AuthenticationMetadata,
  ) { }

  value(): ValueOrPromise<Strategy | undefined> {
    // The function was not decorated, so we shouldn't attempt authentication
    if (!this.metadata) {
      return undefined;
    }
    return new BasicStrategy({
      realm: '',
      passReqToCallback: true
    }, this.verify2);
  }

  verify2(
    req: any,
    username: string,
    password: string,
    done: (error: any, user?: any) => void,
  ) {
    //console.log('req >', req.headers)
    const headers = req.headers

    if (headers.authorization) {
      // here is undefined!!!!!
      console.log('accesstokenRepository >', this.accesstokenRepository);
    } else {
      return done(null, false)
    }
  }
}

It's in my controller and it works:

  constructor(
    @repository(AccesstokenRepository) public accesstokenRepository: AccesstokenRepository,
  ) { }```
@raymondfeng
Copy link
Contributor

You probably don't have the repository bound to the context when the MyAuthStrategyProvider is instantiated. The AccesstokenRepository is bound by @loopback/boot during app.boot().

@bajtos
Copy link
Member

bajtos commented Oct 15, 2018

@ericalves can you try to inject a repository getter instead of the repository class? I don't remember the implementation details of @loopback/authentication, but if the Strategy is instantiated early in application lifecycle (for example from the @authenticate decorator at the time when Controller classes are defined by JS/TS runtime), then getter should solve the problem.

export class MyAuthStrategyProvider implements Provider<Strategy | undefined> {

  constructor(
    @repository.getter(AccesstokenRepository)
    public getAccesstokenRepository: Getter<AccesstokenRepository>,
    @inject(AuthenticationBindings.METADATA) private metadata: AuthenticationMetadata,
  ) { }

  // ...
  verify2(
    req: any,
    username: string,
    password: string,
    done: (error: any, user?: any) => void,
  ) {
    const repo = this.getAccesstokenRepository();
    // etc.
  }
}

@lolocr
Copy link

lolocr commented Oct 16, 2018

I have the same problem as @ericalves. When I have tried your last solution, the problem has not disappeared. I have checked the value of 'this' variable and I have this value: "BasicStrategy { success: [Function], fail: [Function], error: [Function] }". I think that the context inside of 'Verify' function is not the same of the main class 'MyAuthStrategyProvider'.

@raymondfeng
Copy link
Contributor

If one of you can create a sample repo to reproduce the problem, we can help troubleshoot.

@lolocr
Copy link

lolocr commented Oct 16, 2018

I have a model (login.model.ts) with the structure data of users and the repository class (login.repository.ts) with this definition:

import {Login} from '../models';
import {inject} from '@loopback/core';

export class LoginRepository extends DefaultCrudRepository<Login,typeof Login.prototype.id> {
  constructor(@inject('datasources.db') dataSource: juggler.DataSource) {
    super(Login, dataSource);
  }
}

In the strategy provider class i have this:

import {Provider, inject, ValueOrPromise} from '@loopback/context';
import {Strategy} from 'passport';
import {LoginRepository} from '../repositories/login.repository';
import {AuthenticationBindings, AuthenticationMetadata, UserProfile} from '@loopback/authentication';
import {BasicStrategy} from 'passport-http';

export class MyAuthStrategyProvider implements Provider<Strategy | undefined> {
constructor(
    @repository(LoginRepository) protected loginRepo: LoginRepository,
    @inject(AuthenticationBindings.METADATA) private metadata: AuthenticationMetadata,
  ) { }

  value(): ValueOrPromise<Strategy | undefined> {
    // The function was not decorated, so we shouldn't attempt authentication
    if (!this.metadata) {
      return undefined;
    }
    const name = this.metadata.strategy;
    if (name === 'BasicStrategy') {
      return new BasicStrategy({
        passReqToCallback: true
      }, this.verify);
    } else {
      return Promise.reject(`The strategy ${name} is not available.`);
    }
  }

  verify(
    req: any,
    username: string,
    password: string,
    cb: (err: Error | null, user?: UserProfile | false) => void,
  ) {
    console.log(this)
    // find user by name & password
    // call cb(null, false) when user not found
    // call cb(null, user) when user is authenticated
  }
}

I follow the instructions defined in the readme of '@loopback/authentication': https://github.com/strongloop/loopback-next/tree/master/packages/authentication
The problem is when I need to use de LoginRepository object inside of verify function, this value is undefined.

@raymondfeng
Copy link
Contributor

@lolocr Thank you for the information. I would appreciate if you can create a github repo with a LB4 app and your code to show the problem. It would be very helpful for me to check out the repo and debug it.

@lolocr
Copy link

lolocr commented Oct 16, 2018

You can find the repo here: https://github.com/lolocr/lb4auth
Thank you for all

@raymondfeng
Copy link
Contributor

@lolocr I'll take a look

@raymondfeng
Copy link
Contributor

@ericalves I think the root cause is the following:

return new BasicStrategy({
      realm: '',
      passReqToCallback: true
    }, this.verify2);

Please note the this.verify2 lose this when it's invoked.

Can you try the following fix?

return new BasicStrategy({
      realm: '',
      passReqToCallback: true
    }, this.verify2.bind(this));

@raymondfeng
Copy link
Contributor

BTW, I confirmed in debugger that LB4 dependency injection of the repository works correctly in the RequestContext.

@lolocr
Copy link

lolocr commented Oct 17, 2018

It works perfectly!
Thank you for your help.

@Mmalumbo
Copy link

@raymondfeng thank you man

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants