Skip to content

로그인, 로그아웃 흐름 정리

ggyool edited this page Oct 5, 2021 · 4 revisions

로그인 흐름 정리 (/login/{socialType}}

AccessToken: 1시간, RefreshToken: 7일이라고 가정

1. 유저가 로그인을 시도한다.

a. 소셜로그인을 선택하는 창이 뜨고 소셜 로그인을 한다. 
b. 성공시 redirect 방식으로 code 를 받는데 프론트에서는 그 code를 가지고 /login/{socialType} 으로 요청한다.
c. code는 일회용인데 서버에서는 이 code를 가지고 깃헙, 구글 같은 소셜에게 api 를 날려 유저 정보에 접근할 수 있는 AccessToken 을 받음
d. 서버에서는 받은 AccessToken 을 가지고 유저 정보를 받아오고 서비스에서 이용함 (아마 30분쯤 유효할듯)
(유저정보에는 소셜에서 관리하는 SocailId 가 있어, 보또보 DB 에 해당 소셜 사용자가 있는지 검사하면 보또보의 회원인지 알 수 있음)
e. 이미 서비스를 이용하던 사용자라면 보또보 DB 에서 정보를 빼오고, 뉴비라면 DB에 정보를 넣어준다.
f. 보또보 DB에는 유저를 관리하는 정수 UserId 값이 있는데 그 값을 이용하여 보또보의 AccessToken 과 RefreshToekn을 발급한다.
g. 요청에 대한 응답으로 프론트에게 토큰을 넘겨준다.
AccessToken 은 body로 넘겨주고
RefreshToken 은 쿠키에 넘겨준다. (ex. BTOKEN_REFRESH=jwt.refresh.token)

서버에서 쿠키를 추가할 때 HttpOnly, Secure 설정을 해준다.
HttpOnly: javascript의 document.cookie 로 접근 불가
Secure: HTTPS 가 아니면 쿠키를 전달하지 않음 (localhost는 예외)

Refresh 토큰은 따로 규격이 없는 것 같아서, 쿠키에 담기로 정했고 XSS 로 값을 빼가는 것을 막기 위해 HttpOnly 를 넣었고,
WireShark 같은 툴로 가로채는 것을 막기 위해 Secure 옵션을 넣었음.

암튼 정리하면 Refresh Token은 백엔드단에서 쿠키에 넣어주고 보안적인 설정도 해두었음
프론트에서 credentials=true 설정만 해주면 다음 요청의 쿠키에 포함됨

2. 한 시간 동안 룰루랄라 서비스를 이용한다.

프론트에서 로그인 시 받은 AccessToken을  인증이 필요한 요청시 함께 보낸다. (Authorization 헤더에 AccessToken을 넣어서 요청)
백엔드에서는 올바른 토큰인지 검증 후 요청에 맞는 응답을 내려준다.

3. 한 시간 후 AccessToken 만료 후 /Workbook/1 이라는 인증이 필요한 요청을 보내는 상황

서버에서 아래 순서로 검증을 진행한다.
a. AccessToken 이 만료됨을 확인하고, 쿠키를 확인하여 RefreshToken 을 꺼내온다.
b. 꺼내온 RefreshToken이 올바른지 검증한다.
    - 올바른 RefreshToken 이라면 토큰 재발급을 유도하는 예외를 던진다. (code: A008, message: 액세스 토큰 재발급이 필요합니다.)
        -> 프론트에서 해당 예외를 핸들링하여 /token 으로 요청해야함
    - 올바르지 않다면 로그인을 유도하는 예외를 던진다. (code: A001, message: 토큰이 유효하지 않습니다.)
	-> 다시 로그인하게 만들어야함 (1번으로 돌아가도록)

4. AccessToken을 재발급 받기 위해 /token 으로 요청한다.

AccessToken만 재발급 하면 되지만 우리는 RefreshToken 도 재발급한다.
로그인과 마찬가지로 AccessToken 은 body 에 RefreshToken 은 쿠키에 담아서 프론트로 넘겨준다.

RefreshToken을 재발급하는 장점은 서비스를 계속 이용한다고 가정하면 로그인이 풀리지 않는다.
토큰의 유효기간인 7일 안에 요청을 한 번이라도 하면 재발급 받기 때문에 7일 이라는 시간도 갱신되기 때문
또한 보안적으로도 재발급 하는 방향이 좋다고 생각함

5. AccessToken 과 RefreshTokken을 재발급 받은 상황

새로운 토큰으로 3번의 /workbook/1 을 다시 한번 요청한다.



로그아웃 흐름 정리 (/logout)

기존 AccessToken만 있을 때는 따로 로그아웃 api 가 필요 없었음. 프론트에서 안 보내면 되기 때문. 
하지만 RefreshToken이 생기면서 필요해짐 서버에서는 RefreshToken을 Redis라는 인메모리 DB에 넣어서 관리하고 있음 (빠른 디비).

디비에서 관리하는 이유:

RefreshToken과 AccessToken 둘 다 jwt로 만드는데, 서버측에서 아무데도 저장하지 않는 AceessToken과는 달리 
RefreshToken은 데이터베이스 (우리의 경우 Redis) 에 HashMap<UserId, RefreshToken> 형식으로 저장함.

우리는 RefreshToken이 만료되지 않았음에도 새로운 환경에서 로그인을 하면 
재발급 하는 구조이기 때문에 JWT 기준에서는 유효한 토큰이 여러개 존재할 수 있음.

따라서 유저 한 명당 가장 최근에 발급한 하나의 RefreshToken만 redis에 저장함. 
즉, 어떤 유저의 RefreshToken이 redis에 있는데 새로운 환경에서 로그인을 하면 
그 때 새로 발급 받은 RefreshToken으로 redis 안에 있는 RefreshToken을 대체함.

/logout api에 RefreshToken을 담은 쿠키와 함께 요청을 보내면 다음 두 가지 경우가 존재한다

1. RefreshToken이 유효한 경우 
    - redis에서 유저의 최신 RefreshToken 정보를 삭제한다 
    - 유저에게 새로운 쿠키를 set-cookie 해준다. 이 새로운 쿠키안의 RefreshToken은 값이 없고, 쿠키의 MaxAge 는 0 이다
    - no-content로 응답한다 
2. RefreshToken이 유효하지 않은 경우 
    - 유저에게 새로운 쿠키를 set-cookie 해준다. 이 새로운 쿠키안의 RefreshToken은 값이 없고, 쿠키의 MaxAge 는 0 이다 
    - no-content로 응답한다
Clone this wiki locally