How to integrate passport-apple in Loopback 4?

I am following the integration setup of https://github.com/loopbackio/loopback-next/tree/master/examples/passport-login

However, this example uses an Express server with nextjs integrated into the project.

In my case I am useing ReactJS to request an Apple ID Login:

  const loginSocial = (provider: string) => {
    const url = `${process.env.REACT_APP_API_URL}/auth/thirdparty/${provider}`;
    fetch(url).then((res) => {
      return res.json()
    }).then((e) => {
      window.open(e.redirectUrl, '_blank', 'height=600,width=400');
    }).catch((e) => {
      console.warn(e);
    })
  }

My controller looks like this:

import {authenticate, AuthenticationBindings} from '@loopback/authentication';
import {inject} from '@loopback/core';
import {get, param, post, Request, RequestWithSession, Response, RestBindings} from '@loopback/rest';
import {SecurityBindings, UserProfile} from '@loopback/security';
import {oAuth2InterceptExpressMiddleware} from '../authentication-interceptors';
import passport from "passport";

/**
 * Login controller for third party oauth provider
 *
 * This controller demonstrates using passport strategies both as express middleware and as an independent strategy
 *
 * The method loginToThirdParty uses the @authenticate decorator to plugin passport strategies independently
 * The method thirdPartyCallBack uses the passport strategies as express middleware
 */
export class Oauth2Controller {
  constructor() {}

  @authenticate('oauth2')
  @get('/auth/thirdparty/{provider}')
  /**
   * This method uses the @authenticate decorator to plugin passport strategies independently
   *
   * Endpoint: '/auth/thirdparty/{provider}'
   *          an endpoint for api clients to login via a third party app, redirects to third party app
   */
  loginToThirdParty(
    @param.path.string('provider') provider: string,
    @inject(AuthenticationBindings.AUTHENTICATION_REDIRECT_URL)
    redirectUrl: string,
    @inject(AuthenticationBindings.AUTHENTICATION_REDIRECT_STATUS)
    status: number,
    @inject(RestBindings.Http.RESPONSE)
    response: Response,
  ) {
    return {redirectUrl: redirectUrl};
  }

  @oAuth2InterceptExpressMiddleware()
  @get('/auth/thirdparty/{provider}/callback')
  /**
   * This method uses the passport strategies as express middleware
   *
   * Endpoint: '/auth/thirdparty/{provider}/callback'
   *          an endpoint which serves as a oauth2 callback for the thirdparty app
   *          this endpoint sets the user profile in the session
   */
  async thirdPartyCallBack(
    @param.path.string('provider') provider: string,
    @inject(SecurityBindings.USER) user: UserProfile,
    @inject(RestBindings.Http.REQUEST) request: RequestWithSession,
    @inject(RestBindings.Http.RESPONSE) response: Response,
  ) {
    const profile = {
      ...user.profile,
    };
    request.session.user = profile;
    response.redirect('/auth/account');
    return response;
  }

  @oAuth2InterceptExpressMiddleware()
  @post('/auth/thirdparty/{provider}/callback')
  /**
   * This method uses the passport strategies as express middleware
   *
   * Endpoint: '/auth/thirdparty/{provider}/callback'
   *          an endpoint which serves as a oauth2 callback for the thirdparty app
   *          this endpoint sets the user profile in the session
   */
  async thirdPartyCallBackPost(
    @param.path.string('provider') provider: string,
    // @inject(SecurityBindings.USER) user: UserProfile,
    @inject(RestBindings.Http.REQUEST) request: Request,
    @inject(RestBindings.Http.RESPONSE) response: Response,
  ) {

    console.log('reached callback');
    console.log(request.body);

    passport.authenticate('apple', function(err: any, user: any, info: any) {
      console.log(user);
    })(request, response);
    // return request;
  }
}

It turns out that the Apple callback is using a POST method, so I added this to the controller. Note that in the example in the repository there is only a GET method, (I am not sure why that is the case).

However, it only reaches my POST endpoint if I remove @oAuth2InterceptExpressMiddleware(). If I include the decorator nothing happens, and the Apple login windo (which was opened from my ReactJS app) remains open and simply redirects to the login screen. But if I remove the decorator I miss the entire middleware setup as it was intended within the example application.

So my questions are:

  • How do I successfully integrate passport-apple into my Loopback 4 API?
  • How is the Apple login window auto-closed after login has been completed?
  • Why does the example repo have a GET endpoint while Apple is clearly using POST method?

  • Hi, you have a wrong tag. [loopback] should be [loopbackjs]

    – 

Leave a Comment