-1

I am trying to figure out how to implement endpoint user security via https://spring.io/guides/topicals/spring-security-architecture/#_working_with_threads and How to allow a User only access their own data in Spring Boot / Spring Security? However, @AuthenticationPrincipal, Authentication, and Principal when used in this manner. What am I missing? I am certain I have the right secrets/Jwt set up because the authentication itself works, I just cant use the annotation to pull the information I want

SecurityConfig.class

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class SecurityConfig {

    private static final String[] AUTH_WHITELIST = {
            // -- Swagger UI v2
            "/v2/api-docs",
            "/swagger-resources",
            "/swagger-resources/**",
            "/configuration/ui",
            "/configuration/security",
            "/swagger-ui.html",
            "/webjars/**",
            // -- Swagger UI v3 (OpenAPI)
            "/v3/api-docs/**",
            "/swagger-ui/**"
            // other public endpoints of your API may be appended to this array
    };
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                // Our public endpoints
                .antMatchers("/**/openapi.json").permitAll()
                .antMatchers("/api/ims/auth/**").permitAll()
                .antMatchers(AUTH_WHITELIST).permitAll()
                .anyRequest().authenticated()
                .and()
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return http.build();
    }

    // Used by JwtAuthenticationProvider to decode and validate JWT tokens
    @Bean
    public JwtDecoder jwtDecoder() {
        SecretKey secretKey = new SecretKeySpec("INSERTSECRETHERE".getBytes(), "HMACSHA256");
        return NimbusJwtDecoder.withSecretKey(secretKey).macAlgorithm(MacAlgorithm.HS256).build();
    }
}

ConnectionController.class

@Path("connection/v1")
@SecurityScheme(name = SWAGGER_AUTH_NAME, type = HTTP, scheme = SWAGGER_AUTH_SCHEME, in = HEADER)
@Tag(name = "Connection Controller", description = "Manage connection resources")
@RestController
public class ConnectionController {
    private final ConnectionService connectionService;
    @Inject
    public ConnectionController(final ConnectionService connectionService)
    {
        this.connectionService = connectionService;
    }

    @GET
    @Path("/user/{userId}")
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(description = "Retrieve connections by userId")
    //@PreAuthorize("authentication.principal.claims.userId.equals(#userId)")
    public List<ConnectionDto> getConnectionsByUserId(@PathParam("userId") final String userId, @Parameter(hidden = true) @AuthenticationPrincipal Jwt authentication)
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication1 = context.getAuthentication();
        Object jwt = context.getAuthentication().getPrincipal();
        Assert.hasText(userId, "User id must be provided");
        return this.connectionService.getConnectionsByUserId(userId);
    }
}

Variables context, authentication, and jwt all return what I anticipate, the last being a Jwt object, which is exactly what I'd want @AuthenticationPrincipal to return. However, @AuthenticationPrincipal, Authentication, and Principal all return null from that method.

dur
  • 15,689
  • 25
  • 79
  • 125
Caledrith
  • 11
  • 3
  • `I just cant use the annotation to pull the information I want` is a very bad way of describing what it is you want. JWTs dont use principals they use `JwtAuthenticationToken` this will solve what you are doing https://thomasandolf.medium.com/spring-security-jwts-getting-started-ebdb4e4f1dd1 – Toerktumlare Jul 09 '23 at 10:02

1 Answers1

0

@AuthenticationPrincipal is an annotation that should be usable with almost any Spring Security authentication schema. Using this tutorial, I realized that the issue was that I wasn't in a @RequestMapping annotation, but rather a @GET and @PATH annotation as my original project was off a very old version of Spring. Once I adjusted the Controller to the below code, @AuthenticationPrincipal got the JWT properly.

@RestController
public class ConnectionController {
    private final ConnectionService connectionService;

    @Inject
    public ConnectionController(final ConnectionService connectionService)
    {
        this.connectionService = connectionService;
    }
    @GetMapping("/user/{userId}")
    @Produces(MediaType.APPLICATION_JSON)
    @Operation(description = "Retrieve connections by userId")
    //@PreAuthorize("authentication.principal.userId.equals(#userId)")
    public List<ConnectionDto> getConnectionsByUserId(@PathVariable("userId") final String userId, @Parameter(hidden = true) @AuthenticationPrincipal Jwt authentication)
    {
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication authentication1 = context.getAuthentication();
        Object jwt = context.getAuthentication().getPrincipal();
        Assert.hasText(userId, "User id must be provided");
        return this.connectionService.getConnectionsByUserId(userId);
    }
}
Caledrith
  • 11
  • 3