Search code examples
node.jsloopback

Loopback 4 authentication metadata options undefined


I have created a simple jwt auth application the same way its displayed here: https://github.com/raymondfeng/loopback4-example-auth0

The authentication part is working properly but the authorization does not work as expected.

I decorated my controller with following function and added a scope.

@authenticate({strategy: 'auth0-jwt', options: {scopes: ['greet']}})

In my authentication strategy I´m checking the scope via the AuthenticationMetadata class.

import {AuthenticationBindings, AuthenticationMetadata, AuthenticationStrategy} from '@loopback/authentication';
import {inject} from '@loopback/core';
import {ExpressRequestHandler, Request, Response, RestBindings} from '@loopback/rest';
import {UserProfile} from '@loopback/security';
import {JWT_SERVICE} from './types';

const jwtAuthz = require('express-jwt-authz');

export class JWTAuthenticationStrategy implements AuthenticationStrategy {
  name = 'auth0-jwt';

  constructor(
    @inject(RestBindings.Http.RESPONSE)
    private response: Response,
    @inject(AuthenticationBindings.METADATA)
    private metadata: AuthenticationMetadata,
    @inject(JWT_SERVICE)
    private jwtCheck: ExpressRequestHandler,
  ) {}

  async authenticate(request: Request): Promise<UserProfile | undefined> {
    return new Promise<UserProfile | undefined>((resolve, reject) => {
      this.jwtCheck(request, this.response, (err: unknown) => {
        if (err) {
          console.error(err);
          reject(err);
          return;
        }
        console.log(this.metadata.options);
        // If the `@authenticate` requires `scopes` check
        if (this.metadata.options?.scopes) {
          jwtAuthz(this.metadata.options!.scopes, {failWithError: true})(request, this.response, (err2?: Error) => {
            if (err2) {
              console.error(err2);
              reject(err2);
              return;
            }
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            resolve((request as any).user);
          });
        } else {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          resolve((request as any).user);
        }
      });
    });
  }
}

When trying to access

this.metadata.options

I´m always getting an undefined back.

How can I achieve to get the options and the scope out of metadata?

Thanks


Solution

  • For Loopback Authorization your class needs to implement the Provider<Authorizer> interface. In that interface it defines the 2 functions you need to implement

    @injectable({scope: BindingScope.TRANSIENT})
    class AuthorizationService implements Provider<Authorizer>{
    
      value (): Authorizer {
        return this.authorize.bind(this);  
      }
    
      async authorize (
        context: AuthorizationContext,
        metadata: AuthorizationMetadata,
      ) {
         // TODO implement authorization
      }
    
    }
    

    The authorization metadata will be injected by loopback into that function automatically after you bind it with an AuthorizationTags.Authorizer

    If you are having problems implementing Authentication then read my step by step guide on how we implemented Loopback Authentication using Firebase. That should be able to help you with the core ideas to get Authentication running.