Reputation: 770
I have following spring boot app with minimal configuration
application.properties
server.port=8081
spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/auth/realms/master
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.nurgasemetey</groupId>
<artifactId>springboot-keycloak</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-keycloak</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
controller
@RestController
@RequestMapping("/users")
public class UsersController {
@GetMapping("/status/check")
public String status(@AuthenticationPrincipal Jwt principal) {
return "working";
}
}
It seems that Spring Boot Oauth2 doesn't use public key, as I see in code:
OAuth2ResourceServerProperties
/**
* JSON Web Key URI to use to verify the JWT token.
*/
private String jwkSetUri;
/**
* JSON Web Algorithm used for verifying the digital signatures.
*/
private String jwsAlgorithm = "RS256";
/**
* URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0
* Authorization Server Metadata endpoint defined by RFC 8414.
*/
private String issuerUri;
/**
* Location of the file containing the public key used to verify a JWT.
*/
private Resource publicKeyLocation;
But I didn't give publicKeyLocation
, but app can verify without public key.
Under the hood it uses JwtIssuerValidator
and JwtTimestampValidator
validators.
On other hand, with express-jwt
, it requires public key for offline verification
const express = require('express');
const jwt = require('express-jwt');
const app = express();
const secret = 'secret';
const fs = require('fs');
var publicKey = fs.readFileSync('public.pub');
app.get('/protected', jwt({ secret: publicKey, algorithms: ['RS256'] }), (req, res) => {
res.send('protected');
})
app.listen(3000, () => console.log('server started'));
How the Spring Boot Oauth verifies without public key?
Upvotes: 1
Views: 4467
Reputation: 770
Self answer.
Firstly, it seems that http://localhost:8080/auth/realms/master
exposes public key. As said in this Generate JWT Token in Keycloak and get public key to verify the JWT token on a third party platform - Stack Overflow and in this comment to this question by @Thomas Kåsene
Secondly, I digged spring boot oauth2 code and stumbled to this code in
ReactiveOAuth2ResourceServerJwkConfiguration
@Bean
@Conditional(IssuerUriCondition.class)
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
}
JwtDecoderProviderConfigurationUtils
private static Map<String, Object> getConfiguration(String issuer, URI... uris) {
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " +
"\"" + issuer + "\"";
for (URI uri : uris) {
try {
RequestEntity<Void> request = RequestEntity.get(uri).build();
ResponseEntity<Map<String, Object>> response = rest.exchange(request, typeReference);
Map<String, Object> configuration = response.getBody();
if (configuration.get("jwks_uri") == null) {
throw new IllegalArgumentException("The public JWK set URI must not be null");
}
return configuration;
} catch (IllegalArgumentException e) {
throw e;
} catch (RuntimeException e) {
if (!(e instanceof HttpClientErrorException &&
((HttpClientErrorException) e).getStatusCode().is4xxClientError())) {
throw new IllegalArgumentException(errorMessage, e);
}
// else try another endpoint
}
}
throw new IllegalArgumentException(errorMessage);
}
which seems to fetch public key from issuer-uri
given in application.properties
. After it fetched it verifies jwt tokens with fetched public key.
To test
Close your jwt provider, keycloak in my case and run spring boot application, then it gives
Caused by: java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "http://localhost:8080/auth/realms/master"
Upvotes: 1