4 minute read

Three Ways to Implement Authentication

Before implementing user sign-in and sign-up, we need to decide on an authentication method. Generally, there are three common authentication approaches:

  1. Basic authentication: This method involves sending the user’s credentials (user ID and password) encoded in Base64 with every request. While it is simple to implement, several critical issues make it unsuitable:

    1. Insecure: Basic authentication transmits credentials in an encoded format, not encrypted. Encoding is reversible, meaning attackers can easily decode and retrieve user credentials.
    2. Lack of session management: Since there is no session or token-based authentication, users must send their credentials with every request. This results in a poor user experience😡, as users have to repeatedly enter their credentials for every interaction, such as liking or commenting.
  2. OAuth Authentication: Many of you have likely signed up for a website using third-party providers such as Google or GitHub. This process is implemented using OAuth authentication. OAuth allows users to sign in using external identity providers (e.g., Google, GitHub) without directly handling their credentials.

    However, I have decided not to use OAuth for Quad because I will not be integrating third-party authentication services. Although OAuth can also support internal authentication systems, implementing it requires significantly more time and effort compared to the next authentication method I will introduce.

  3. Bearer Token: When a client signs in, the server generates and transmits a Bearer Token to the client. This token remains valid until its expiration time set within the token.

    Once the client receives the Bearer Token, it can use it for authentication by attaching it to the HTTP request header when accessing protected resources. This is a example of an HTTP request header with a Bearer Token:

    Authorization: Bearer <token>
    

    I will implement Bearer Token authentication using JWT (JSON Web Token), which provides secure and stateless authentication. It ensures security through encryption with specific algorithms, and its stateless nature eliminates the need for servers to store session data. This means the server doesn’t need to query the database for each request, resulting in faster and more efficient authentication.

JWT…? Who are you?

  1. How is a JWT token structured?

    Jwt is consist of three parts, separated by dots(.):

    <Header>.<Payload>.<Signature>
    
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIiwiZXhwIjoxNzEwOTUwNDAwfQ.H0VlMa2UNxIl_6Im7vx2gCy0j2sr_vsaKdmX9EoIqKw
    ### This is what a JWT looks like.
    ### Try to find the two dots separating the JWT sections!
    
    1. Header: This section defines the signing algorithm. I chose a symmetric key algorithm because it’s simpler to implement than asymmetric key algorithms. 😂
    2. Payload: It contains data fields, such as user data and JWT expiration time. ✅ Every payload properties are called claims.
    3. Signature: The signature is generated when the JWT is created. It is formed by encrypting the header and payload using the server’s secret key. Once created, the signature cannot be altered.
  2. How is JWT used for authentication?

    IMG_2646

    1. The client signs in with a username and password and sends an HTTP request to the server for authentication.
    2. The server verifies whether the provided credentials are valid by checking them against the database.
    3. If valid, the server generates a JWT, and sends it to the client in an HTTP response as aJSON object.
    4. Once the client stores the JWT, it is used as a Bearer Token for authentication.
    5. For future requests, the client includes the Bearer Token in the HTTP headers until the JWT expires.
    6. When the client sends a request with the Bearer Token, the server verifies the JWT. If the token is valid and not expired, the server processes the request; otherwise, it rejects it.

In which part of the application is the Bearer token validated?

In Spring Boot, when a client sends a request, it passes through several filters before reaching the controller. Filters act as a security layer, verifying whether the request is valid.

Generally the request first goes through

  1. CORS filter

  2. Spring Security filter

  3. Custom filters like JwtAuthenticationFilter

  4. And finally reaches the DispatcherSevlet which maps HTTP request to appropriate controller.


  1. CORS filter

    To understand CORS(Cross-Origin Resource Sharing), we need to understand SOP(Same-Origin Policy). SOP is a fundamental security mechanism enforced by web browsers that restricts web pages from making requests to a different origin (protocol, domain, or port) than the one that served them. Origin refers to the protocol, hostname and port of a web application. Here is a example of origin:

    <protocol>://<hostname>:<port>
    ## e.g. http://localhost:3000
    

    By default, SOP prevents unauthorized cross-origin requests, protecting data from malicious websites. For example, a front-end application running on http://localhost:3000 cannot directly access an API hosted at http://localhost:8080 due to SOP restrictions.

    To enable cross-origin communication, the backend server must explicitly allow the frontend domain using CORS (Cross-Origin Resource Sharing).

    To allow a frontend (http://localhost:3000) to send requests to the backend, configuring CORS in the backend is required.

    CorsConfiguration configuration = new CorsConfiguration();
            configuration.addAllowedOrigin("http://localhost:3000");
    
  2. Spring Security filter

    Spring security applies security logics such as authentication (who you are), authorization (what can you access) and CSRF (Cross-Site Request Forgery) protection.

    In WebSecurityConfig.java, the security configuration file, there’s an interesting point to highlight.

    httpSecurity.csrf(CsrfConfigurer::disable)
    

    I have explicitly disabled CSRF. Even for someone unfamiliar with CSRF, just reading the term might give the impression that disabling it is a bad idea. So why have I disabled it?

    CSRF (Cross-Site Request Forgery) is an attack where a malicious website tricks an authenticated user into making unwanted requests to a web application while they are logged in. Being logged in means that the user is authenticated and has an active session on the server, rather than using stateless token-based authentication like JWT. In other words, it can be safely disabled when using JWT or Bearer Tokens.

  3. JwtAuthenticationFilter

    JwtAuthenticationFilter is a custom Spring Security filter that extracts the JWT from the Authorization header, and validates it using JwtProvider. If the token is valid, it extracts the user details and stores them in SecurityContextHolder, making the user data accessible throughout the application.

    SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
    securityContext.setAuthentication(authenticationToken);
       
    SecurityContextHolder.setContext(securityContext);
    

Leave a comment