And now here is my secret, a very simple secret: It is only with the heart that one can see rightly; what is essential is invisible to the eye. : The Fox

Day-1#to-do-

Breakfast was Amazing !! overnight oats

So let’s experiment with the api keys

what do we need ?

We are api keys

We get decrypted and then gets matched with our SHA 256 Hash and then if matched we become responsible for the person i.e. ADMIN who created us

So we need

  • Generate api key
  • Delete Api key
  • Get Api Key Roles
  • Catch the key in the security config
  • Decrypt for role
  • Pass it to the controller
  • Fetch the User

So This APi key thing Worked pretty Nicely What did i learn. All this Ai coding is complete bullshit.

East or West VsCode is the best.

Also In Spring boot there is this thing in Security Principal and Authorities are something what they call the caller of a particular api

So

  • Access Token Means User
  • X-Api-Key System

All we need is to do this

public Collection<? extends GrantedAuthority> getAuthorities() {
 
	return Collections.singletonList(
		new SimpleGrantedAuthority("ROLE_" + role.name())
);
 
}

Add this to the model

And then add the role to the request thus and spring will map it to Authentication Proncipal

package io.saanvi.saanvibackend.shared.utils;
 
import io.saanvi.saanvibackend.auth.models.ApiKey;
import io.saanvi.saanvibackend.auth.services.ApiKeyService;
import io.saanvi.saanvibackend.auth.services.TokenService;
import io.saanvi.saanvibackend.user.model.User;
import io.saanvi.saanvibackend.user.services.UserService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.stereotype.Component;
import  jakarta.servlet.ServletException;
import java.io.IOException;
import java.util.Optional;
 
 
@Component
public class requestFilter extends OncePerRequestFilter {
    
    @Autowired
    protected UserService userService;
    
    @Autowired
    protected ApiKeyService apiKeyService;
 
    private static final String[] EXCLUDED_PATHS = {
            "/auth/login",
 
 
 
 
 
    /**
     * Fetches the Bearer token from the Authorization header of the request.
     *
     * @param request The HttpServletRequest object containing the request details.
     * @return An Optional containing the Bearer token if present, otherwise an empty Optional.
     */
    protected Optional<String> fetchBearerToken(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        return (token != null && token.startsWith("Bearer ")) ? 
            Optional.of(token.substring(7)) :
            Optional.empty();
    }
 
    /**
     * Fetches the API key from the request header.
     *
     * @param request The HttpServletRequest object containing the request details.
     * @return An Optional containing the API key if present, otherwise an empty Optional.
     */
    protected Optional<String> fetchApiKey(HttpServletRequest request) {
        String apiKey = request.getHeader("X-API-KEY");
        return (apiKey != null && !apiKey.isEmpty()) ? 
            Optional.of(apiKey) :
            Optional.empty();
    }
 
 
    /**
     * Filters incoming requests to check for a valid JWT token.
     * If a valid token is found, it sets the authentication in the SecurityContext.
     *
     * @param request The HttpServletRequest object containing the request details.
     * @param response The HttpServletResponse object for sending the response.
     * @param filterChain The FilterChain to continue the request processing.
     * @throws ServletException If an error occurs during request processing.
     * @throws IOException If an I/O error occurs during request processing.
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain )throws ServletException, IOException {
        String path = request.getRequestURI();
 
        for (String excluded : EXCLUDED_PATHS) {
            if (path.startsWith(excluded)) {
                filterChain.doFilter(request, response);
                return;
            }
        }
        
 
        Optional<String> bearerToken = fetchBearerToken(request);
        Optional<String> apiKey = fetchApiKey(request);
        
        if(apiKey.isPresent()){
            
            try {
                logger.info(hashUtil.getHashedValue(apiKey.get()));
                Optional<ApiKey> _apiKeyDetails = apiKeyService.findByApiKey(hashUtil.getHashedValue(apiKey.get()));
                
                if (_apiKeyDetails.isEmpty())  throw new Error("Invalid API key");
            
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(_apiKeyDetails.get(), null, _apiKeyDetails.get().getAuthorities());
                SecurityContextHolder.getContext().setAuthentication(authentication);
            } catch (Exception e) {
                throw new Error("Invalid API key");
            }
            filterChain.doFilter(request, response);
        }
 
        else if(bearerToken.isPresent()){
            String token = bearerToken.get();
            String userId;
            try {
                userId = TokenService.validateAccessToken(token);
                User user = userService.findUserById(userId);
                UsernamePasswordAuthenticationToken authentication =
                        new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                        SecurityContextHolder.getContext().setAuthentication(authentication);
            } catch (Exception e) {
                throw new Error("Invalid token");
            }
            
        }
 
        filterChain.doFilter(request, response);
    }
}
 

And wait to use it as a

    
 
    @GetMapping("/users/me") // User Response
    public ResponseEntity<User> getAuthenticatedUser(@AuthenticationPrincipal User user) {
        return ResponseEntity.ok(user);
    }
 
 
 
    @PreAuthorize("hasRole('ADMIN')")
    @GetMapping("/users")
    public List<User> getAllUsers(){ // [User Response]
        logger.info("Fetching all users");
        return userService.findAll();
    }
    

Now Both From api key and Access-token we can call the Service and check the authority of that request

  • Generate api key : Done
  • Delete Api key : Done
  • Get Api Key Roles : Done
  • Catch the key in the security config : Done
  • Decrypt for role : Done
  • Pass it to the controller : Done
  • Fetch the User : Done

To Do

  • Make the login api Live
  • Make Entity Use it
  • Make Saanvi Live