Add the following dependency to the build file
dependencies {
...
implementation("io.hndrs:jwt-auth-spring-boot-starter:1.0.0")
...
}
Adding the issuer and the jwks path for the verification to the application.properties
hndrs.jwt.key-store-path=https://domain.auth0.com/.well-known/jwks.json
To inject the claimSet into a RestController
method just use the @Identity
annotation on the parameter
@GetMapping("/user")
fun getUser(@Identity claimSet: Map<String, Any>): Map<String, Any> {
// do something with the user claimSet
return claimSet
}
By default the jwt token will be resolved from the Authorization
Header in the following
format Bearer <jwt_token>
. To resolve the token from another header or in a different format a bean implementing
the RequestTokenResolver
interface can be used.
Resolving token from Header x-custom-header: Token <jwt_token>
@Bean
fun requestTokenResolver(): RequestTokenResolver {
return object : RequestTokenResolver {
override fun tokenHeaderName(): String {
return "x-custom-header"
}
override fun tokenResolver(headerValue: String?): String {
if (headerValue == null) {
throw UnauthorizedIdentityException("${tokenHeaderName()} Header not present")
}
if (!headerValue.startsWith("Token ")) {
throw UnauthorizedIdentityException("Token is not present")
}
return headerValue.replace("Token ", "")
}
}
}
By default the claimSet is represented as a Map<String, Any>
to enrich or transform the map into a typed object a
bean implementing the ClaimSetTransformer
interface can be used.
Transforming claimSet to a CustomUser object
data class CustomUser(val id: String, val name: String, val email: String)
@Bean
fun claimSetTransformer(): ClaimSetTransformer {
return object : ClaimSetTransformer {
override fun transform(claimSet: Map<String, Any>): Any {
return CustomUser(
claimSet["sub"] as String,
claimSet["name"] as String,
claimSet["email"] as String,
)
}
}
}
// transformed object
@GetMapping("/user")
fun getUser(@Identity user: CustomUser): CustomUser {
// do something with the user claimSet
return user
}
Loading a user object
interface UserRepository : MongoRepository<String, DatabaseUser>
@Component
class UserLoadingClaimSetTransformer(
private val userRepository: UserRepository
) : ClaimSetTransformer {
override fun transform(claimSet: Map<String, Any>): Any {
return userRepository.findById(claimSet["sub"] as String)
}
}
// transformed object
@GetMapping("/user")
fun getUser(@Identity user: DatabaseUser): CustomUser {
// do something with the user claimSet
return user
}