Since my job was working on the login page, the main portion of my backend was implementing JWT: JSON Web Tokens.

INITIAL PLAN FROM GROUP ISSUE:

image.png

Diagram I made to Understand Spring and JWT Organization Better

image.png

DETAILED OVERVIEW OF API & CHANGES MADE TO EXISTING TEMPLATE

DatabaseApiController.java

Purpose: The getDB method is responsible for checking the user’s permissions before getting the entries from the SQLite DB, and a user’s permissions are determined based on the JWT cookie we’ve made named “flashjwt.”

@RestController
@RequestMapping("/api/db")
public class DatabaseApiController {

  @Autowired
  LoginHandler handler;

  @GetMapping("/sqlite.db")
  public ResponseEntity<Object> getDB (@CookieValue("flashjwt") String jwt) throws IOException {
    Person p = handler.decodeJwt(jwt);
    if (p.isAdmin() && p != null) {
      Resource coolDbResource = new FileSystemResource("volumes/sqlite.db");

      // Check if the file exists
      if (!coolDbResource.exists()) {
          throw new FileNotFoundException("sqlite.db file not found");
      }

      // Set the content type of the file
      String contentType = "application/octet-stream";

      // Returns the response back from the database
      return ResponseEntity.ok()
              .contentType(MediaType.parseMediaType(contentType))
              .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + coolDbResource.getFilename() + "\"")
              .body(coolDbResource);
    } else {
      Map<String, Object> resp = new HashMap<>();
      resp.put("err", "Unauthorized");
      return new ResponseEntity<>(resp, HttpStatus.UNAUTHORIZED);
    }
      
  }
}

IMPORTANT COMPONENTS & KEY LEARNINGS

The getDB method takes the parameter ‘jwt’ - extracting the “flashjwt” cookie value and providing it as the ‘jwt’ parameter to the method.

Then, the handler method is used to decode the JWT and retrieve a ‘Person’ object, checking whether or not a user is an admin AND if the ‘Person’ object is not null.

If the user is an admin, it is able to access the sqlite.db file and its accompanying entries.

OOP PRINCIPLES:

MODULARITY:

The code is modular, with different methods and classes responsible for distinct tasks. This allows for a separation of concerns and allows for easier maintenance of the entire authentication portion of the project.

PersonApiController.java

@GetMapping("/isAdmin")
    public ResponseEntity<Object> getIsAdmin (@CookieValue("flashjwt") String jwt) {
        Person p = handler.decodeJwt(jwt);
        if (p != null && p.admin) {
            Map<String, Object> resp = new HashMap<>();
            resp.put("err", false);
            return new ResponseEntity<>(resp, HttpStatus.OK);

        } else {
            Map<String, Object> resp = new HashMap<>();
            resp.put("err", "Unauthorized");
            return new ResponseEntity<>(resp, HttpStatus.UNAUTHORIZED);
        }
    }

OOP APPLICATION

ABSTRACTION

  1. Utilizing HashMaps for holding key-value pairs of “err” key representing the authorization status of a user, and the value of “false” or “Unauthorized” - this structures how the response is sent to the client.

  2. Easy Simplification of JWT Decoding Process - only one line of code. ‘decodeJWT’ returns the Person object and this is something that can easily be applied to another project.

image.png image1.png image2.png

PROGRESSING

image.png

ANALYTICS:

image.png

I have put in a lot of effort towards the backend - I put in a lot of effort towards learning about JWT and Spring, and though I still have a long way to go, I have made a lot of progress from the start, when I had absolutely nothing.

CHALLENGES WE FACED WITH JWT AND AUTHORIZATION

415 Unsupported Media Type Error

Every time we would try to send a POST request to authorize the user (during Postman testing stages as well) we would receive a 415 Unsupported Media Type Error.

SOLUTION: We made sure that the request headers included “Content-Type”: “application/json” to specify the content type as JSON.

403 Forbidden Error

At one point in the last minute our backend runtime requests just stopped functioning, giving us a 403 Forbidden Error.

SOLUTION: We had to go into the SecurityConfig.java file, make sure that we used permitAll(), allowing public access to specific endpoints for both GET and POST requests.

CORS

CORS is still giving us errors despite the cookie for authorization showing up.

SOLUTION: We were able to add the necessary CORS headers to allow frontend requests. These included Access-Control-Allow-Origin and Access-Control-Allow-Methods in specific in the SecurityConfig file.