I'm building a Spring Boot microservices application where an auth-service generates JWTs and a user-service validates them. I'm consistently getting a 401 Unauthorized error in the user-service when I make a request with a valid token generated by the auth-service.The specific error from the user-service logs is: An error occurred while attempting to decode the Jwt: Signed JWT rejected: Another algorithm expected, or no matching key(s) foundThis is very confusing because I have meticulously configured both services to use the HS512 algorithm and the exact same secret key.
My Setup1. auth-service - Token Generation (JwtService.java)This service creates the token using the io.jsonwebtoken (jjwt) library and signs it with SignatureAlgorithm.HS512.
package com.grambasket.authservice.security;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.util.Date;
import java.util.Map;
@Service
@Slf4j
public class JwtService {
@Value("${spring.security.oauth2.resourceserver.jwt.secret-key}")
private String jwtSecret;
private Key signInKey;
@PostConstruct
public void init() {
this.signInKey = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
log.info("JWT signing key initialized for use with HS512 algorithm.");
}
private String buildToken(Map<String, Object> extraClaims, UserDetails userDetails, long expiration) {
// ...
return Jwts.builder()
// ... other claims (subject, issuer, etc.)
.signWith(signInKey, SignatureAlgorithm.HS512) // Explicitly using HS512
.compact();
}
// ... rest of the class
}
package com.grambasket.userservice.security;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig {
@Value("${spring.security.oauth2.resourceserver.jwt.secret-key}")
private String jwtSecret;
// ... other beans and configs
@Bean
public JwtDecoder jwtDecoder() {
// Expecting HmacSHA512
SecretKeySpec secretKey = new SecretKeySpec(jwtSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA512");
log.info("Configuring JwtDecoder with HmacSHA512 algorithm.");
return NimbusJwtDecoder.withSecretKey(secretKey).build();
}
}
security:
oauth2:
resourceserver:
jwt:
secret-key: "mysupersecretkeymysupersecretkey12"
````
user-service/application.yml:
spring:
security:
oauth2:
resourceserver:
jwt:
secret-key: "mysupersecretkeymysupersecretkey12"
What I Have Tried
1.Algorithm Alignment:
I have triple-checked that both the signing (SignatureAlgorithm.HS512) and decoding ("HmacSHA512") configurations match.
2.Secret Key Verification: The secret key is identical in both services. I've copy-pasted it to be sure.
3.Key Length: I replaced an initial shorter key with a longer one (> 64 bytes) to satisfy the length requirement for HS512, but the error persists.
4.Clean Build: I have run mvn clean install -U and manually deleted my .m2 repository folders to rule out caching issues.
5.Public Endpoint Test: I created a public /ping endpoint in the user-service which works perfectly. The 401 error only occurs when the Authorization: Bearer <token> header is present, which isolates the problem to JWT validation.
Given that the algorithms and secret keys appear to be perfectly matched, what could be the underlying cause for the JwtAuthenticationProvider to fail with Another algorithm expected, or no matching key(s) found?Could there be a subtle conflict between the io.jsonwebtoken library used for signing and the com.nimbusds library used by Spring for decoding? Or could the non-GA dependency versions in my pom.xml be causing this unpredictable behavior?Any insight would be greatly appreciated.
Configure your user-service
correctly to expect HS512 tokens and use the provided 64-byte key for validation. Fix discrepancy between the required key length for HS512 algorithm and the key you're providing.
You need a key that is at least 64 bytes long. The best practice is to use a cryptographically random string and then Base64-encode it. Use this generated key in the application.yml
of both your auth-service
and user-service
.
Update your user-service/application.yml
with the new, 64-byte, Base64-encoded key and add jws-algorithms
property to explicitly tell Spring Security which algorithm to use for validation:
spring:
security:
oauth2:
resourceserver:
jwt:
secret-key: "bU9YUGlFcGZBYW14dElzckQ5QklmVEdqVk1SU1hJc1RGNUZpSmxLcUZkM0d2UHpocVd4M3g2a0JtY0VvVlZUeA=="
jws-algorithms: HS512
Ensure your auth-service/application.yml
uses the exact same new key:
spring:
security:
oauth2:
resourceserver:
jwt:
secret-key: "bU9YUGlFcGZBYW14dElzckQ5QklmVEdqVk1SU1hJc1RGNUZpSmxLcUZkM0d2UHpocVd4M3g2a0JtY0VvVlZUeA=="
Your user-service
has a manual JwtDecoder
bean. While this works you're fighting Spring Boot's auto-configuration. You can achieve the same result with zero custom code, which is the much more robust and reliable embedded auto-configuration mechanism.
Spring Security's resource server can be configured entirely from application.yml
. You just need to tell it which algorithm to expect.
Since Spring Boot has all the information it needs, you can completely delete your jwtDecoder
bean from your SecurityConfig
in user-service
. The framework will now automatically create correctly configured NimbusJwtDecoder
for you. The best code is often the code you don't have to write. Just let the framework do its job by proper configuration.
Your SecurityConfig
can be simplified:
@Configuration
@EnableWebSecurity
@Slf4j
public class SecurityConfig {
// No @Value for the secret is needed here anymore
// No jwtDecoder() bean is needed anymore!
// ... other beans and security configs (for example SecurityFilterChain bean)
}