hey, I created a jwttoken, and the token is expire...
# ktor
k
hey, I created a jwttoken, and the token is expired, so when GET the backend url, the server throws an error
Copy code
com.auth0.jwt.exceptions.TokenExpiredException: The Token has expired on 2024-01-16T08:48:32Z.
I want to know how can I return a json response instead of throwing an error
t
hi, StatusPages allows you to map specific exceptions to specific responses
k
ok, I got it
hey, it seems not working, here is the stacktrace
Copy code
2024-01-16 19:22:57.999 [DefaultDispatcher-worker-5] TRACE io.ktor.server.auth.Authentication - Trying to authenticate / with auth-jwt
2024-01-16 19:22:58.050 [DefaultDispatcher-worker-5] TRACE io.ktor.auth.jwt - Token verification failed
com.auth0.jwt.exceptions.TokenExpiredException: The Token has expired on 2024-01-16T08:48:32Z.
	at com.auth0.jwt.JWTVerifier$BaseVerification.assertValidInstantClaim(JWTVerifier.java:346)
	at com.auth0.jwt.JWTVerifier$BaseVerification.lambda$addMandatoryClaimChecks$17(JWTVerifier.java:308)
and this is the code
Copy code
install(StatusPages) {
        exception(TokenExpiredException::class) { call, _ ->
            call.respond(Response.Err("token expired"))
        }
    }
where is the error of my code ?
t
you can also have a look at the minimal status pages example from the documentation: https://github.com/ktorio/ktor-documentation/tree/2.3.7/codeSnippets/snippets/status-pages
k
eh, does statuspage just catch the error from routing ?
t
thats the main usecase yes, “when GET the backend url” sounds a lot like routing 😅 your authentication setup is also something that is happening inside the routing, right?
k
nope, I am still looking for where the error throws, please hang on
eh, the error is from
Copy code
authenticate("auth-jwt") {
            get("/") {
                val principal = call.principal<UserIdPrincipal>()
                if (principal == null) {
                    call.respond(HttpStatusCode.Unauthorized, Response.Err("not login"))
                } else {
                    val username = principal.name
                    call.respond(Response.Ok("get ok", "you are $username"))
                }
            }
        }
but I can't catch the error by StatusPages
t
can you change the exception you are catching to throwable (exceptionThrowable)? just to be sure you are looking at the correct exception… this should catch everything
k
really ? actually I have written this code
Copy code
install(StatusPages) {
        exception<Throwable> { call, cause ->
            call.respond(Response.Err("Fuck"))
        }

        status(HttpStatusCode.NotFound) { call, status ->
            call.respondText(text = "404: Page Not Found", status = status)
        }

        exception<TokenExpiredException> { call, cause ->
            call.respond(HttpStatusCode.Unauthorized, Response.Err(cause.message ?: "Fuck"))
        }
    }
but still not work
if you don't mind, talk is cheap, can you check the code, it is very simple, don't worry 😋
t
do you have multiple modules? your authentication plugin seems to work, but it seems like your StatusPages plugin is not applied at all 🤔
k
yes, but actually the StatusPages works well on this code
Copy code
fun Application.configureException() {
    install(StatusPages) {
        exception<Throwable> { call, cause ->
            call.respond(Response.Err("Fuck"))
        }

        status(HttpStatusCode.NotFound) { call, status ->
            call.respondText(text = "404: Page Not Found", status = status)
        }

        exception<TokenExpiredException> { call, cause ->
            call.respond(HttpStatusCode.Unauthorized, Response.Err(cause.message ?: "Fuck"))
        }
    }

    routing {
        get("/error/") {
            call.respondText("Hello, world!")
        }
        get("/error/internal-error") {
            throw Exception("Internal Server Error")
        }
        get("/error/authorization-error") {
            throw Exception("Forbidden Error")
        }
        get("/error/authentication-error") {
            call.respond(HttpStatusCode.Unauthorized)
        }
        get("/error/payment-error") {
            call.respond(HttpStatusCode.PaymentRequired)
        }
    }
}
t
i am busy right now, i might check your zip later 🤞
k
thank you, I will be waitting for your response 🙂
t
your StatusPages work perfectly fine (e.g. seen when using a path that is not routed) seems like the authentication part is not throwing an exception, the easiest fix would be to configure StatusPages for the Unauthorized Http Code
Copy code
status(HttpStatusCode.Unauthorized) { call, status ->
    call.respondText(text = "401: Unauthorized!", status = status)
}
If you need more control i think using challenge in the authentication and throwing the exceptions yourself would be an option, as seen here
k
thanks, I choose using challenge
by the way, can I think the challenge as the interceptor of validate ?
I think the TokenExpiredException is not thrown, but printed and catched, right ? so I can't catch this error
t
hm i see it more as a different behaviour than the default in case the authentication fails
k
may be I should make a issue of ktor, anyway thanks for your help
k
ok