mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-14 10:34:06 +02:00
fix: User principal was discarded by the resource server re-authentication
The saas chain authenticates bearer requests twice: SupabaseAuthenticationFilter builds an EnhancedJwtAuthenticationToken with the resolved User principal, but BearerTokenAuthenticationFilter (oauth2ResourceServer) then re-authenticates the same token through the static toAuthentication converter and overwrites the SecurityContext with a token whose principal is the raw Jwt - so storage endpoints kept returning 401 "Unsupported user principal" despite the principal fix. Carry the User across in the converter: when the context already holds an EnhancedJwtAuthenticationToken for the same subject with a User principal, attach that User to the converter-built token. No extra DB lookups; anonymous sessions and API-key auth unchanged. Covered by new unit tests (carry, no-context, subject mismatch).
This commit is contained in:
@@ -23,8 +23,10 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
|
||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.OAuth2Error;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
@@ -44,6 +46,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import stirling.software.common.model.ApplicationProperties;
|
||||
import stirling.software.common.util.RequestUriUtils;
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.proprietary.security.service.TeamService;
|
||||
import stirling.software.proprietary.security.service.UserService;
|
||||
import stirling.software.saas.service.CreditService;
|
||||
@@ -333,6 +336,16 @@ public class SupabaseSecurityConfig {
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.collect(Collectors.joining(",")));
|
||||
}
|
||||
return new EnhancedJwtAuthenticationToken(jwt, authorities, email, supabaseId);
|
||||
// BearerTokenAuthenticationFilter overwrites the context SupabaseAuthenticationFilter
|
||||
// built; carry its resolved User across so instanceof-User authorization keeps working.
|
||||
User user = null;
|
||||
Authentication existing = SecurityContextHolder.getContext().getAuthentication();
|
||||
if (existing instanceof EnhancedJwtAuthenticationToken enhanced
|
||||
&& supabaseId != null
|
||||
&& supabaseId.equals(enhanced.getSupabaseId())
|
||||
&& enhanced.getPrincipal() instanceof User existingUser) {
|
||||
user = existingUser;
|
||||
}
|
||||
return new EnhancedJwtAuthenticationToken(jwt, authorities, email, supabaseId, user);
|
||||
}
|
||||
}
|
||||
|
||||
+49
@@ -9,17 +9,25 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.authentication.AbstractAuthenticationToken;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
|
||||
import org.springframework.security.oauth2.jwt.Jwt;
|
||||
|
||||
import stirling.software.proprietary.security.model.User;
|
||||
import stirling.software.saas.security.SupabaseSecurityConfig.SupabaseTokenValidator;
|
||||
|
||||
/** Unit tests for the JWT-claim → Spring authorities mapping. */
|
||||
class SupabaseSecurityConfigTest {
|
||||
|
||||
@AfterEach
|
||||
void clearSecurityContext() {
|
||||
SecurityContextHolder.clearContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
void anonymousJwtGetsLimitedApiUserRole() {
|
||||
Jwt jwt = jwtWith(true, null, null, null, List.of());
|
||||
@@ -75,6 +83,47 @@ class SupabaseSecurityConfigTest {
|
||||
.doesNotContain("ROLE_", "PERM_");
|
||||
}
|
||||
|
||||
@Test
|
||||
void carriesUserPrincipalFromContextBuiltByAuthFilter() {
|
||||
Jwt jwt = jwtWith(false, "[email protected]", "authenticated", null, List.of());
|
||||
User user = new User();
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(
|
||||
new EnhancedJwtAuthenticationToken(
|
||||
jwt, List.of(), "[email protected]", jwt.getSubject(), user));
|
||||
|
||||
AbstractAuthenticationToken auth = SupabaseSecurityConfig.toAuthentication(jwt);
|
||||
|
||||
assertThat(auth.getPrincipal()).isSameAs(user);
|
||||
}
|
||||
|
||||
@Test
|
||||
void principalStaysJwtWithoutContextUser() {
|
||||
Jwt jwt = jwtWith(false, "[email protected]", "authenticated", null, List.of());
|
||||
|
||||
AbstractAuthenticationToken auth = SupabaseSecurityConfig.toAuthentication(jwt);
|
||||
|
||||
assertThat(auth.getPrincipal()).isSameAs(jwt);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ignoresContextUserForDifferentSubject() {
|
||||
Jwt jwt = jwtWith(false, "[email protected]", "authenticated", null, List.of());
|
||||
Jwt other = jwtWith(false, "[email protected]", "authenticated", null, List.of());
|
||||
SecurityContextHolder.getContext()
|
||||
.setAuthentication(
|
||||
new EnhancedJwtAuthenticationToken(
|
||||
other,
|
||||
List.of(),
|
||||
"[email protected]",
|
||||
other.getSubject(),
|
||||
new User()));
|
||||
|
||||
AbstractAuthenticationToken auth = SupabaseSecurityConfig.toAuthentication(jwt);
|
||||
|
||||
assertThat(auth.getPrincipal()).isSameAs(jwt);
|
||||
}
|
||||
|
||||
private static Jwt jwtWith(
|
||||
boolean anonymous,
|
||||
String email,
|
||||
|
||||
Reference in New Issue
Block a user