I am working on a simple web app for learning purposes using Angular for the frontend and Java Spring for the backend. I don't have a particular problem that I want you guys to help me out with, instead I have a question about OAuth2 authentication.
I have registered my Angular SPA in Azure AD (Authorization Code Flow + PKCE), I set up roles and everything is working okay. My question is what do I do when authenticated users ping my backend? My backend has no information about the users.
I thought of a solution to make a web filter, and every time an authenticated user pings any endpoint requiring the user to be authenticated, to check the database if the user exists (through the username), and save him if he does not exist. I'm pretty sure this will work, but I don't think this is the best solution, considering my web filter will have to read from the databases for every single HTTP request that comes in, and write to the database occasionally (if the user logs in for the first time).
I shouldn't be worried about performance issues because I'm building this strictly for learning purposes, but nevertheless I want to do this the right way. I tried googling this in multiple ways, but I guess I'm not using the right keywords to find what I'm looking for. Any opinion or advice would be much appreciated! Thanks!
EDIT: I followed this article to achieve the OAuth2 + OIDC authentication and authorization, my security config in the backend is the same: https://ordina-jworks.github.io/security/2020/08/18/Securing-Applications-Azure-AD.html
Post the discussion with clarity on the requirements. If you want to use have the following:
With the requirement of not always hitting your Database, one option is to use a cache.
The ideal solution for this cache to work is:
Example:
Implement a CacheService.java
package com.example.springboot;
import java.util.Collections;
import org.apache.catalina.User;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class CacheService {
@Bean
public CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.setCaches(Collections.singletonList(new ConcurrentMapCache("users")));
return cacheManager;
}
@Cacheable(cacheNames = "users")
public User getUser(String username) {
// Code below will not execute after the first calling for the given username.
// So if one username is already cached, it would not invoke for the same user again from the DB.
// Get or Create a new user based on the Database call
return null;
}
}
Then implement a web filter like:
package com.example.springboot;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
@Component
public class CredentialsInjectionFilter extends GenericFilterBean {
@Autowired
private CacheService cacheService;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
cacheService.getUser("my_username");
filterChain.doFilter(servletRequest, servletResponse);
}
}
More on Caching with Springboot: https://www.javadevjournal.com/spring/spring-caching/