Ktor-OpenAPI-Generator
Ktor-OpenAPI-Generator copied to clipboard
JWT authorization
Good afternoon, please help me implement JWT authorization. Old authorization doesn't work for apiRouting.
An implementation that works in Route
install(Authentication) {
jwt {
verifier(JWTService.verifier)
validate { credential ->
JWTPrincipal(credential.payload)
}
}
}
And the implementation, unfortunately, which is written in link is not clear to me. Please tell me how to implement ?
The basic ktor implementation is not strongly typed and this lib requires strongly typed routes due to the init time analysis of the routes. You need to implement the auth handler class like in the link, there is no way around it and i don't know how to make it clearer than in link. There could be an out of the box implementation for jwt but there isn't one yet. Maybe ask the community if they are willing to make a pull request for that, i heard a few falk about using jwt recently in PRs.
We've managed to get JWT authentication working. I don't really have time to clean it up right now, so this will contain a lot of references to our system, but in case it helps:
First, this function is registered with Ktor:
fun jwt(provider: Authentication.Configuration) {
provider.apply {
jwt(name = "user") {
realm = authSettings.jwtRealm
verifier(getJwkProvider(authSettings.jwtEndpoint), authSettings.jwtIssuer)
validate { credentials ->
UserPrincipal( //UserPrincipal is a data class inheriting from io.ktor.auth.Principal
userId = credentials.payload.subject,
email = credentials.payload.claims[ClaimTypes.EMAIL]?.asString(),
name = credentials.payload.claims[ClaimTypes.NAME]?.asString(),
roles = listOf()
)
}
}
}
}
private fun getJwkProvider(jwkEndpoint: String): JwkProvider {
return JwkProviderBuilder(URL(jwkEndpoint))
.cached(10, 24, TimeUnit.HOURS)
.rateLimited(10, 1, TimeUnit.MINUTES)
.build()
}
Then this code is basically what we needed:
val authProvider = JwtProvider()
inline fun NormalOpenAPIRoute.auth(route: OpenAPIAuthenticatedRoute<UserPrincipal>.() -> Unit): OpenAPIAuthenticatedRoute<UserPrincipal> {
val authenticatedKtorRoute = this.ktorRoute.authenticate("user", "admin") { }
val openAPIAuthenticatedRoute= OpenAPIAuthenticatedRoute(authenticatedKtorRoute, this.provider.child(), authProvider = authProvider)
return openAPIAuthenticatedRoute.apply {
route()
}
}
data class UserPrincipal(
val userId: String,
val email: String?,
val name: String?,
val roles: List<String>
) : Principal
class JwtProvider : AuthProvider<UserPrincipal> {
override val security: Iterable<Iterable<Security<*>>> =
listOf(listOf(Security(SecuritySchemeModel(
SecuritySchemeType.http,
scheme = HttpSecurityScheme.bearer,
bearerFormat = "JWT",
name = "jwtAuth"),
emptyList<Scopes>())))
override suspend fun getAuth(pipeline: PipelineContext<Unit, ApplicationCall>): UserPrincipal {
return pipeline.context.authentication.principal() ?: throw RuntimeException("No JWTPrincipal")
}
override fun apply(route: NormalOpenAPIRoute): OpenAPIAuthenticatedRoute<UserPrincipal> {
val authenticatedKtorRoute = route.ktorRoute.authenticate { }
return OpenAPIAuthenticatedRoute(authenticatedKtorRoute, route.provider.child(), this)
}
}
enum class Scopes(override val description: String) : Described
I forgot one thing - in addition to the above, we also needed this workaround: https://github.com/papsign/Ktor-OpenAPI-Generator/issues/98
Any plans to put a generic JWTAuthProvider into this library? Help needed?
Any plans to put a generic JWTAuthProvider into this library? Help needed?
If you want to do a PR i will gladly accept it.
If I find a good way to make sigmanils example generic, I'll make a PR.
@Wicpar I created a new provider as you mention, but how can i show in swagger-ui? Here is my apiRouting: `apiRouting { tag(object : APITag { override val description: String get() = "User Methods" override val name: String get() = "User"
}) {
val jwtAuthProvider = JwtProvider()
auth(jwtAuthProvider) {
route("/user") {
get<AuthHeader, List<User>>(
info(
summary = "Get all users",
description = "Return a list of all users"
),
status(HttpStatusCode.OK),
example = listOf(User(1, "emir", "1234"), User(2, "emirhan", "5678")),
) {
respond(listOf(User(1, "emir", "1234"), User(2, "emirhan", "5678")))
}
}
}
}
}`