Loopback-next: [Spike] Explore/research the oauth2+session based authentication strategy

Created on 21 Mar 2019  路  13Comments  路  Source: strongloop/loopback-next

Description / Steps to reproduce / Feature proposal

A follow-up story for PR https://github.com/strongloop/loopback-next/pull/2576

Add new authentication strategies in loopback4-example-shopping to investigate how to verify a 3rd-party user using oauth2, and track the user using session.

Acceptance Criteria:

The acceptance criteria is written with oauth2+google, the story owner can choose any 3rd party auth provider: oauth2+fb, oauth2+google, etc...

  • [x] Create a static login page that allows user to choose from local login or 3rd-party login. Keep the UI as simple as possible
  • [x] ~Create an oauth2 strategy to authenticate the users by interacting with 3rd-party provider's auth server~ completed with https://github.com/strongloop/loopback-next/pull/4919

    • [x] Please note that usually 3rd-party login requires a callback URL, which implies 2 endpoints are involved in the login flow. E.g. login/facebook and login/facebook/callback. And this is the most important user experience we need to figure out from design's perspective.
  • [x] Track the user profile using session. Keep the session related functionalities as simple as possible, we can create sub-stories to enhance.

  • [x] Create an example endpoint that verifies user using session.
    ~- [ ] Update the md file docs/strategies/oauth2.md~
2020Q2 Authentication feature spike

Most helpful comment

following pending items in this issue will be completed by https://github.com/strongloop/loopback-next/pull/4899

  • Create a static login page that allows user to choose from local login or 3rd-party login. Keep the UI as simple as possible
  • Track the user profile using session. Keep the session related functionalities as simple as possible, we can create sub-stories to enhance.
  • Create an example endpoint that verifies user using session.
  • Update the md file docs/strategies/oauth2.md

All 13 comments

After talking with @raymondfeng yesterday, he suggested we investigate the two main flows of OAuth

  • Implicit Flow
  • Authorization Code Flow

Implicit Flow is when all of the logic for authentication is in the UI flow, and the goal is to get the access token, and then pass this token to the backend when accessing endpoints.

https://auth0.com/docs/flows/concepts/implicit

Authorization Code Flow is when the backend server has to direct the user to login, and the oauth server sends an authorization code to the backend. The backend uses this temporary code to get an access token and passes it to the client. The client then has to use it to access all the backend's protected endpoints. The backend, when sent a token, needs to always double-check with Facebook that this token is a valid token...so it needs to call the FB api to do this.

https://auth0.com/docs/flows/concepts/auth-code

With regards to https://www.npmjs.com/package/@loopback/authentication-passport , we know the adapter works well with passport-jwt and passport-http.

We need to adjust the design (if necessary) to ensure that it can work well with OAuth2.0 passport-* modules like passport-facebook, passport-google, etc.

Most people uses Facebook authentication to get a token to then invoke Facebook API.
This is not our goal per se.
We want to use it to get a Bearer token to protect our server API.

We should also look into passport-http-bearer as well : http://www.passportjs.org/docs/oauth2-api/

I began playing around with Facebook stuff in my investigation.

All of these projects need an App ID and secret of a Facebook app that is registered via the Facebook Developer Console.

Node.js / Express app using passport-facebook

https://github.com/emonddr/express-4.x-facebook-example

server.js is an example from the author of passport-facebook.
blogger_example.js is an example from a blogger that was playing with passport-facebook.

Loopback 4 app using passport-http

https://github.com/emonddr/authapp

It uses https://www.npmjs.com/package/@loopback/authentication-passport adapter to wrap passport-http module. It uses the Provider approach.

Loopback 4 app using passport-facebook

https://github.com/emonddr/authapp_fb

It uses https://www.npmjs.com/package/@loopback/authentication-passport adapter to wrap passport-facebook module. It uses the Provider approach.

It is incomplete. I need to get more familiar with the redirecting url flows that seem to occur with OAuth2.0.

Currently my app suffers from this error

Unhandled error in GET /auth/facebook/callback: 500 InternalServerError: TypeError: self.redirect is not a function
        at Strategy.strategy.error (/Users/dremond/Documents/all_facebook_auth_examples/authapp_fb/node_modules/@loopback/authentication-passport/src/strategy-adapter.ts:57:16)
        at Strategy.OAuth2Strategy.authenticate (/Users/dremond/Documents/all_facebook_auth_examples/authapp_fb/node_modules/passport-oauth2/lib/strategy.js:285:21)
        at Strategy.authenticate (/Users/dremond/Documents/all_facebook_auth_examples/authapp_fb/node_modules/passport-facebook/lib/strategy.js:84:41)
        at Promise (/Users/dremond/Documents/all_facebook_auth_examples/authapp_fb/node_modules/@loopback/authentication-passport/src/strategy-adapter.ts:61:16)
        at new Promise (<anonymous>)
        at StrategyAdapter.authenticate (/Users/dremond/Documents/all_facebook_auth_examples/authapp_fb/node_modules/@loopback/authentication-passport/src/strategy-adapter.ts:35:12)
        at AuthenticateActionProvider.action (/Users/dremond/Documents/all_facebook_auth_examples/authapp_fb/node_modules/@loopback/authentication/src/providers/auth-action.provider.ts:52:40)
        at process._tickCallback (internal/process/next_tick.js:68:7)

In https://github.com/emonddr/authapp_fb/blob/master/src/controllers/facebook.controller.ts
I was trying to have the authentication strategy handle

  • /auth/facebook
  • /auth/facebook/callback

and ran into the self.redirect error above.

