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

Authentication and Authorization extension points #1035

Open
dhmlau opened this Issue Feb 22, 2018 · 8 comments

Comments

Projects
None yet
6 participants
@dhmlau
Copy link
Contributor

dhmlau commented Feb 22, 2018

Per discussion with @raymondfeng @kjdelisle:

Need extension point implementation and extension interfaces for authentication and authorization

User Experience

  • For a given controller method, we should be able to use decorators to express the authorization requirement. e.g. we allow / deny certain roles.

  • need to have the ability to decorate the methods to indicate the scope of the resource access. i.e. group the methods for access. e.g. write access for Order.

    • describe your protection scope for the method
  • example: when cancelling order, only the customer and customer rep can cancel.

  • can possibly use json / yaml files to define the access rules, besides using decorators.

  • Need infrastructure to help us to make the yes/no decision - decision could be no-go, consult external provider (e.g. enterprise might have a centralized authorization management system). This can be expressed in extension points to plug in other authorization providers.

  • from admin perspective, we need a way to specify the role mapping. i.e. which user belongs to which role. -- 1) static mapping table. 2) some role has to be dynamically determined per request, e.g. cancelling order. need to figure out for the given orderid, it matches with the customerid to place the order.

  • authorization has the dependencies on authentication, because we need to know who is making the call. Two pieces of identity 1) the user itself, 2) application id making the request <--- think FB app.

  • also look at what we have in LB3 for functional parity

  • we also allow developers to write code to override our authorization decisions (added business logics in the controller)

Questions to answer

Timeboxed max to 2 weeks

  • mockup decorators to show what metadata is required for controller/method
  • need to understand the interface for authorization provider
  • need to understand the roles (for grouping users)/scope (for grouping resources)
  • what customization we allow developers to do at the controller level
  • need to understand what json/yaml externalization for access rules and resource definitions and other metadata
  • need to understand what tooling to provide to harness the authorization requirement
  • identify the dependencies to the authentication component
@bajtos

This comment has been minimized.

Copy link
Member

bajtos commented Oct 15, 2018

I find the scope of this epic rather overwhelming and would not know where to start. I am proposing to define scenarios based on our Shopping example app and split the big auth+auth story into smaller incremental chunks that are more manageable. Here is my take on the scenarios.

  1. Refactoring: Customer credentials
  2. Groundwork: Access tokens, login & logout
  3. The First Scenario: Authenticated orders (a minimal authentication)
  4. The Second Iteration: Cancelling orders (a minimal scope-based authorization)
  5. Next: Extension Points

@raymondfeng @strongloop/loopback-next thoughts?

Refactoring: Customer credentials #1996

Let's keep things simple for now and use a password as the only credential. (No 2-factor-auth, no OAuth/OpenID/SAML for now.)

In LoopBack 3.x, we are storing storing user's password together with user data in the same model (table), which opens a lot of security vulnerabilities to deal with. For example:

  • When returning user data in HTTP response, the password property must be filtered out.
  • When searching for users, the password must be excluded from the query.
  • It is not possible to do a full replace of user data from a REST client (e.g. "save()") because a) the client usually does not know the password b) password must be changed using a different flow.
  • A policy preventing users from reusing the same password is difficult to implement because old password are not preserved.

For LB4, I would like us to explore the design where passwords are stored in their own table and a relation "user has one password" is configured (#1422 is tracking HasOne relation). We can also use "user has many password" and include a flag (a Password model property) to distinguish between the current active password and the passwords used in the past.

The current version of the Shopping Server is already storing the password in the User model. Let's move it out to a dedicated model as part of this preparation work.

Groundwork: Access tokens, login & logout #1997

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).

The First Scenario: Authenticated orders (a minimal authentication) #1998

Rework UserOrderController to obtain the customer id from the request (the access token provided by the client) instead of a URL-path parameter.

For example, we can remove /users/{userId} prefix from all endpoints to end up with the following API:

  • POST /orders creates a new order
  • GET /orders returns all orders of the current user
  • PATCH /orders?where= to update some of the orders of the current user. I think this endpoint does not make sense in a Shopping app and should be eventually removed.
  • DELETE /orders?where= to delete some of the orders of the current user. I think this endpoint should be eventually removed, because orders are never deleted, they can be only closed (e.g. as cancelled).

Existing REST API exposed by UserController should remain unauthenticated (allowing anonymous access).

The goal of this story is to leverage @loopback/authentication to implement this feature and improve @loopback/authentication along the way as needed.

The Second Iteration: Cancelling orders (a minimal scope-based authorization) #1999

Let's add a new Order property called state with the following values: new (set for all newly created orders), delivered, canceled. Implement a new REST API for cancelling orders: POST /orders/{orderId}/cancel.

Let's define two kinds of users in our Shopping app:

  • customers can see and cancel their own orders only (scope: orders:my)
  • admins can see and cancel all orders in the system (scope: orders:all)

As part of this iteration, we need to figure out how to implement scopes, how to decorate controller methods like cancel to enforce a certain scope, but also how to push the access restriction down to repository level, so that a request to list all orders returns only orders accessible to the current user.

Next: Extension Points #2000

Armed with a small application having authentication and authorization constraints in place, let's look at extensibility and what extension points we may need to define to support those use cases.

Few example scenarios to drive our focus:

  • Allow customers to register and login using their Google account.
  • Implement multi-tenancy - group users and admins into realms. Users (customers and admins) from one realm cannot see data from other realms. A superuser can see data from all realms.
  • Simplify management of scopes by introducing Roles (a user belongs to a single role, a role grants many scopes).
  • Implement an OAuth provider for 3rd party client apps. Let the customer decide which scopes they are willing to grant to each app (an access token used by an app).
  • Add support for 2-factor authentication based on TOTP (Time-based One-time Password), using a mobile phone app like Authy or Google Authenticator as the second factor.

