Loopback-next: LB4: The key 'authentication.currentUser' is not bound to any value in context application

Created on 7 Apr 2019  路  13Comments  路  Source: strongloop/loopback-next

I'm starting to use lb4 to create a small application with authentication by jwt. I have been reviewing the shopping-cart code with authentication and apparently have followed a similar scheme, however, when I try to verify an api or simply return a user's information through the token, the exception appears.

I'm stuck and I do not know how to solve the problem

Description / Steps to reproduce / Feature proposal

  @get('/auth/me', {
    responses: {
      '200': {
        description: 'The current user profile',
        content: {
          'application/json': {
            schema: UserProfileSchema,
          },
        },
      },
    },
  })
  @authenticate('jwt')
  async printCurrentUser(
    @inject('authentication.currentUser') user: UserProfile,
  ): Promise < UserProfile > {
    return user;
  }

Current Behavior

Unhandled error in GET /auth/me: 500 Error: The key 'authentication.currentUser' is not bound to any value in context application
    at AppMirrorTestApplication.getBinding (/opt/app-mirror-test/node_modules/@loopback/context/dist/context.js:503:15)
    at RestServer.getBinding (/opt/app-mirror-test/node_modules/@loopback/context/dist/context.js:499:33)
    at RequestContext.getBinding (/opt/app-mirror-test/node_modules/@loopback/context/dist/context.js:499:33)
    at RequestContext.getValueOrPromise (/opt/app-mirror-test/node_modules/@loopback/context/dist/context.js:535:30)
    at resolution_session_1.ResolutionSession.runWithInjection.s (/opt/app-mirror-test/node_modules/@loopback/context/dist/resolver.js:103:24)
    at value_promise_1.tryWithFinally (/opt/app-mirror-test/node_modules/@loopback/context/dist/resolution-session.js:89:53)
    at Object.tryWithFinally (/opt/app-mirror-test/node_modules/@loopback/context/dist/value-promise.js:158:18)
    at Function.runWithInjection (/opt/app-mirror-test/node_modules/@loopback/context/dist/resolution-session.js:89:32)
    at resolve (/opt/app-mirror-test/node_modules/@loopback/context/dist/resolver.js:94:59)
    at value_promise_1.resolveList (/opt/app-mirror-test/node_modules/@loopback/context/dist/resolver.js:175:16)
    at Object.resolveList (/opt/app-mirror-test/node_modules/@loopback/context/dist/value-promise.js:131:32)
    at resolveInjectedArguments (/opt/app-mirror-test/node_modules/@loopback/context/dist/resolver.js:158:28)
    at Object.invokeMethod (/opt/app-mirror-test/node_modules/@loopback/context/dist/resolver.js:200:27)
    at ControllerRoute.invokeHandler (/opt/app-mirror-test/node_modules/@loopback/rest/dist/router/controller-route.js:78:32)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Files

Controller auth

import {inject} from '@loopback/core';
import {get, post,requestBody} from '@loopback/rest';
import {authenticate, UserProfile } from '@loopback/authentication';
import {
  CredentialsRequestBody,
  UserProfileSchema,
} from './specs/user-controller.specs';
import {Credentials} from '../repositories/user.repository';
import {JWTAuthenticationBindings} from '../keys';
import {JWTAuthenticationService} from '../services/JWT.authentication.service';
import {validateCredentials} from '../services/JWT.authentication.service';

export class AuthController {
  constructor(
    @inject(JWTAuthenticationBindings.SERVICE)
    public jwtAuthenticationService: JWTAuthenticationService,
  ) {}

  @post('/auth', {
    responses: {
      '200': {
        description: 'Token',
        content: {
          'application/json': {
            schema: {
              type: 'object',
              properties: {
                token: {
                  type: 'string',
                },
              },
            },
          },
        },
      },
    },
  })

  // @authenticate('jwt', {action: 'generateAccessToken'})
  async auth(
    @requestBody(CredentialsRequestBody) credentials: Credentials,
  ): Promise<{token: string}> {
    validateCredentials(credentials);
    const token = await this.jwtAuthenticationService.getAccessTokenForUser(
      credentials,
    );
    return {token};
  }

  @get('/auth/me', {
    responses: {
      '200': {
        description: 'The current user profile',
        content: {
          'application/json': {
            schema: UserProfileSchema,
          },
        },
      },
    },
  })
  @authenticate('jwt')
  async printCurrentUser(
    @inject('authentication.currentUser') user: UserProfile,
  ): Promise < UserProfile > {
    return user;
  }
}

All 13 comments

@emonddr , could you please help with this question? thanks.

@charlypeluch , I will investigate this. It would be more helpful to have a github repo to clone and take a look at...

The error

Unhandled error in GET /auth/me: 500 Error: The key 'authentication.currentUser' is not bound to any value in context application

is basically indicating that a binding key wasn't bound properly to the application context.

This key is :

  export const CURRENT_USER = BindingKey.create<UserProfile | undefined>(
    'authentication.currentUser',
  );

and is defined in

https://github.com/strongloop/loopback-next/blob/master/packages/authentication/src/keys.ts#L99

