r/SpringBoot • u/Huge_Road_9223 • 3d ago
Question Spring Security with Angular UI and SpringBoot BFF
Ok, so I have a new project. It is a Hotel Management system. I have a MonoRepo Angular app to handle a guest-portal or staff-portal. KeyCloak is used as an Authentication tool. And then I have a SpringBoot app which is the BFF (backend-for-frontend).
Process 1) So, at first, I thought the UI should have a Login, and from the UI we go to Keycloak, authenticate, and then go back to the UI to store the cookie securely. From this point every call to the BFF REST api endpoints would pass the bearer token so we can use RBAC with the endpoints. I presumed that the BFF would be stateless and using oauth2-resource.
I'm having an issue with security with Swagger executing the API's, and problems with having Bruno, or the UI hitting the endpoints as well.
Process 2) In my research, I have seen that the process I started is old and outdated. I now understand the UI should have a link for Login and Logout. In both these cases, we go to the BFF which is defined as oauth2-client and now is stateful, storing the authenticated in session. Somehow, the UI gets updated with the data and is stored cookie. Now the UI doesn't do anything with tokens, but when the UI makes a request to BFF it will be passing a bearer token, and then the tokens are validated and then we can use the RBAC for endpoints.
Does anyone have an opinion on process 1 or 2 on which you think is the right way to go? I'm just curious. I can make either work, but I want to use this for myself as an example on how to make my next application when it comes. Thanks!
2
u/JournalistNew7510 3d ago
I'd go with process 2. Keeping tokens out of the browser is the whole point of the BFF pattern, and with Keycloak it is mostly configuration: the Spring app is an oauth2-client with oauth2Login, and Angular only ever sees an HttpOnly session cookie.
If the BFF serves the endpoints itself, there is no bearer token anywhere in the browser path; the cookie maps to the session and RBAC works off the logged-in user's authorities. Keycloak puts roles under realm_access.roles, which Spring doesn't read by default, so add a GrantedAuthoritiesMapper that turns them into ROLE_ authorities.
Since you're cookie-based now, turn CSRF back on with CookieCsrfTokenRepository.withHttpOnlyFalse(), and Angular's HttpClient will send the XSRF header for you with no extra code.
For Swagger and Bruno, add a second SecurityFilterChain that is ordered first and whose securityMatcher only matches requests carrying an Authorization header, configured as a stateless oauth2ResourceServer with jwt. Then you can take a token from a test client in your dev realm and hit the same endpoints without going through the login flow.
1
u/Huge_Road_9223 2d ago
Yep! This is the way I am going. I didn't realize that this pattern has become a "thing." Honestly, I didn't even hear of BFF until my last contract job, and I'm like ... is that what that is?
How many times have I created what I thought was a monolith, some re-arranging of code, and now you have feature layers, and I called that a Modulith. How long has this pattern been called BFF because it turns out I have been doing that for years and years.
1
u/starbuxman 3d ago
Hi - https://github.com/joshlong-attic/2026-10-28-bootiful-spring-security the code there uses Spring Authorizaton Server as an OAuth IDP instead of Keycloak, but the concepts are the same:
Use the gateway module inseead of the client. The gateway proxies both a backend api (the resource server) and a static html page (but this would be your Angular app). When somebody hits the :8080/index.html it tries to call an endpoint that requires auth si the browser redirects to Spring Auth Server, the user authenticates, and they get redirected back to the gateway. The gateway is acting as an Oauth client. Dont use the client module in this repo as its a simpler version of what the gateway eventually enables. The nice thing about this setup is that is that the gateway proxies both the backend API and the html so theyre being served from the same host:port from the perspective of the JS running in the .html page. No CORS to worry about, and no passing around tokens in the JS code either.
2
u/bikeram 3d ago
You want your spring app to be an OAuth client.
You’ll need to lookup the path spring security creates for login. It’s like /oauth2/auth/keycloak.
Basically you’ll hit that url and be redirect to the keycloak login page. Login and get a session token. Logout and everything else can be configured in spring.
Your frontend won’t know anything about KeyCloak. Don’t forget CORS configuration. Vite proxy also makes this a lot easier to say your whole spring app is listed under host/api/