package com.okta.developer.store.config; import com.okta.developer.store.security.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 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.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import com.okta.developer.store.security.oauth2.AudienceValidator; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.jwt.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.oauth2.core.oidc.OidcUserInfo; import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.stream.Collectors; import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport; @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @Import(SecurityProblemSupport.class) public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Value("${spring.security.oauth2.client.provider.oidc.issuer-uri}") private String issuerUri; private final SecurityProblemSupport problemSupport; public SecurityConfiguration(SecurityProblemSupport problemSupport) { this.problemSupport = problemSupport; } @Override public void configure(HttpSecurity http) throws Exception { // @formatter:off http .csrf() .disable() .exceptionHandling() .accessDeniedHandler(problemSupport) .and() .headers() .frameOptions() .disable() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers("/api/**").authenticated() .antMatchers("/api/auth-info").permitAll() .antMatchers("/management/health").permitAll() .antMatchers("/management/info").permitAll() .antMatchers("/management/prometheus").permitAll() .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) .and() .oauth2ResourceServer().jwt(); // @formatter:on } /** * Map authorities from "groups" or "roles" claim in ID Token. * * @return a {@link GrantedAuthoritiesMapper} that maps groups from * the IdP to Spring Security Authorities. */ @Bean @SuppressWarnings("unchecked") public GrantedAuthoritiesMapper userAuthoritiesMapper() { return (authorities) -> { Set<GrantedAuthority> mappedAuthorities = new HashSet<>(); authorities.forEach(authority -> { OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority; OidcUserInfo userInfo = oidcUserAuthority.getUserInfo(); Collection<String> groups = (Collection<String>) userInfo.getClaims().get("groups"); if (groups == null) { groups = (Collection<String>) userInfo.getClaims().get("roles"); } mappedAuthorities.addAll(groups.stream() .filter(group -> group.startsWith("ROLE_")) .map(SimpleGrantedAuthority::new).collect(Collectors.toList())); }); return mappedAuthorities; }; } @Bean JwtDecoder jwtDecoder() { NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport) JwtDecoders.fromOidcIssuerLocation(issuerUri); OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(); OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuerUri); OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator); jwtDecoder.setJwtValidator(withAudience); return jwtDecoder; } }