Having difficulty with utilizing bindNodeCallback

Currently experimenting with this code snippet:

return Observable.bindNodeCallback(this.webAuth.client.userInfo)(this.accessToken);

I am attempting to convert the Auth0 userInfo function into an observable for use in the canActivate method within AuthGuard.

Encountering this error message:

TS2346:Supplied parameters do not match any signature of call target.

The corresponding library function is as follows:

Authentication.prototype.userInfo = function(accessToken, cb) {
  var url;
  assert.check(accessToken, {
    type: 'string',
    message: 'accessToken parameter is not valid'
  });
  assert.check(cb, {
    type: 'function',
    message: 'cb parameter is not valid'
  });
  url = urljoin(this.baseOptions.rootUrl, 'userinfo');
  return this.request
    .get(url)
    .set('Authorization', 'Bearer ' + accessToken)
    .end(responseHandler(cb, {
      ignoreCasing: true
    }));
};

Answer №1

I encountered the identical issue. Instead of utilizing bindNodeCallback(), I stumbled upon a solution where you can simply enclose the entire call to userInfo() within Observable.create() as shown below (NOTE: the outer userInfo() function is a part of my AuthProvider class):

userInfo = (token: string): Observable<User> => Observable.create(observer => {
  this.auth0.client.userInfo(token, (err: any, user: User) => {
    if (err) {
      observer.error(err);
    }
    observer.next(user);
    observer.complete();
  });
});

Debugging (using Jest)

If you need a unit test for the code above, I make use of the incredible rxjs-marbles library created by @cartant (who commented in the previous sections). Here are snippets from the relevant parts of my test:

import { TestBed } from '@angular/core/testing';
import { marbles } from 'rxjs-marbles/jest'; // <-- add /jest here for enhancement!
import { Observable } from 'rxjs';
import { StoreModule, Store } from '@ngrx/store';
import { AuthProvider } from './auth'; // my authentication service

// Mocking essential segments of the Auth0 library
jest.mock('auth0-js', () => ({
  WebAuth: options => ({
    client: {
      userInfo: (token, cb) => { 
        if ( "error" === token ) {
          cb(new Error("Profile error!"), null);
        } else {
          cb(null, {name: "Alice"}); 
        }
      }
    }
  })
}));

describe("Auth0.WebAuth.client", () => {
  let auth: AuthProvider;
  let store: Store<any>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports:   [ StoreModule.forRoot({}) ],
      providers: [ AuthProvider ]
    });
    store = TestBed.get(Store);
    spyOn(store, 'pipe');
    auth = TestBed.get(AuthProvider);
  });

  it("should provide an Observable<User> when calling userInfo()", marbles((m) => {
    const user = { name: "Alice" }; // matched with mocked value above
    const source = m.cold("--a", { a: "dummy-access-token" });
    const target = m.cold("--b", { b: user });
    const profile = source.flatMap(auth.userInfo);

    m.expect(profile).toBeObservable(target);
 }));

  it("emit an Error on failure while calling userInfo()", marbles((m) => {
    const err = new Error("Profile error!"); // matched with mocked value above
    const source = m.cold("--a", { a: "error" }); // triggers mock error
    const target = m.cold("--#", null, err);
    const profile = source.flatMap(auth.userInfo);

    m.expect(profile).toBeObservable(target);
  }));
});

Some challenges that I faced:

  • Ensure to utilize flatMap() in your tests! It took me a while to understand its significance
  • I was relatively new to marble testing and had to repeatedly refer to the documentation before grasping the concept thoroughly

Answer №2

Although this thread is old, I managed to enhance my auth0 userInfo method by implementing bindNodeCallback.

In case it might be beneficial to someone else, here is my approach:

// The use of the bind() method is necessary to alter the scope of "this" within the userInfo function
private _userInfo$ = bindNodeCallback(this._Auth0.client.userInfo.bind(this._Auth0.client));

// Retrieve user information
public getUserInfo$(): Observable<any>{
  
  return this.token$.pipe(
    switchMap(token => this._userInfo$(token))
  );

}

// Verify if the user's email is confirmed
public checkEmailVerified$(): Observable<boolean>{

  return this.token$.pipe(
    switchMap(token => this._userInfo$(token)),
    map(userInfo => userInfo.email_verified )
  );

}

Answer №3

The main issue you are facing is that the initial function requires access to the this keyword in order to reach internal properties of the Auth0 Client. To tackle this, you need to associate this client when wrapping its function within an Observable instance. Take a look at this illustrative code:

const userInfo = Observable.bindNodeCallback(this.webAuth.client.userInfo);
userInfo.call(this, this.accessToken);
// or
userInfo.bind(this)(this.accessToken);

As demonstrated above, you can then utilize the response as an Observable.

Answer №4

If you want to make this work, ensure that the parameters are clearly defined:

const fetchUserData$ = Observable.bindNodeCallback((
  token: string,
  callback: Auth0Callback<Auth0UserProfile>
) => this.auth0.client.loadUserInformation(token, callback));

return fetchUserData$(token);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Creating a versatile TypeScript interface that can accurately represent a wide range of types, interfaces, and objects whilst imposing restrictions on the allowable value types within