These endpoints are decorated like this:

  • @authenticate('facebook', {scope: "email"})
  • @authenticate('facebook', {successRedirect: "/content", failureRedirect: "/"})

In pure custom authentication strategies, there is a way to get the options for every requested url that is decorated with @authenticate : https://loopback.io/doc/en/lb4/Loopback-component-authentication.html#update-custom-authentication-strategy-to-handle-options .

Currently there is no way to pass these into the strategy adapter
https://www.npmjs.com/package/@loopback/authentication-passport .

In https://github.com/strongloop/loopback-next/blob/labs/passport-adapter/labs/authentication-passport/src/strategy-adapter.ts

we need to be able to pass in options in the line

strategy.authenticate(request);

A good place to pass in the request options is probably as an optional parameter in the constructor. https://github.com/strongloop/loopback-next/blob/labs/passport-adapter/labs/authentication-passport/src/strategy-adapter.ts#L25

Since an authentication provider strategy is instantiated with each request url, the authentication strategy should obtain the options from the metadata of the endpoint decorated with @authenticate, and pass in the options in the constructor of the strategy adapter as another optional parameter.

In my facebook authentication strategy provider, on this line
https://github.com/emonddr/authapp_fb/blob/master/src/provider/passportfacebook.provider.ts#L52

instead of

return new StrategyAdapter(facebook, AUTH_STRATEGY_NAME);

we would need to do

return new StrategyAdapter(facebook, AUTH_STRATEGY_NAME, request_options);

where request_options are the options specified in the @authenticate decorator for that request (specific endpoint).

Web UI app (One of the official Facebook examples)

Also part of authapp_fb (but not interacting with the Loopback 4 server at all) was an
example given by Facebook itself.

I just piggy-backed it as a static HTML website on the Loopback4 server.

The code is here:
https://github.com/emonddr/authapp_fb/blob/master/public/facebook/login.html

The /facebooklogin endpoint is defined here : https://github.com/emonddr/authapp_fb/blob/master/src/application.ts#L25

Thanks for sharing @emonddr . I am running into the exact same problem with Loopback4 when trying to implement facebook authentication.

I know RestServer and RestApplication offer API's for redirect. Should we be leveraging this somehow in the Strategy?

Unhandled error in GET /auth/facebook: 500 InternalServerError: TypeError: self.redirect is not a function
    at Strategy.strategy.error (c:\Users\sbesong\Documents\WORKSPACE\VOID\myworry-server\node_modules\@loopback\authentication-passport\src\strategy-adapter.ts:73:16)
    at Strategy.OAuth2Strategy.authenticate (c:\Users\sbesong\Documents\WORKSPACE\VOID\myworry-server\node_modules\passport-oauth2\lib\strategy.js:285:21)
    at Strategy.authenticate (c:\Users\sbesong\Documents\WORKSPACE\VOID\myworry-server\node_modules\passport-facebook\lib\strategy.js:84:41)
    at Promise (c:\Users\sbesong\Documents\WORKSPACE\VOID\myworry-server\node_modules\@loopback\authentication-passport\src\strategy-adapter.ts:77:16)
    at new Promise (<anonymous>)
    at StrategyAdapter.authenticate (c:\Users\sbesong\Documents\WORKSPACE\VOID\myworry-server\node_modules\@loopback\authentication-passport\src\strategy-adapter.ts:47:12)
    at AuthenticateActionProvider.action (c:\Users\sbesong\Documents\WORKSPACE\VOID\myworry-server\node_modules\@loopback\authentication\src\providers\auth-action.provider.ts:52:40)
    at process._tickCallback (internal/process/next_tick.js:68:7)

Is there any update on this?

I also have the same concern with passport-github.

I found this deposit which seems to correct the problem:
https://github.com/sourcefuse/loopback4-authentication

Thanks for investigating that issue @emonddr. I was working on a strategy for keycloak and end up with the same problem.

Unhandled error in GET /whoami: 500 InternalServerError: TypeError: self.redirect is not a function
    at Strategy.strategy.error (/Users/pilare/Workspace/loopback/lb-4-getting-started/node_modules/@loopback/authentication-passport/src/strategy-adapter.ts:73:16)

@jannyHou any chances for some development or instructions on how to get past that?

I tried some other approaches like:
https://github.com/codejamninja/lb4-authorization
but they are not using @loopback/authentication, which I would like to be compliant with, and had some other issues with those.

@pilare sorry for the late reply, looking, will get back to asap today.

@deepakrkris (Thank you!) is working a fix in https://github.com/strongloop/loopback-next/issues/4902, I think it's the same issue.

@deepakrkris, with PR #4919 landed, anything we need to do for this task? Thanks.

@dhmlau the PR https://github.com/strongloop/loopback-next/pull/4899 will complete the pending items in this task.

following pending items in this issue will be completed by https://github.com/strongloop/loopback-next/pull/4899

  • Create a static login page that allows user to choose from local login or 3rd-party login. Keep the UI as simple as possible
  • Track the user profile using session. Keep the session related functionalities as simple as possible, we can create sub-stories to enhance.
  • Create an example endpoint that verifies user using session.
  • Update the md file docs/strategies/oauth2.md
Was this page helpful?
0 / 5 - 0 ratings

Related issues

teambitcodeGIT picture teambitcodeGIT  路  3Comments

rexliu0715 picture rexliu0715  路  3Comments

acrodrig picture acrodrig  路  3Comments

teambitcodeGIT picture teambitcodeGIT  路  3Comments

mhdawson picture mhdawson  路  3Comments