In this project, you will add authentication and authorization to the persistent marketplace. You will explore how a separate login service issues JSON Web Tokens (JWTs), how the marketplace verifies them, and how one shared wrapper can protect several REST routes.
Clone the repository https://github.com/wngjia/ece573-prj03.git that holds scripts and source code for Project 3. Similar to Project 1 and 2, please create a new VM by importing our VM Appliance, make a clone of the repository (or fork it first), and provision the instance with the script setup_vm.sh.
It is optional to incorporate your completed Project 2 into Project 3 in order to support all functionalities. If you have completed Project 2, you may copy your completed versions into Project 3, assuming you have cloned your Project 2 into your home directory like the Project 3:
ubuntu@ubuntu24:~/ece573-prj03$ cp ../ece573-prj02/marketplace-service/marketplace/accounts.go marketplace-service/marketplace/accounts.go ubuntu@ubuntu24:~/ece573-prj03$ cp ../ece573-prj02/marketplace-service/marketplace/orders.go marketplace-service/marketplace/orders.goAccording to the architecture of our services, these two files should not be modified in Project 3 otherwise.
Briefly review our README.md, either in its textual form, or directly on GitHub as a web page. Execute the script run_baseline.sh from the project directory to run the starter baseline:
ubuntu@ubuntu24:~/ece573-prj03$ ./run_baseline.sh ... Starter baseline completed. Responses are saved in out/. The Project 2 account assembly and credit check remain TODOs. The services are stopped; the Postgres volume and signing keys are preserved.We organize the scripts in Project 3 in a similar way as Project 2. The baseline uses these scripts to reset the database and signing key, start Postgres and two Go services, register two accounts, grants credit, list and order an item, and finally restart the marketplace service to make sure the order succeeds.
Open 'docker-compose.yml' to understand the architecture of our marketplace with added support of authentication and authorization. It defines three services: postgres and marketplace-service similar to that of Project 2, and a login service to authenticate users. The two Go services are built from separate targets in the root Dockerfile so that both can use the shared 'auth' package, which takes care of handling JWTs.
Read the source files for the login service. The login service owns registration, password verification, and JWT issuance. Registration creates an account with zero credits and a regular-user role. The supplied helpers hash passwords with Argon2id and sign JWTs using an Ed25519 private key. It creates 'course-admin' on startup if the account doesn't exist and updates the password based on 'secrets/admin-password', which may be regenerated randomly by the reset script. Then, the login service will serve two RESTful endpoints, POST /accounts for registration and POST /login that issues a JWT Bearer token upon successful login. Follow-up RESTful requests should include the JWT to identify the user.
Since we haven't implemented authorizations in the marketplace service yet, run_baseline.sh sets the environment variable AUTH_ENABLED=false so that the first run could proceed without triggering the unfinished authorizations. Later on, to experiment with authorizations, set AUTH_ENABLED=true when starting the services:
ubuntu@ubuntu24:~/ece573-prj03$ ./scripts/reset.sh
...
Signing keys and admin password rotated.
Services are stopped. Run ./scripts/start.sh to use the new secrets.
Database reset. Admin password is in secrets/admin-password.
ubuntu@ubuntu24:~/ece573-prj03$ AUTH_ENABLED=true ./scripts/start.sh
...
Marketplace service is ready at http://127.0.0.1:8080.
Login service is ready at http://127.0.0.1:8081.
ubuntu@ubuntu24:~/ece573-prj03$ ./scripts/create-account.sh Alice
Password:
{"account_id":"Alice","balance":0,"role":"user"}
ubuntu@ubuntu24:~/ece573-prj03$ ./scripts/login.sh Alice
Password:
{"access_token":"*hidden*","expires_in":1800,"token_type":"Bearer"}
Registration and login scripts prompt for a password.
The login script prints the issued JWT token to stdout.
Make sure that passwords, JWTs, and private keys
should not appear in your report screenshots or ordinary service logs.
Read README.md
for examples using the Marketplace scripts with the JWT token.
The marketplace service retains four RESTful endpoints from Project 2, including GET /listings, POST /listings, POST /orders, and GET /accounts/{account_id}, and also adds POST /accounts/{account_id}/credits. While anyone can use GET /listings, only specific users can access the other four when AUTH_ENABLED=true. In short, only regular users may list or buy, users may only view their own accounts, and only the admin may grant positive credits to regular users.
Read 'marketplace-service/main.go' first to see the route table. It includes all routes when AUTH_ENABLED=false, and one route for AUTH_ENABLED=true as the example. A 'TODO Project 3' comment indicates that you will need to complete the other three routes requiring authorizations based on the example. Without knowing the detail of the authorization wrapper 'requireAuthorization', please make reasonable guess on its parameters and then complete this TODO item.
Now we are ready to review 'marketplace-service/authorization.go' to understand and implement the authorization wrapper 'requireAuthorization'. This function uses the decorator pattern as we have introduced in the lectures to add authorization to any RESTful handler function ('http.HandlerFunc'). The supplied code extracts the JWT token from request header and verifies it. The 'TODO Project 3' requires you to write a few lines of code to check whether the token's subject and role are permitted using the filters 'matchesID' and 'allowsRole', before proceeding to the requested marketplace operation 'next'. Make sure to generate specific error response for failures: return HTTP 400 ('http.StatusBadRequest') if the filter reports an invalid identity field, and return HTTP 403 ('http.StatusForbidden') if the operation is not permitted.
After you have completed both Project 3 TODOs, run acceptance.sh and attack.sh to verify the correctness.
ubuntu@ubuntu24:~/ece573-prj03$ ./acceptance.sh Project 3 acceptance test passed. Responses are saved in out/. Services are stopped; the Postgres volume and signing keys are preserved. ubuntu@ubuntu24:~/ece573-prj03$ ./attack.sh Project 3 attack test passed. Responses are saved in out/attack/. Services are stopped; the Postgres volume and signing keys are preserved.The acceptance script checks successful registration, an admin credit grant, listing, public browse, and checkout. The attack script numbers its ten rejection tests:
Prepare a project report in .doc/.docx or .pdf format and submit it to Canvas before the deadline. Your project report should include the following for a total of 15 points: