_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:
export class AccesstokenController {
constructor(
@repository(AccesstokenRepository) public accesstokenRepository: AccesstokenRepository,
) { }
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().
@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.
}
}
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'.
If one of you can create a sample repo to reproduce the problem, we can help troubleshoot.
I have a model (login.model.ts) with the structure data of users and the repository class (login.repository.ts) with this definition:
```import {DefaultCrudRepository, juggler} from '@loopback/repository';
import {Login} from '../models';
import {inject} from '@loopback/core';
export class LoginRepository extends DefaultCrudRepository
constructor(@inject('datasources.db') dataSource: juggler.DataSource) {
super(Login, dataSource);
}
}
In the strategy provider class i have this:
```import {repository} from '@loopback/repository';
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.
@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.
You can find the repo here: https://github.com/lolocr/lb4auth
Thank you for all
@lolocr I'll take a look
@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));
BTW, I confirmed in debugger that LB4 dependency injection of the repository works correctly in the RequestContext.
It works perfectly!
Thank you for your help.
@raymondfeng thank you man
Most helpful comment
@ericalves I think the root cause is the following:
Please note the
this.verify2losethiswhen it's invoked.Can you try the following fix?