I added your AuthController code into a file auth.controller.ts and placed it into the
src/controllers directory in a cloned copy of https://github.com/strongloop/loopback4-example-shopping .

I also updated src\index.ts to include your new controller

// Copyright IBM Corp. 2018. All Rights Reserved.
// Node module: loopback4-example-shopping
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT

export * from './ping.controller';
export * from './user.controller';
export * from './shopping-cart.controller';
export * from './user-order.controller';
export * from './auth.controller';

I kept the user.controller (that comes with example), so that I could add users.

I installed the dependencies:

npm install

I ran to start the database and redis

./bin/start-dbs.sh

I started the shopping cart app:

npm start

I posted a new user
using

UserController
post /users (make sure password is at least 8 chars, or it will fail)

I then obtained a token using both:

UserController
post /users/login

OR

AuthController
post /auth

(that you provided)

I then printed the user's info (using POSTMAN to pass the token) :

UserController
get /users/me

OR

AuthController
get /auth/me

Everything ran successfully.

@emonddr Hello there!
Thank you for so fast answer & excuse me that I write here my question.
but, I have started to use lb4 and have problem with jwt too.

Perhaps somewhere has a complete example with lb4 & jwt?

i took an example with packages/authentication change passport-http with passport-jwt and
got 401 as OK.

but i had 500, the same error of this topic when trying to get route without authenticate:

//file: who-am-i.controller.ts
export class WhoAmIController {
  constructor(
    @inject(AuthenticationBindings.CURRENT_USER, { optional: true })
    private user: UserProfile,
  ) { }

  @authenticate('ExtractJwt')
  @get('/whoami')
  whoAmI(): string {
    return this.user.id;
  }

  @get('/whoami2')
  whoAmI2(): string {
    return 'HELLO';
  }
}

p.s.: I think I did something bad in my application.ts or auth.strategy.ts ...

p.s.: oh, didnt see you last answer... brb

It is in the authentication action that a user is set on the context

https://github.com/strongloop/loopback4-example-shopping/blob/master/src/providers/custom.authentication.provider.ts#L28

image

Both the UserController and AuthController have a method to print the current user in the context.

This is yours...

image

It looks correct, and I've tried it..it is correct.

So perhaps your action isn't setting it in the context?

If I comment out setCurrentUser method in my authentication action:

image

and I run the steps outlined earlier (logging in , getting a token, then trying to print out user info),
I get:

500 Error: The key 'authentication.currentUser' is not bound to any value in context application

e.g.

Mock Product Recommender powered by Express started on port 3001
[0] Unhandled error in GET /auth/me?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjUiLCJlbWFpbCI6Im1hZzJAZ21haWwuY29tIiwiaWF0IjoxNTU0OTA4MzM0LCJleHAiOjE1NTQ5MDg2MzR9.WZ6Te5mhv6Wx1vF4V2YygsUC3TAO4czUofW15aREqUw: 500 Error: The key 'authentication.currentUser' is not bound to any value in context application
[0] at ShoppingApplication.getBinding (/Users/dremond/Documents/PRs_LoopBack/bug_2700/loopback4-example-shopping/node_modules/@loopback/context/dist/context.js:503:15)

@charlypeluch ^^^

@xenik , please see : https://github.com/strongloop/loopback4-example-shopping for a working jwt example

Hi @emonddr,

I spent the whole week working and today I was able to review the subject again. I still have the same error (sorry for my clumsiness), as I indicated I have uploaded the code in a repository to see if we can see the problem more easily.

https://github.com/charlypeluch/lb4_authentication_jwt

Thanks for the speed resonse and dedication! :-)

Finish i found the error:

Problem was in sequence ts, i forgot to import the authentication:

Sequence.ts

import {inject} from '@loopback/context';
import {
  FindRoute,
  InvokeMethod,
  ParseParams,
  Reject,
  RequestContext,
  RestBindings,
  Send,
  SequenceHandler,
} from '@loopback/rest';
import {AuthenticationBindings, AuthenticateFn} from '@loopback/authentication';

const SequenceActions = RestBindings.SequenceActions;

export class MySequence implements SequenceHandler {
  constructor(
    @inject(SequenceActions.FIND_ROUTE) protected findRoute: FindRoute,
    @inject(SequenceActions.PARSE_PARAMS) protected parseParams: ParseParams,
    @inject(SequenceActions.INVOKE_METHOD) protected invoke: InvokeMethod,
    @inject(SequenceActions.SEND) public send: Send,
    @inject(SequenceActions.REJECT) public reject: Reject,
    @inject(AuthenticationBindings.AUTH_ACTION)
    protected authenticateRequest: AuthenticateFn,
  ) {}

  async handle(context: RequestContext) {
    try {
      const {request, response} = context;
      const route = this.findRoute(request);
      await this.authenticateRequest(request);
      const args = await this.parseParams(request, route);
      const result = await this.invoke(route, args);
      this.send(response, result);
    } catch (err) {
      this.reject(context, err);
    }
  }
}

Closing this issue as @charlypeluch found the cause in his code.

Thank you @charlypeluch, you saved me from hours of frustration

Was this page helpful?
0 / 5 - 0 ratings