I am looking to define a versatile TypeScript interface that can accommodate any type, interface, or object while imposing restrictions on the types of values it contains. Let me introduce MyInterface, which includes properties fooIProp and barIProp stori ...

Navigating with Angular: Displaying child components on separate pages

When I click on a button in match-controls.component.html, I want my child component (BasicStatControlComponent) to load on a new page. However, the component is rendering directly below the buttons instead. How can I fix this issue? Any help would be appr ...

Get the data property from the interface and define the return type

In my function, I am trying to extract data from an object structured by an interface. I want this function to specifically resolve the property data from the data object. While it works when I have any as the return type, I believe there should be a more ...

Revamp the way slides are showcased in swiper.js

I implemented a swiper js slider in my project and I am wondering if it is possible to change the default slide that is shown. For example, my slides are named slide1, slide2, slide3, etc. Currently, swiper js is displaying slide 1 by default, but I would ...

Rendering recursively within React Context using an array of objects

I have developed a React Native code that includes a Markdown editor capable of parsing text and extracting information related to Headers. My goal is to construct a menu showcasing the headers, allowing users to click on them and navigate directly to the ...

Having trouble with the npm Fluid Player installation

I am attempting to integrate Fluid Player into my Angular application Using - npm i fluid-player However, I'm encountering this error ...

Ensuring the correct setup of PixiJS within Angular 6

I am facing an issue with correctly installing PixiJS in my Angular 6 project. I attempted to: npm install pixi.js and then: npm install --save @types/pixi.js I also adjusted my path in the script array: "scripts": [ "node_modules/pixi.js/di ...

Creating a Circle with Pixi.js v4 and Typerscript in IONIC 2

I have been attempting to create a custom class in TypeScript that utilizes PIXI.js to draw circles. Below is the code for my home.ts class: import { Component, ViewChild, ElementRef } from '@angular/core'; import { NavController } from 'i ...

Verify if the date and time in string format is the exact same as noon

In my data collection, there are multiple objects each containing a specific date and time value: [ {dt: "2019-11-29 12:00:00"}, {dt: "2019-11-29 3:00:00"}, {dt: "2019-11-29 6:00:00"}, {dt: "2019-11-30 12:00:00"}, {dt: "2019-11-30 6:00:00"} ] M ...

Updating the status of various sections with Redux toolkit

Is it possible to update the state of a store, which consists of multiple slices, with a new state in React using Redux Toolkit? While you can revert the entire store to its initial state using extraReducers, is it also possible to change the state to som ...

Typescript is failing to perform type checking

I'm encountering an issue while trying to utilize TypeScript type checking with the following code snippet: abstract class Mammal { abstract breed(other: Mammal); } class Dog extends Mammal { breed(other: Dog) {} } class Cat extends Mammal { ...

Customizing the appearance of a form control in response to its value in Angular's Reactive

I have a unique query regarding formatting text related to formControl in an application where the formControls are created using FormBuilder and arrays of formControls. This inquiry involves retrieving the current value of a formControl and altering the ...

The Angular router outlet is seamlessly activating without any manual intervention

Within the main view, a mat-button-toggle-group is used to toggle between a grid and list layout. The grid layout utilizes router-outlet to display the child component <router-outlet></router-outlet>, while the list layout directly implements t ...

An issue has been identified with React's HTML input maxLength feature where it does not display an error

Within my form, I have an input field that currently does not include validation for a maximum length. <input type="text" className="form-control" id="company" onBlur= ...

Issue with Importing 'tiny-secp256k1' Library in Jest with Typescript

I have recently integrated the npm package tiny-secp256k1 into my project. This package offers non-default exports of various functions, each with type declaration included. Despite multiple attempts to import it and run my test suite using Jest, I keep ...

Ensure the cursor is continually grabbing while moving items within a list using the @angular/cdk/drag-drop functionality

I have an example on stackblitz where I am using @angular/cdk/drag-drop in my project. I am attempting to change the cursor to cursor:grabb and cursor:grabbing when the cursor is over an element and when I drag a picked element. Here is the CSS line I am ...

AngluarFire 2 authState function displaying null after refreshing the page

Currently, I am working on integrating Firebase with AngularFire 2 and facing an issue. The problem arises when I try to refresh the page, as the auth instance returns null. Below is the code snippet for my AuthService: Everything functions correctly, b ...

Encountering a configuration error in the Nginx.conf file while attempting to use

I recently obtained a Visual Studio solution that includes multiple projects. https://i.sstatic.net/HuRyl.jpg A Nginx.conf file was created in ClientApp/Angular. https://i.sstatic.net/CN65e.jpg This is my docker-compose file: clientapp: image: ${DOCKER ...

Struggling to compile a next.js application using both typescript and sass

After creating my first Next.js app, I encountered deployment problems. The issue seems to be related to the node version on the server as running "npm run build" successfully on my Windows computer fails on the Ubuntu server with the following error: Glob ...

The error message "Directive does not contain a property called 'element'.ts" is displayed

I'm encountering an issue where the property 'element' is not recognized on 'directive'. I am currently working on implementing functionalities for directives. import { Directive, ElementRef, HostListener } from '@angular/cor ...