@@ -21,6 +21,7 @@ import io.ktor.server.auth.oauth
2121import io.ktor.server.auth.principal
2222import io.ktor.server.response.respond
2323import io.ktor.server.routing.Routing
24+ import io.ktor.server.routing.RoutingContext
2425import io.ktor.server.routing.get
2526import io.ktor.server.routing.post
2627import io.ktor.server.routing.routing
@@ -37,14 +38,15 @@ import no.java.cupcake.slack.SlackService
3738import no.java.cupcake.slack.SlackUser
3839import java.time.ZonedDateTime
3940import java.util.Date
41+ import kotlin.time.Duration
4042import kotlin.time.Duration.Companion.hours
4143import kotlin.time.Duration.Companion.minutes
4244import kotlin.time.Duration.Companion.seconds
4345
4446private const val SLACK_AUTH = " slack-oauth"
4547const val JWT_AUTH = " jwt-oauth"
4648
47- private val idTokenLifetime = 8 .hours.inWholeMilliseconds
49+ private val idTokenLifetime = 8 .hours
4850
4951private fun Map <String , Claim >.str (
5052 key : String ,
@@ -75,7 +77,7 @@ fun buildIdToken(
7577 .withClaim(" name" , userInfo.name)
7678 .withClaim(" email" , userInfo.email)
7779 .withClaim(" avatar" , userInfo.avatar)
78- .withExpiresAt(Date (System .currentTimeMillis() + idTokenLifetime))
80+ .withExpiresAt(Date (System .currentTimeMillis() + idTokenLifetime.inWholeMilliseconds ))
7981 .sign(Algorithm .HMAC256 (jwtConfig.secret))
8082
8183fun buildAccessToken (
@@ -112,10 +114,10 @@ fun buildRefreshToken(
112114 ),
113115 ).sign(Algorithm .HMAC256 (jwtConfig.secret))
114116
115- private fun ZonedDateTime.cookieExpiry (seconds : Long ) =
117+ private fun ZonedDateTime.cookieExpiry (duration : Duration ) =
116118 GMTDate (
117119 this
118- .plusSeconds(seconds )
120+ .plusSeconds(duration.inWholeSeconds )
119121 .toEpochSecond()
120122 .seconds.inWholeMilliseconds,
121123 )
@@ -174,6 +176,29 @@ fun Application.configureSecurity(
174176 }
175177}
176178
179+ private fun RoutingContext.setSessionCookies (
180+ jwtConfig : JwtConfig ,
181+ slackId : String ,
182+ ) {
183+ val now = ZonedDateTime .now()
184+ val accessToken = buildAccessToken(jwtConfig = jwtConfig, slackId = slackId)
185+ val refreshToken = buildRefreshToken(jwtConfig = jwtConfig, slackId = slackId)
186+
187+ call.response.cookies.append(
188+ name = " access_token" ,
189+ value = accessToken,
190+ path = " /" ,
191+ expires = now.cookieExpiry(jwtConfig.accessTokenLifetimeMinutes.minutes),
192+ )
193+ call.response.cookies.append(
194+ name = " refresh_token" ,
195+ value = refreshToken,
196+ path = " /" ,
197+ httpOnly = true ,
198+ expires = now.cookieExpiry(jwtConfig.refreshTokenLifetimeMinutes.minutes),
199+ )
200+ }
201+
177202private fun Routing.configureAuthRouting (
178203 slackService : SlackService ,
179204 redirect : String ,
@@ -214,32 +239,16 @@ private fun Routing.configureAuthRouting(
214239 }
215240
216241 val user = claims.toSlackUser(userId, true )
217- val now = ZonedDateTime .now()
218-
219- val idToken = buildIdToken(jwtConfig = jwtConfig, userInfo = user)
220- val accessToken = buildAccessToken(jwtConfig = jwtConfig, slackId = userId)
221- val refreshToken = buildRefreshToken(jwtConfig = jwtConfig, slackId = userId)
222242
223243 call.response.cookies.append(
224244 name = " id_token" ,
225- value = idToken,
226- path = " /" ,
227- expires = now.cookieExpiry(idTokenLifetime / 1000 ),
228- )
229- call.response.cookies.append(
230- name = " access_token" ,
231- value = accessToken,
245+ value = buildIdToken(jwtConfig = jwtConfig, userInfo = user),
232246 path = " /" ,
233- expires = now.cookieExpiry(jwtConfig.accessTokenLifetimeMinutes * 60 ),
234- )
235- call.response.cookies.append(
236- name = " refresh_token" ,
237- value = refreshToken,
238- path = " /" ,
239- httpOnly = true ,
240- expires = now.cookieExpiry(jwtConfig.refreshTokenLifetimeMinutes * 60 ),
247+ expires = ZonedDateTime .now().cookieExpiry(idTokenLifetime),
241248 )
242249
250+ setSessionCookies(jwtConfig, userId)
251+
243252 // Expire old user_session cookie
244253 call.response.cookies.append(
245254 name = " user_session" ,
@@ -253,6 +262,13 @@ private fun Routing.configureAuthRouting(
253262 }
254263 }
255264
265+ refreshRouting(jwtVerifier, jwtConfig)
266+ }
267+
268+ private fun Routing.refreshRouting (
269+ jwtVerifier : JWTVerifier ,
270+ jwtConfig : JwtConfig ,
271+ ) {
256272 post(" /refresh" ) {
257273 val refreshCookie = call.request.cookies[" refresh_token" ]
258274
@@ -271,23 +287,7 @@ private fun Routing.configureAuthRouting(
271287 return @post
272288 }
273289
274- val now = ZonedDateTime .now()
275- val newAccessToken = buildAccessToken(jwtConfig = jwtConfig, slackId = slackId)
276- val newRefreshToken = buildRefreshToken(jwtConfig = jwtConfig, slackId = slackId)
277-
278- call.response.cookies.append(
279- name = " access_token" ,
280- value = newAccessToken,
281- path = " /" ,
282- expires = now.cookieExpiry(jwtConfig.accessTokenLifetimeMinutes * 60 ),
283- )
284- call.response.cookies.append(
285- name = " refresh_token" ,
286- value = newRefreshToken,
287- path = " /" ,
288- httpOnly = true ,
289- expires = now.cookieExpiry(jwtConfig.refreshTokenLifetimeMinutes * 60 ),
290- )
290+ setSessionCookies(jwtConfig, slackId)
291291
292292 call.respond(HttpStatusCode .OK , mapOf (" status" to " refreshed" ))
293293 } catch (_: Exception ) {
0 commit comments