The problem a B2B inventory API actually has
A supply-chain backend is not a toy CRUD app. Procurement, production, and delivery all share stock, orders, and partners. If authentication is weak, one leaked session can touch the whole chain.
On my B2B inventory project, I built a Spring Boot API with a clear MVC split, then locked the critical REST flows with Spring Security and JWT.
The goal was simple: every request that changes stock or a delivery must prove who is calling, without turning the domain layer into a security maze.
MVC first, then security
I keep three layers boring on purpose:
- Controllers map HTTP to commands. They do not calculate stock.
- Services own procurement, production, and delivery rules.
- Repositories talk to the database and nothing else.
That split makes JWT easier to reason about. Security filters sit in front of controllers. Domain code never parses a token.
What JWT does in this stack
After login, the API returns a signed token. Later requests send Authorization: Bearer …. Spring Security validates the signature, expiry, and roles, then puts the principal on the request.
I treat JWT as a passport, not as a place to dump the whole user profile. Keep claims small: subject, roles, expiry. If a partner needs extra data, load it from the database after the token is trusted.
Protecting the flows that matter
Not every GET needs the same pressure. Read-only catalogues can stay lighter. The routes that create purchase orders, move production lots, or confirm delivery must fail closed.
A practical checklist I used:
- Require authentication on mutating endpoints.
- Map roles to resources (ops vs admin vs partner).
- Reject expired or malformed tokens with a consistent error body.
- Never log raw tokens.
- Keep CORS explicit if a web client calls the API.
If a filter is wrong, tests should catch it before Docker ever builds. That is why this API sits behind a quality gate in GitHub Actions, which I cover in the CI/CD post.
Testing security is part of the feature
Spring Security is easy to “add” and hard to prove. I cover:
- Unauthenticated calls to protected routes return 401.
- A valid JWT with the wrong role returns 403.
- A valid admin token can complete a delivery update.
JUnit 5, Mockito, and TestContainers keep that honest. Unit tests mock the JWT decoder for speed. Integration tests hit a real database in Docker so MVC wiring is not imaginary.
You can see the testing setup in Spring Boot testing with TestContainers and SonarQube.
What I would repeat on the next SCM API
- Design the domain first. Security should wrap a clear model.
- Put JWT at the edge. Do not leak claims into entities.
- Automate the quality gate so a broken filter cannot ship.
If you are hiring for Java or Spring Boot work, or you need a similar backend, get in touch.
Frequently Asked Questions
Why JWT instead of session cookies for a B2B API?
A supply-chain backend is consumed by several clients. JWT keeps authentication stateless, so each service can validate tokens without sharing server sessions.
Where should Spring Security sit in an MVC backend?
Keep security at the HTTP boundary. Controllers stay thin, services own business rules, and repositories never see raw credentials.
How do you protect procurement and delivery endpoints?
Map roles to resources, require a valid JWT on every critical route, and cover those filters with tests before the CI quality gate.
Related articles
Testing Spring Boot with TestContainers and Sonar
The QA chain I use on Spring Boot backends: JUnit 5, Mockito, TestContainers, JaCoCo coverage, and a SonarQube quality gate.
GitHub Actions, Docker, and a real quality gate
How I wired GitHub Actions, Docker Compose, and SonarQube so a Spring Boot image only ships after the quality gate passes.