Skip to content
isdnetworks
Go back

다루지 않은 토큰 만료 시점

(수정됨)

현장 장비 에이전트의 인증을 JWT 로 바꾸고 잘 돌다가 한 시간 뒤에 요청이 전부 401 로 실패했다. 한 번 받은 토큰을 계속 쓰고 있었고 그 토큰에는 exp 가 들어 있었다.

Table of contents

Open Table of contents

원인 — 만료를 안 다룬 첫 구현

로그인해서 받은 값을 Authorization: Bearer 헤더에 넣고 그대로 두는 코드였다. 토큰의 페이로드를 열어 보니 iat 와 exp 가 있었다.

RFC 7519 는 exp 이후의 토큰을 처리에 받아들이면 안 된다고 정한다. 인증이 성공했다는 것과 그 인증이 계속 유효하다는 것은 다르다. 토큰 방식으로 옮기면서 그 차이를 코드에 반영하지 않은 것이 원인이었다.

해 본 것 — 401을 받고 다시 하는 구조

401 을 받으면 다시 로그인하고 한 번 더 보내게 고쳤더니 돌아가기는 했다. 다만 만료 때마다 반드시 한 번은 실패하고 다시 하는 구조였다.

동시에 여러 요청이 나가면 전부 그 응답을 받고 전부 각자 다시 로그인한다. 실패를 신호로 삼는 구조는 동시성이 붙으면 그 신호가 배로 증폭된다.

조치 — 미리 갱신하고 동시 갱신을 막는다

만료 조금 전에 미리 새 토큰을 받는 쪽을 골랐다. 이유는 401 을 아예 안 만나는 것이 실패 뒤 재시도보다 단순해서다. 갱신 구간에 synchronized 잠금을 걸어 여러 스레드가 동시에 로그인하지 않게 했다.

잠금을 안 걸면 만료 직후에 로그인 요청이 한꺼번에 몰린다. 대신 갱신 타이머가 도는 동안은 토큰을 한 번 더 받는 요청이 늘어나는 것을 감수했다. 실패한 뒤에 반응하는 대신 만료되기 전에 움직이는 것이 이 문제의 형태에 맞는 대응이었다.

막힌 지점 — 가정한 만료와 받은 만료

처음에는 만료를 한 시간으로 가정해서 3600 을 코드에 넣었다. 서버가 만료 시간을 바꾸면 그 순간부터 안 맞는다.

응답에 expires_in 이 들어 있어서 그것을 쓰고 없으면 페이로드의 exp 에서 뽑아 쓰게 했다. 장비 시계와 서버 시계가 다를 수 있으므로 만료 판단에 여유를 뒀는데 RFC 7519 도 시계 차이를 위해 몇 분 이내의 여유를 둘 수 있다고 적는다.

주의 — 갱신 실패의 두 종류

갱신 자체가 실패할 때를 자격 증명 오류와 일시 오류로 나눴다. 앞의 것은 재시도해도 같은 결과이므로 바로 중단하고 알린다.

자격 증명이 틀린 채로 계속 재시도하면 계정이 잠기는데 실제로 그런 일이 있었다. refresh_token 을 쓸지는 어디서 도는지에 달렸고 장비 안의 에이전트라 비밀 값을 안전하게 둘 수 있어서 단순한 쪽으로 갔다.

디버그 로그가 헤더를 통째로 찍고 있어서 토큰이 로그에 남는 것도 그때 고쳤다. Authorization 헤더를 가리게 하고 지난 로그에 남은 것도 찾아 지웠다.

정리


Share this post on:

Previous Post
다음 사람에게 남기는 것
Next Post
양쪽을 다 읽는 이관