Skip to main content
  1. All Posts/

spring-angular-ecommerce-frontend

eCommerce TypeScript

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:

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.

    • To view the Spring Boot backend please click here
    • To view the live site hosted on Heroku please click here
    • Additional functionality may be accessed by logging in with the test account

      • username: “test@test.com”
      • password: “techtonics”
      • More info on enabling Okta user registration on applications here

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

  1. Problem – Handling Stored Data and Browser Refresh
  2. Okta Integration
  3. Form Validation
  4. Save the Order
  5. Order History
  6. Secure Order History
  7. Secure Communication
  8. 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
    •   <li>
          Each web browser tab has it&#8217;s own &#8220;session&#8221; <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>
      
    • 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 &#8211; called <strong>Data Scoping</strong> <ul dir="auto">
              <li>
                i.e. &#8211; 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 &#8230; 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 Scoping

      • 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

      1. Update CartService to read data from Session storage
        File: cart.service.ts

         export 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();
             }
         }
        

        }

      2. Add new method in CartService: persistCartItems()
        File: cart.service.ts

         export class CartService {
             cartItems: cartItem[] = [];
             ...
        
         storage : Storage = sessionStorage;
        
         persistCartItems() {
             this.storage.setItem('cartItems', JSON.stringify(this.cartItems));
         }
        

      3. Modify computeCartTotals() to call new method: persistCartItems()
        File: cart.service.ts

         export class CartService {
             cartItems: cartItem[] = [];
             ...
        
         storage : Storage = sessionStorage;
        
         persistCartItems() {
             this.storage.setItem('cartItems', JSON.stringify(this.cartItems));
         }
        
         computeCartTotals() {
             ...
             ...
        
             // persist cart data
             this.persistCartItems();
         }
        

      Top

      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

      • Using Okta for authentication and application sign-in and redirect uses COOKIES

        <ul dir="auto">
          <li>
            <strong>If you have &#8216;Third Party Cookies&#8217; disabled</strong> or are using an <strong>&#8216;Incognito&#8217;</strong> or <strong>&#8216;Private&#8217;</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

      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> &#8211; public identifier of client app
                </li>
                <li>
                  <strong>issuer</strong> &#8211; developer domain, URL used when authorizing with Okta authorization server
                </li>
                <li>
                  <strong>redirectUri</strong> &#8211; once user logs in successfully, send them here
                </li>
                <li>
                  <strong>scopes</strong> &#8211; 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']
            }
          

          }

        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>
            

          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