Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Groundwork: Access tokens, login & logout #1997

Closed
nabdelgadir opened this issue Nov 8, 2018 · 9 comments
Closed

Groundwork: Access tokens, login & logout #1997

nabdelgadir opened this issue Nov 8, 2018 · 9 comments

Comments

@nabdelgadir
Copy link
Contributor

nabdelgadir commented Nov 8, 2018

Description

Step 2 from #1035 (comment).

Define a new AccessToken model and a relation to Customer (a customer has many tokens, a token belongs to a customer).

Implement login and logout, expose them via REST API. Let's create a new Controller class for these endpoints!

The goal of this step is to show a reference implementation in our Shopping App and prepare ground for the actual auth-related work in the next iterations. Later, we may decide to extract AccessToken & Password models together with the relevant repositories and Controller classes into a shareable LB4 component (e.g. @loopback/local-credentials).

Ideally, there should be a documentation and/or a blog-post and/or a reference implementation to make it easier for LB4 users to implement similar functionality in their project.

Previous step: #1996
Next step: #1998

@David-Mulder
Copy link
Contributor

Is the idea here that tokens are stored in the database or to use JWTs?

@dhmlau
Copy link
Member

dhmlau commented Nov 13, 2018

@David-Mulder , we're planning to implement a simple authentication provider that uses JWT access token under the hood.

@dhmlau
Copy link
Member

dhmlau commented Nov 13, 2018

Extracted from the discussion notes captured in #1035 (comment):

Acceptance Criteria

  • Authentication package needs to be refactored to extract the minimal contract for authentication providers
  • Implement simple authentication provider that users JWT access token under the hood, and contain hardcoded list of user credentials (could be in-memory in the shopping card example). It has 2 APIs:
    • login: input: userid+password. output: Object that contains "yes/no", JWT access token, and user info.
    • logout: invalidate the access token
  • Modify the shopping app accordingly to make use of it
  • docs/blog post, if applicable

@dhmlau dhmlau mentioned this issue Nov 20, 2018
20 tasks
@bajtos
Copy link
Member

bajtos commented Nov 23, 2018

  1. Authentication package needs to be refactored to extract the minimal contract for authentication providers
  2. Implement simple authentication provider that users JWT access token under the hood, and contain hardcoded list of user credentials (could be in-memory in the shopping card example).
  3. Modify the shopping app accordingly to make use of it

I am little bit concerned about practical aspects of this approach. My original vision was to start from outside (the example app) and drive the internal design & implementation based on what we need to provide to API consumers.

I fear that in the current proposal, we will start implementation inside @loopback/authentication before we fully understand what is required to make authentication easy to use. Developing features in two repositories (example-shopping & loopback-next) is usually cumbersome, as it's difficult to switch from end-to-end/acceptance-level tests in the example app to lower level integration/unit tests in the authentication component. As a result, there may be a lot of going forth and back between the example app & loopback-next repositories while we iterate on the design.

Personally, I would use a different plan:

  • Start the work in the shopping app. Implement any authentication-related interfaces and classes inside the example app (for example auth provider with login/logout/verify methods), this will make it easier to iterate on the design & implementation. Focus on the user experience, what is needed to make authentication easy to add to LB4 applications.
  • Open a (spike?) pull request where we can discuss the proposed design & implementation.
  • After we all gain a better understanding of the problem domain and agree on the high-level design & implementation, take the relevant bits from the spike PR and move them to loopback-next - open a new PR to modify @loopback/authentication component.

Anyhow, just my two cents.

@clayrisser
Copy link
Contributor

@bajtos, couldn't the shopping-app be added to the examples and then the authentication could be developed off a feature branch?

@bajtos
Copy link
Member

bajtos commented Dec 4, 2018

We are intentionally keeping the shopping example outside of our monorepo. Running tests again multiple databases is expensive, we don't want to slow down the build process for loopback-next monorepo.

@jannyHou
Copy link
Contributor

jannyHou commented Jan 2, 2019

A diagram shows metadata injection dependent relations among different layers:

screen shot 2019-01-02 at 4 57 35 pm

The background of this diagram will be explained in the next comment.

@jannyHou
Copy link
Contributor

jannyHou commented Jan 3, 2019

A summary of the progress in PR loopbackio/loopback4-example-shopping#26

It turns out adding a new authentication strategy in the example repository is a wise decision.
We did come across some confusions and twisted concepts when adding the JWT strategy, and had a chat with Raymond and drew the diagram above to explain each part's responsibility and dependent relations between each other.

In PR#26 we created the following stuff:

  • A strategy resolver that returns a JWT strategy instance if a controller method is decorated with @authenticate('jwt')
    • It is bound to AuthenticationBindings.STRATEGY
    • IMPROVE(open a new story for it): turn it to an extension point that user can register custom strategies easily. We could take similar approach as register the custom body parser
  • A JWT strategy class that verifies a user is an authenticated one or not.
    • This should eventually be moved to either a standalone extension or @loopback/authentication
  • An AccessToken model that stores the accesstoken
    • User hasOne AccessToken
  • A sign in operation in the user controller that finds a user by provided credentials, if found, generates the accesstoken returned by jwt.sign()
    • BLOCKER: do we store the user or the accesstoken in app? see the discussion 🔽

Discussion:

  • Store the user

    • I found a UserProfile type and its corresponding authentication binding key in @loopback/authenticate, my understanding is after a user login, we bind its corresponding user profile to AuthenticationBindings.CURRENT_USER, and retrieve that user's accesstoken by calling this.userRepository.accesstoken(userProfile.id), then calls jwt.verify(accesstoken).
    • This means we make a backend call per request to retrieve the accesstoken which is too expensive IMO.
  • Store the accesstoken

    • Given my concern above, another solution is binding the accesstoken to a key.
    • Then inject the accesstoken per request.
    • And then the question would be do we need still an AccessToken model?

cc @strongloop/loopback-next thought?

@jannyHou
Copy link
Contributor

When write the tutorial I feel it's better to have the blog when we finish story #2311, at least people don't need to hardcode the strategy resolver to plugin their strategies.
I can summary what we learned and achieved from this story in the milestone blog.
So I am closing it.

See follow-up stories:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants