Bluesky feed server - NSFW Likes

fix: Add custom logic for token refresh

The PDS responds with an HTTP 400 code when using an expired auth token.
This won't trigger Ktor's token refresh, so we add a custom interceptor
to detect this failure and manually refresh the tokens.

Changed files
+44
darkfeed
src
main
kotlin
+44
darkfeed/src/main/kotlin/api/BskyApi.kt
··· 79 79 } 80 80 }, 81 81 ) { 82 + init { 83 + httpClient.plugin(HttpSend).intercept { request -> 84 + val originalCall = execute(request) 85 + 86 + if (originalCall.response.status == HttpStatusCode.BadRequest) { 87 + val errorResponse = try { 88 + originalCall.response.body<ErrorResponse>() 89 + } catch (e: Exception) { 90 + null 91 + } 92 + 93 + if (errorResponse?.error == "ExpiredToken") { 94 + val currentRefreshToken = bearerTokens.lastOrNull()?.refreshToken ?: return@intercept originalCall 95 + 96 + @Serializable 97 + data class Response(val accessJwt: String, val refreshJwt: String) 98 + 99 + val refreshSessionResponse = httpClient.post("com.atproto.server.refreshSession") { 100 + header("Authorization", "Bearer $currentRefreshToken") 101 + } 102 + 103 + if (refreshSessionResponse.status == HttpStatusCode.OK) { 104 + val refreshSessionTokens = refreshSessionResponse.body<Response>() 105 + val newBearerTokens = 106 + BearerTokens(refreshSessionTokens.accessJwt, refreshSessionTokens.refreshJwt) 107 + 108 + bearerTokens.addLast(newBearerTokens) 109 + 110 + val newRequest = HttpRequestBuilder() 111 + newRequest.takeFrom(request) 112 + newRequest.headers { 113 + remove(HttpHeaders.Authorization) 114 + append(HttpHeaders.Authorization, "Bearer ${newBearerTokens.accessToken}") 115 + } 116 + 117 + return@intercept execute(newRequest) 118 + } 119 + } 120 + } 121 + 122 + originalCall 123 + } 124 + } 125 + 82 126 @Serializable 83 127 data class ErrorResponse( 84 128 val error: String,