This project aims to represent an online ecommerce platform APIs to validate / test the LoopBack 4 framework readiness for GA. See loopbackio/loopback-next#1476 for more information.
Make sure you have Node.js >= 8.9.0 installed. Then do the following to clone and start the project.
git clone https://github.com/strongloop/loopback4-example-shopping.git
cd loopback4-example-shopping
npm i
npm start
The main app will be running at http://[::1]:3000
.
You will also see Recommendation server is running at http://127.0.0.1:3001.
,
it is the server to which the services/recommender.service
service will
connect to get the recommendations for a user.
This app is intended to be interacted with using the API Explorer located at http://[::1]:3000/explorer/.
This app has five models:
User
- representing the users of the system.Product
- a model which is mapped to a remote service byservices/recommender.service
.ShoppingCartItem
- a model for representing purchases.ShoppingCart
- a model to represent a user's shopping cart, can contain many items (items
) of the typeShoppingCartItem
.Order
- a model to represent an order by user, can have many products (products
) of the typeShoppingCartItem
.
ShoppingCart
and Order
are marked as belonging to the User
model by the
use of the @belongsTo
model decorator. Correspondingly, the User
model is
marked as having many Order
s using the @hasMany
model decorator. Although
possible, a hasMany
relation for User
to ShoppingCart
has not be created
in this particular app to limit the scope of the example.
Controllers expose API endpoints for interacting with the models and more.
In this app, there are four controllers:
ping
- a simple controller to checking the status of the app.user
- controller for creating user, fetching user info, updating user info, and logging in.shopping-cart
- controller for creating, updating, deleting shopping carts, and getting the details about a shopping cart.user-order
- controller for creating, updating, deleting orders, and getting the details about an order.
Services are modular components that can be plugged into a LoopBack application in various locations to contribute additional capabilities and features to the application.
This app has five services:
services/recommender.service
- responsible for connecting to a "remote" server and getting recommendations for a user. The API endpoint atGET /users/{userId}/recommend
, is made possible by this service.services/user-service
- responsible for verifying if user exists and the submitted password matches that of the existing user.services/hash.password.bcryptjs
- responsible for generating and comparing password hashes.services/validator
- responsible for validating email and password when a new user is created.services/jwt-service
- responsible for generating and verifying JSON Web Token.
The endpoint for logging in a user is a POST
request to /users/login
.
Once the credentials are extracted, the logging-in implementation at the
controller level is just a four step process. This level of simplicity is made
possible by the use of the UserService
service provided by
@loopback/authentication
.
const user = await this.userService.verifyCredentials(credentials)
- verify the credentials.const userProfile = this.userService.convertToUserProfile(user)
- generate user profile object.const token = await this.jwtService.generateToken(userProfile)
- generate JWT based on the user profile object.return {token}
- send the JWT.
You can see the details in
packages/shopping/src/controllers/user.controller.ts
.
swagger-ui
module is built with authorization component, which will show up by
adding the security schema and operation security spec in the OpenAPI spec.
You can check the OpenAPI doc to learn how to add it, see section "Describing Bearer Authentication".
As a demo, the security related specs are hardcoded and merged into the application's OpenAPI spec in the main file in this spike.
Should be moved to https://loopback.io/doc/en/lb4/Authentication-Tutorial.html#try-it-out
After creating a user, you can login with email
and password
:
A JWT token will be generated and returned in the response body, you can copy the token for setting the bearer header in the next step:
Then set the token for every request's Bearer header. You will find a green button called "Authorize" on the right corner of the explorer:
Click it and the token set dialog will be prompted:
Paste the token you just copied in the field, then click "Authorize". The token will be hidden:
Now you can try endpoint like GET/users/me
to verify that the logged in user
is injected in the request:
You can also specify security policy for a single endpoint, by adding the security spec in the operation decorator, like:
// Define your operation level security policy
const SECURITY_SPEC_OPERATION = {
[{basicAuth: []}];
}
// Also add the corresponding security schema into `components.securitySchemas`
const SECURITY_SCHEMA_SPEC = {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
},
basicAuth: {
type: 'http',
scheme: 'basic',
},
},
};
// Add security policy in the operation decorator
@get('/users/{userId}', {
security: SECURITY_SPEC_OPERATION,
responses: RESPONSE_SPEC,
})
async findById(@param.path.string('userId') userId: string): Promise<User> {
// Print out the header, you will see the Basic auth string in the header
console.log(`header ${inspect(this.request.headers)}`);
// business logic
}
The "Authorize" dialog will show up your new added entry as:
Provide the username and password to login:
Try endpoint GET users/{userId}
, the printed header contains the basic auth
string:
As you could find in the security-spec.ts
file, security related spec is
hardcoded now and is manually merged into the openapi spec in the main file
index.ts
, to enable the token set more automatically, there are 3 things we
could improve:
- The security schema could be contributed by the authentication strategy, see story#3669
- This spike sets a global security policy for all the endpoints, while the
policy spec can be set on the operation level, it's also documented in
swagger/authentication/bearer-authentication.
This can be achieved by using
@api()
(controller class level) or@authenticate()
(controller method level)
There is a tutorial which shows how to apply the JWT strategy to secure your
endpoint with @loopback/[email protected]
. You can check more details in
https://loopback.io/doc/en/lb4/Authentication-Tutorial.html
Please check the try it out section in the tutorial.
This project uses DCO. Be sure to sign off
your commits using the -s
flag or adding Signed-off-By: Name<Email>
in the
commit message.
Example
git commit -s -m "feat: my commit message"
Other LoopBack 4 Guidelines apply. See the following resources to get you started:
See all contributors.