Ideally, for each scenario we look into, 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.

@dhmlau

This comment has been minimized.

Copy link
Contributor Author

dhmlau commented Nov 13, 2018

After discussing with @bajtos and @raymondfeng, we're going to implement our own dummy authentication provider for the purpose of this epic.

What changes needed in our shopping app example

  1. Remove password in User model. This will be handled in #1996.

  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).  This will be handled in #1997.

  • Authentication provider has the following APIs:
    • login: input: userid+password.  output: Object that contains "yes/no", JWT access token, and user info. 
    • verify: accept access token. and tell you whether the token is valid plus user info.  (will be covered in the next task #1998)
    • logout: invalidate the access token
  1. User experience: go to API Explorer, login and set the token so that API Explorer can use the token for subsequent request. #2027 is created.

  2. Implement the "verify" api in the authentication provider. Will be handled in #1998. See @loopback/authentication as inspiration

    1. custom sequence action - authenticate.  This will verify the access token and bind the security context in the context. 
    2. well-known binding key for DI for the security context.
      After this task is done, we should be able to support these APIs:
      POST /orders creates a new order
      GET /orders returns all orders of the current user
  3. For the next task #1999, we can probably consider it as stretch goal. Will need more discussion for clarification.

@jgwinner

This comment has been minimized.

Copy link

jgwinner commented Jan 20, 2019

Just jumping on as a desired feature!

Yes, I know about 'subscribe' (and did so) but wanted to bump the thread :)

== John ==

@bajtos

This comment has been minimized.

Copy link
Member

bajtos commented Jan 25, 2019

wanted to bump the thread

Please use "thumbs up" vote a the top (in the issue description) in the future. That way we can sort issues by the number of upvotes.

@jannyHou jannyHou referenced this issue Jan 29, 2019

Merged

feat: jwt auth #26

@dhmlau dhmlau referenced this issue Jan 30, 2019

Closed

Monthly Milestone - February 2019 💝⛄️ #2313

14 of 37 tasks complete
@dhmlau

This comment has been minimized.

Copy link
Contributor Author

dhmlau commented Feb 19, 2019

There had been a few discussions around this epic among @raymondfeng @bajtos @jannyHou @emonddr. Hopefully I can capture the summary here. Please correct me if I'm wrong or miss anything!

What we've done (a very high level)

We've added the PoC to show how to add JWT authentication in the example-shopping app.

What's outstanding in the shopping app (post Q1)

  • The First Scenario: Authenticated orders (a minimal authentication), #1998
    • Eventually we want to remove /users/{userid} in the endpoints so that we have POST /orders, GET /orders, with the current user injected into the controller and becomes the property of the controller.
  • Make UserProfile more flexible: #2246
    • Currently we have name + email as the user profile info, but we may have first name / last name.
    • We'll need to identify the properties required by other authentication/authorization infrastructure. Can look at OpenID to see what's the minimal required properties https://openid.net/specs/openid-connect-core-1_0.html#UserInfo.
    • Need to put the UserProfile back to @loopback/authentication package so that others can start to plug in. We should also have a default authentication sequence action.
  • Add logout function to invalidate the accesstoken strongloop/loopback4-example-shopping#36
    • there is no good way to invalidate the token. It might be a lower priority for us at this point than to enable the extension points (see below).

Next steps

The next steps are to:

  • refactor some of the code in the shopping example app back to @loopback/authentication
  • create extension points to plug in different behaviors
  • rebase shopping example against those extension points

screen shot 2019-02-19 at 3 32 15 pm
Note: Some of the details might need to be updated

  • Step 1a: create extension point for access token generator in @loopback/authentication, define the key as the constant and interface
    • in shopping example, it will implement this access token generator
  • Step 1b: create interface for strategy and then the extension point for strategies -- needs Raymond's PR
  • Step 1c: Add common action to authenticate inside authentication module. Make sure passport based strategies and non-passport based strategies all work with the new abstractions. 
  • ** Step 1d**: Move the credential extractor code from the example app back to the authentication package. out of scope of Q1
  • Step 2 : rebase shopping example against the @loopback/authentication
  • Step 3: Blog/tutorial: using loopback4-example-shopping and passport-jwt as two example flows to guide users how to contribute strategies and make them work.

Note: Step 1a-d can happen in parallel.

@jannyHou

This comment has been minimized.

Copy link
Contributor

jannyHou commented Feb 19, 2019

@dhmlau Thank you for the summary.

@raymondfeng Regarding the authentication strategy part in the picture above(4,6,C), the shopping example app has C in the authentication strategy class, see 'JWTStrategy', while has 4 and 6 as services, see JWTAuthenticationService.

By extracting them into interfaces, do we want to still keep 6 and 4 as services or merging them into the strategy class?

@raymondfeng

This comment has been minimized.

Copy link
Member

raymondfeng commented Feb 19, 2019

We make a decision based on whether such functions represent the related resonsibity of a strategy. If yes, it make sense to define an interface to group these methods and have classes to implement them. Please note that a class can use DI to conpose with other services and offer a facade.

@jannyHou

This comment has been minimized.

Copy link
Contributor

jannyHou commented Feb 21, 2019

Story created for 1a-1c:
#2435
#2491
#2466
#2467

@jannyHou jannyHou referenced this issue Feb 25, 2019

Open

Add abstraction for authentication strategy #2466

0 of 3 tasks complete

@dhmlau dhmlau referenced this issue Mar 1, 2019

Open

Monthly Milestone - March 2019 ☀️🚗💐 #2516

9 of 34 tasks complete
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.