JWT and JWK authentication
Ktor supports JWT (JSON Web Tokens), which is a mechanism for authenticating JSON-encoded payloads.It is useful to create stateless authenticated APIs in the standard way, since there are client libraries for itin a myriad of languages.
This feature will handle Authorization: Bearer <JWT-TOKEN>
.
This feature is defined in the method io.ktor.auth.jwt.jwt
in the artifact io.ktor:ktor-auth-jwt:$ktor_version
.
dependencies { implementation "io.ktor:ktor-auth-jwt:$ktor_version"}
dependencies { implementation("io.ktor:ktor-auth-jwt:$ktor_version")}
<project> … <dependencies> <dependency> <groupId>io.ktor</groupId> <artifactId>ktor-auth-jwt</artifactId> <version>${ktor.version}</version> <scope>compile</scope> </dependency> </dependencies></project>
Ktor has a couple of classes to use the JWT Payload as Credential
or as Principal
.
class JWTCredential(val payload: Payload) : Credential
class JWTPrincipal(val payload: Payload) : Principal
Configuring server/routes:
JWT and JWK each have their own method with slightly different parameters. Both require the realm
parameter, which is used in the WWW-Authenticate response header.
Using a verifier and a validator:
The verifier will use the secret to verify the signature to trust the source.You can also check the payload within validate
callback to ensure everything is right and to produce a Principal.
application.conf:
jwt {
domain = "https://jwt-provider-domain/"
audience = "jwt-audience"
realm = "ktor sample app"
}
JWT auth:
val jwtIssuer = environment.config.property("jwt.domain").getString()
val jwtAudience = environment.config.property("jwt.audience").getString()
val jwtRealm = environment.config.property("jwt.realm").getString()
install(Authentication) {
jwt {
realm = jwtRealm
verifier(makeJwtVerifier(jwtIssuer), jwtIssuer)
validate { credential ->
if (credential.payload.audience.contains(jwtAudience)) JWTPrincipal(credential.payload) else null
}
}
}
Using a JWK provider:
fun AuthenticationPipeline.jwtAuthentication(jwkProvider: JwkProvider, issuer: String, realm: String, validate: (JWTCredential) -> Principal?)
val jwkIssuer = "https://jwt-provider-domain/"
val jwkRealm = "ktor jwt auth test"
val jwkProvider = JwkProviderBuilder(jwkIssuer)
.cached(10, 24, TimeUnit.HOURS)
.rateLimited(10, 1, TimeUnit.MINUTES)
.build()
install(Authentication) {
jwt {
verifier(jwkProvider, jwkIssuer)
realm = jwkRealm
validate { credentials ->
if (credentials.payload.audience.contains(audience)) JWTPrincipal(credentials.payload) else null
}
}
}