I have problem to authentication with spring boot 3 and angular 16. I develop an Authorization Server and get access token with "authorization_code" grand type in postman successfully. but it dose not worked in angular 16 project.
The Authorization server configuuration:
@Configuration
@EnableWebSecurity
public class AuthorizationServerConfig {
private final UserRepository userRepository;
private final ClientRepository clientRepository;
public AuthorizationServerConfig(UserRepository userRepository, ClientRepository clientRepository) {
this.userRepository = userRepository;
this.clientRepository = clientRepository;
}
@Bean
public AuthorizationServerSettings authorizationServerSettings() {
return AuthorizationServerSettings.builder()
.issuer("http://localhost:8089")
.build();
}
@Bean
@Order(1)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
http.cors(Customizer.withDefaults());
http.exceptionHandling(exceptions -> exceptions
.defaultAuthenticationEntryPointFor(
new LoginUrlAuthenticationEntryPoint("/login"),
new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
)
);
http.oauth2ResourceServer(resourceServer -> resourceServer
.jwt(Customizer.withDefaults())
);
http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
.oidc(Customizer.withDefaults());
return http.build();
}
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/.well-known/*").permitAll()
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> accessTokenCustomizer() {
return context -> {
if (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {
String username = context.getPrincipal().getName();
UserEntity user = userRepository.findByName(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
Set<String> roles = new HashSet<>();
for (RoleEntity role: user.getRoles())
roles.add(role.getName());
context.getClaims().claim("username", username);
context.getClaims().claim("roles", roles);
// find branch
String branchId = "";
context.getClaims().claim("organization", branchId);
}
};
}
@Bean
@Primary
public RegisteredClientRepository registeredClientRepository() {
List<RegisteredClient> registeredClients = new ArrayList<>();
List<ClientEntity> clientEntities = clientRepository.findAll();
for (ClientEntity clientEntity : clientEntities)
registeredClients.add(clientEntity.maptoRegisteredClient());
registeredClients.add(publicClientPKCE());
return new InMemoryRegisteredClientRepository(registeredClients);
}
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
private RegisteredClient publicClientPKCE() {
return RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("public-client")
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:4200")
.scope(OidcScopes.OPENID)
.scope(OidcScopes.PROFILE)
.clientSettings(ClientSettings.builder()
.requireAuthorizationConsent(false)
.requireProofKey(true) // PKCE required
.build()
)
.build();
}
}
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("http://127.0.0.1:4200");
config.addAllowedOrigin("http://localhost:4200");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
config.setAllowCredentials(true);
source.registerCorsConfiguration("/**", config);
source.registerCorsConfiguration("/.well-known/**", config);
return source;
}
}
and angular configuration like this:
export const authConfig: AuthConfig = {
issuer: 'http://localhost:8089', // URL of the Authorization Server
redirectUri: window.location.origin, // Redirect URI
clientId: 'public-client', // Client ID for the Angular app
responseType: 'code', // Authorization Code Flow
scope: 'openid profile', // Scopes requested
showDebugInformation: true,
useSilentRefresh: false, // To silently refresh tokens
silentRefreshTimeout: 5000,
sessionChecksEnabled: true,
timeoutFactor: 0.75,
useHttpBasicAuth: false,
disableAtHashCheck: true,
strictDiscoveryDocumentValidation: false
};
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(private oauthService: OAuthService, private router: Router) {
this.oauthService.configure(authConfig);
}
ngOnInit(): void {
this.oauthService
.loadDiscoveryDocumentAndTryLogin()
.then(() => {
debugger;
if (this.oauthService.hasValidAccessToken()) {
console.log('Logged in successfully');
this.router.navigate(['test']);
} else {
this.oauthService.initLoginFlow();
}
})
.catch((error) => {
debugger
console.error('Error during login:', error);
});
}
}
browser console error:
Access to XMLHttpRequest at 'http://localhost:8089/.well-
known/openid-configuration' from origin 'http://127.0.0.1:4200'
has been blocked by CORS policy: Response to preflight request
doesn't pass access control...
I believe requests to .well-known
is processed by the second SecurityFilterChain
, hence you'll need to add .cors(Customizer.withDefaults())
there as well.
@Bean
@Order(2)
public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http
.cors(Customizer.withDefaults())
.authorizeHttpRequests(authorize -> authorize
.requestMatchers("/.well-known/*").permitAll()
.requestMatchers("/login").permitAll()
.anyRequest().authenticated()
)
.formLogin(Customizer.withDefaults());
return http.build();
}