Search code examples
node.jsloopback4

Loopback 4 error after authentication. The key 'authentication.currentUser' is not bound to any value in context application


I've been working with Loopback 4. I'm facing an error recently. After authentication is successful and I try to access the current user from my end point. I get this error

13:31:07 0|index  | Unhandled error in POST /posts: 500 Error: The key 'authentication.currentUser' is not bound to any value in context application
13:31:07 0|index  |     at HomeApplication.getBinding (/Development/home/node_modules/@loopback/context/dist/context.js:596:15)
13:31:07 0|index  |     at RestServer.getBinding (/Development/home/node_modules/@loopback/context/dist/context.js:592:33)
13:31:07 0|index  |     at RequestContext.getBinding (/Development/home/node_modules/@loopback/context/dist/context.js:592:33)
13:31:07 0|index  |     at RequestContext.getValueOrPromise (/Development/home/node_modules/@loopback/context/dist/context.js:651:30)
13:31:07 0|index  |     at RequestContext.get (/Development/home/node_modules/@loopback/context/dist/context.js:574:21)
13:31:07 0|index  |     at PostController.getter [as getCurrentUser] (/Development/home/node_modules/@loopback/context/dist/inject.js:258:20)
13:31:07 0|index  |     at PostController.<anonymous> (/Development/home/dist/controllers/user/post.controller.js:57:35)

I've tried all the available solutions to this problem on GitHub and StackOverflow. Been searching for a solution for a couple of days now.

I also tried to debug this myself by putting console logs in generated files. This is what I did in auth-action.provider.js

async action(request) {
    const strategy = await this.getStrategy();
    if (!strategy) {
        // The invoked operation does not require authentication.
        return undefined;
    }
    console.log(`Strategy obtained successfully`)
    const userProfile = await strategy.authenticate(request);
    if (!userProfile) {
        // important to throw a non-protocol-specific error here
        const error = new Error(`User profile not returned from strategy's authenticate function`);
        Object.assign(error, {
            code: types_1.USER_PROFILE_NOT_FOUND,
        });
        throw error;
    }
    console.log(`User obtained successfully`)
    console.log(JSON.stringify(userProfile))
    this.setCurrentUser(userProfile);
    console.log(`Called setCurrentUser`)
    return userProfile;
}

result after this

13:46:55 0|index  | Authenticating
13:46:55 0|index  | Strategy obtained successfully
13:46:55 0|index  | ~~ Authenticating: token --> 9823eb1940eae6df49c54698d8a71b319f0b00635321a02632965a7667d69ce68883b61d803f0691c2b393bc9841606b153fa28fc853ecfd41bd647725479b54,  mode --> personal~~
13:46:55 0|index  | User found. Id --> 5d8f549de7179a022443e34e
13:46:55 0|index  | User obtained successfully
13:46:55 0|index  | {"email":"[email protected]","id":"5d8f549de7179a022443e34e","name":"name","image":null,"designation":"Designation","company":"string"}
13:46:55 0|index  | Called setCurrentUser
13:46:55 0|index  | Trace: Error: The key 'authentication.currentUser' is not bound to any value in context application
13:46:55 0|index  |     at HomeApplication.getBinding (/Development/home/node_modules/@loopback/context/dist/context.js:596:15)
13:46:55 0|index  |     at RestServer.getBinding (/Development/home/node_modules/@loopback/context/dist/context.js:592:33)
13:46:55 0|index  |     at RequestContext.getBinding (/Development/home/node_modules/@loopback/context/dist/context.js:592:33)
13:46:55 0|index  |     at RequestContext.getValueOrPromise (/Development/home/node_modules/@loopback/context/dist/context.js:651:30)
13:46:55 0|index  |     at RequestContext.get (/Development/home/node_modules/@loopback/context/dist/context.js:574:21)
13:46:55 0|index  |     at PostController.getter [as getCurrentUser] (/Development/home/node_modules/@loopback/context/dist/inject.js:258:20)
13:46:55 0|index  |     at PostController.<anonymous> (/Development/home/dist/controllers/user/post.controller.js:57:35)

As you can see everything is working as expected. It's just that when I try to get the user in my controller, this error occurs. If I don't try to access the user, everything works fine.

My guess is something is wrong with the setCurrentUser method injected in auth-action.provider.

Here is my controller if you need it.

export class PostController {

  static MAX_POST_LENGTH = 800;

  constructor(
    @repository(PostRepository)
    public postRepository: PostRepository,
    @inject(MyAuthBindings.USER_TOKEN_SERVICE)
    public userTokenService: UserTokenService,
    @inject.getter(AuthenticationBindings.CURRENT_USER)
    public getCurrentUser: Getter<MyUserProfile>,
  ) {
  }

  @post('/posts', {
    responses: {
      '200': {
        description: 'Post model instance',
        content: {'application/json': {schema: {'x-ts-type': UserPost}}},
      },
    },
  })
  @authenticate('user')
  async create(
    @param.header.string('token') token: string,
    @param.header.string('mode') mode: string,
    @requestBody() newPostRequest: PostRequest): Promise<UserPost> {

    if (newPostRequest.body.length > PostController.MAX_POST_LENGTH) {
      throw new HttpErrors.BadRequest(`Post body cannot be larger than ${PostController.MAX_POST_LENGTH} characters`);
    }

    let user = await this.getCurrentUser();

    console.log(`Authenticated user --> ${JSON.stringify(user, null, 2)}`);

    let postItem = new Post(newPostRequest);
    postItem.userId = user.id;
    postItem.createdAt = new Date();
    postItem.updatedAt = new Date();
    postItem = await this.postRepository.create(postItem);

    if (postItem.id != null)
      PostController.checkAndSendStyleworkPostNotification(user.id, postItem.id);

    return new UserPost(postItem, user, user);
  }

Processing does not get to the log in the method above.

P.S. This problem started to occur after @loopback/authentication update from 2.2.2 to 3.0.0


Solution

  • I fixed the issue. The problem was as noted in Authentication component documentation that AuthenticationBindings.CURRENT_USER is now an alias of SecurityBindings.USER in @loopback/security.

    I just had to replace AuthenticationBindings.CURRENT_USER with SecurityBindings.USER. This change was made in v3.0.0 as I noted in my question.