spring-angular-ecommerce-frontend
8/9/2021 There is some backend issue with Spring and the Web Security Configuration
- I am getting a 404 error when making a purchase, it’s acting like the api/checkout/purchase endpoint isn’t there when its clearly defined in the CheckoutController
- Could be CORS or CSRF related but nothing seems to work
- Could be Okta related, I’ve checked Trusted Origin could be a missing backslash “/”
This is the error I’m getting in browser console
General:
- Request URL: https://springboot-angular-ecommerce.herokuapp.com/api/checkout/purchase
- Request Method: POST
- Status Code: 404
- Referrer Policy: strict-origin-when-cross-origin
Response Headers:
- Access-Control-Allow-Credentials: true
- Access-Control-Allow-Headers: Content-Type, Accept, X-Requested-With, remember-me
- Access-Control-Allow-Methods: GET, OPTIONS
- Access-Control-Allow-Origin: https://spring-angular-ecommerce-front.herokuapp.com
- Access-Control-Max-Age: 3600
- Connection: keep-alive
- Content-Type: application/json
- Date: Tue, 10 Aug 2021 12:58:19 GMT
- Server: Cowboy
- Transfer-Encoding: chunked
- Vary: Origin
- Vary: Access-Control-Request-Method
- Vary: Access-Control-Request-Headers
- Via: 1.1 vegur
Request Headers:
- Accept: application/json, text/plain, /
- Accept-Encoding: gzip, deflate, br
- Accept-Language: en-US,en;q=0.9
- Connection: keep-alive
- Content-Length: 604
- Content-Type: application/json
- Host: springboot-angular-ecommerce.herokuapp.com
- Origin: https://spring-angular-ecommerce-front.herokuapp.com
- Referer: https://spring-angular-ecommerce-front.herokuapp.com/
- sec-ch-ua: “Chromium”;v=”92″, ” Not A;Brand”;v=”99″, “Google Chrome”;v=”92″
- sec-ch-ua-mobile: ?0
- Sec-Fetch-Dest: empty
- Sec-Fetch-Mode: cors
- Sec-Fetch-Site: cross-site
Request Payload: (JSON)
- {customer: {firstName: “asdf”, lastName: “asdf”, email: “asdf@asdf.com”},…}
- billingAddress: {street: “asdf”, city: “asdf”, state: “Andhra Pradesh”, zipCode: “asdf”, country: “India”}
- customer: {firstName: “asdf”, lastName: “asdf”, email: “asdf@asdf.com”}
- order: {totalPrice: 37.98, totalQuantity: 2}
- orderItems: [{imageUrl: “assets/images/products/coffeemugs/coffeemug-luv2code-1000.png”, quantity: 1,…},…]
- shippingAddress: {street: “asdf”, city: “asdf”, state: “Andhra Pradesh”, zipCode: “asdf”, country: “India”}
SpringBoot Angular e-Commerce App
- This project was built based on a course by Chad Darby
- This page is the Angular front end.
All of the page sections are Angular components that are populated with data (Product, Product Category, etc) from a MySQL database through Spring Data REST JPA calls.
My additions are responsive product display (CSS Grid), responsive mobile view and development, custom icons, navigation and style as well as deployment to heroku and SQL database setup.
The course and deployment were a lot of fun. I look forward to creating more Angular/React + SpringBoot applications.
This project was generated with Angular CLI version 10.0.6.
Development server
Run ng serve
for a dev server. Navigate to http://localhost:4200/
. The app will automatically reload if you change any of the source files.
8/1/2021 Update
Table of Contents
- Problem – Handling Stored Data and Browser Refresh
- Okta Integration
- Form Validation
- Save the Order
- Order History
- Secure Order History
- Secure Communication
- Publishing events – messages
Problem – Handling Stored Data and Browser Refresh
-
Possible solutions
- Keep track of cart products using client side browser web storage
-
HTML5 introduced Web Storage API
- store data in the browser using key/value pairs
Two types of Web Storage
-
Session Storage
-
Data is stored in web browser’s memory
-
Browser session means Web browser client-side memory
- Nothing is sent to the server
- Nothing to do with Backend HttpSession
-
Browser session means Web browser client-side memory
<li> Each web browser tab has it’s own “session” <ul dir="auto"> <li> Data is not shared between web browser tabs </li> <li> Once a web browser tab is closed then that data is forgotten and no longer available </li> </ul> </li> </ul>
-
Data is stored in web browser’s memory
-
Local Storage
-
Data is stored on the client side computer
- Data is never sent to the server
<li> Data is available to tabs of the same web browser for same origin – called <strong>Data Scoping</strong> <ul dir="auto"> <li> i.e. – data from a browser tab open to cnn.com cannot read data from another tab open to apple.com </li> <li> Likewise browsers cannot access local storage data of another browser (Firefox cannot access data in Chrome) </li> <li> App must read data again … normally with a browser refresh </li> <li> Data persists even if web browser is closed </li> <li> Can clear the data using JavaScript or clearing web browser cache </li> </ul> </li> </ul>
-
Data is stored on the client side computer
-
Data Scoping
-
Data is scoped to a given origin
- Origin: protocol + hostname + port
- Same origin: http://localhost:4200 == http://localhost:4200
- Different origin: http://localhost:4200 != http://localhost:8080
-
Data is scoped to a given origin
-
Factors to consider
- Data in web storage is stored in plain text … NOT encrypted
- Do not use it to store any sensitive info such as personal info or credit card info etc.
- Be aware that power users may tinker with files
-
You app should be resilient to still work if storage is not available
- the user may clear their browser cache etc
- Your app should use reasonable defaults
Web Storage API
-
The API works the same for Session Storage and Local Storage
- Based on key value pairs
- Keys and values are always strings
-
Remember data is scoped to page origin: protocol + hostname + port
- Store items using: storage.setItem(key, value)
- Retrieve items using: storage.getItem(key)
- Remove items using: storage.removeItem(key)
- Clear all stored data using: storage.clear()
Development Process – Angular
-
Update CartService to read data from Session storage
File: cart.service.tsexport class CartService { cartItems: CartItem[] = []; ...
storage : Storage = sessionStorage; // reference to web browser's session storage constructor() { // read data from storage let data = JSON.parse(this.storage.getItem('cartItems')); if (data != null) { this.cartItems = data; // compute totals based on the data that is read from storage this.computeCartTotals(); } }
}
-
Add new method in CartService: persistCartItems()
File: cart.service.tsexport class CartService { cartItems: cartItem[] = []; ...
storage : Storage = sessionStorage; persistCartItems() { this.storage.setItem('cartItems', JSON.stringify(this.cartItems)); }
-
Modify computeCartTotals() to call new method: persistCartItems()
File: cart.service.tsexport class CartService { cartItems: cartItem[] = []; ...
storage : Storage = sessionStorage; persistCartItems() { this.storage.setItem('cartItems', JSON.stringify(this.cartItems)); } computeCartTotals() { ... ... // persist cart data this.persistCartItems(); }
Okta Integration
For more background info on Okta, Oauth 2, OpenID Connect, JWT, etc. please visit the backend notes here
Oauth updates
https://auth0.com/blog/browser-behavior-changes-what-developers-need-to-know/
SameSite Cookies explained
https://web.dev/samesite-cookies-explained/
Problem using Okta encountered in development
-
<ul dir="auto"> <li> <strong>If you have ‘Third Party Cookies’ disabled</strong> or are using an <strong>‘Incognito’</strong> or <strong>‘Private’</strong> browser window, the Okta Sign-in Widget <strong>WILL</strong> stall indefinitely </li> </ul>
Okta Development Process – Angular
1. Create a free developer account at okta.com
- visit http://developer.okta.com
- sign up for free account
2. Add OpenID Connect client app in Okta
-
In your Okta Developer Account
- Create a new application
- Select option for Single-Page Application (like Angular, React, etc.)
3. Set up app configuration for OpenID Connect
- To connect with Okta, need to provide configs
- Need the clientId (public identifier of the client app) and
-
issuer … available on Okta application details screen under General Settings
-
the issuer is the issuer of tokens, a unique domain that is setup for your developer account
- ex.) https://${yourOktaDomain}/oauth2/default
- This is the URL the application will use for tokens
<li> Code:</p> <ul dir="auto"> <li> <strong>clientId</strong> – public identifier of client app </li> <li> <strong>issuer</strong> – developer domain, URL used when authorizing with Okta authorization server </li> <li> <strong>redirectUri</strong> – once user logs in successfully, send them here </li> <li> <strong>scopes</strong> – OpenID Connect uses pre-configured scopes to determine what precisely can be used for user authorization / authentication </li> </ul> </li> </ul> <p> File: my-app-config.ts </p> <pre class="notranslate"><code> export default { oidc: { clientId: 'asd987sad98f', issuer: 'https://dev-12123.okta.com/oauth2/default', redirectUri: 'http:localhost:4200/login/callback', scopes: ['openid', 'profile', 'email'] }
}
-
the issuer is the issuer of tokens, a unique domain that is setup for your developer account
4. Install Okta SDK dependencies
-
We will use two Okta SDK dependencies
-
Okta Sign-In Widget
- Okta Sign-In Widget is a JavaScript library for application login
- You dont have to create the HTML … just integrate the widget into your application
- Customizable … can use your own logo, field names and custom fields
<li> Okta Angular SDK <ul dir="auto"> <li> Okta Angular SDK provides <strong>integration with Angular Router</strong> for authentication and authorization </li> <li> Core Features: <ul dir="auto"> <li> Login/ logout from Okta using OAuth 2.0 API </li> <li> Retrieve user information and determine authentication status </li> </ul> </li> <li> Additional Features: <ul dir="auto"> <li> Add protected routes that require authentication </li> <li> Subscribe to changes in authentication state </li> </ul> </li> </ul> </li> <li> Install <ul dir="auto"> <li> <strong>$ npm install @okta/okta-sign-widget</strong> </li> <li> <strong>$ npm install @okta/okta-angular</strong> </li> </ul> </li> </ul>
-
Okta Sign-In Widget
5. Integrate Okta Sign-In Widget on our site / application
-
Add reference for okta sin in CSS
File: angular.json
… “styles”: [ “src/styles.css”, “node_modules/bootstrap/dist/css/bootstrap.min.css”, “node_modules/@fontawesome/fontawesome-free/css/all.min.css”, “node_modules/@okta/okta-signin-widget/dist/css/okta-sign-in.min.css” ], …
-
generate a login component using the Angular CLI ng generate component
- $ ng generate component components/login
- in our login.component.html file create a div container to inject the Okta Sign-in Widget