Skip to content

[JWT 관련]

tthisag246 edited this page Aug 5, 2024 · 11 revisions

카카오 로그인/회원가입 플로우

  1. 카카오 인가 코드 받기

    1. PuangFilm ClientKakao Server 요청

      GET https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${REDIRECT_URI}&response_type=code&scope=openid
      

      ** 이때 Redirect URI는 PuangFilm Server 주소로 설정 (Client를 거치지 않고 Server로 바로 인가 코드를 전달하기 위함)

    2. 카카오계정 로그인 화면 출력

    3. 카카오계정 로그인 로그인

    4. 동의 화면 출력

    5. 동의하고 계속하기

    6. Kakao ServerPuangFilm Client 응답

      302 Found
      Location: ${REDIRECT_URI}?code=${KAKAO_AUTHORIZE_CODE}
      

      ** 이때 Redirect URI는 PuangFilm Server 주소 https://${PUANG_FILM_SERVER_URI}/auth/login/oauth/kakao 이므로, 자동으로 2-i 요청이 이루어짐

  2. 사용자 로그인 처리

    1. PuangFilm ClientPuangFilm Server 요청

      GET https://${PUANG_FILM_SERVER_URI}/auth/login/oauth/kakao?code=${KAKAO_AUTHORIZE_CODE}
      

      관련 코드) AuthController와 AuthService의 loginWithKakao()

    2. PuangFilm ServerKakao Server 요청

      POST https://kauth.kakao.com/oauth/token
      {
          "grant_type" : "authorization_code",
          "client_id" : "${CLIENT_ID}",
          "redirect_uri" : "${REDIRECT_URI}",
          "code" : "${KAKAO_AUTHORIZE_CODE}",
          "client_secret" : "${CLIENT_SECRET}"
      }
      

      카카오 토큰 받기 요청

      관련 코드) KakaoProvier의 getTokenByCode()

    3. Kakao ServerPuangFilm Server 응답

      200 OK
      {
          "token_type": "bearer",
          "access_token": "${ACCESS_TOKEN}",
          "id_token": "${ID_TOKEN}",
          "expires_in": 7199,
          "refresh_token": "${REFRESH_TOKEN}",
          "refresh_token_expires_in": 86399,
          "scope": "openid profile_nickname"
      }
      

      카카오 토큰 발급

      ** 이때 ID 토큰(id_token)은 JWT 형식의 토큰으로, ID 토큰의 페이로드는 다음 정보를 포함함

      iss: "https://kauth.kakao.com"
      aud: "${APP_KEY}"
      sub: "${USER_ID}" // kakaoId
      iat: 1661967952
      auth_time: 1661967952
      exp: 1661967972
      nickname: "JordyTest"
      

      ** 주의) 위 카카오 토큰의 access_token, refresh_token은 2-vi, 2-vii에서 PuangFilm Server가 발행하는 access_token, refresh_token과 다름. <푸앙이 사진관> 로그인 상태 유지에는 후자가 사용됨.

    4. ID 토큰 유효성 검증

      관련 코드) OIDCProvider의 verify()

      1. 페이로드 검증

        관련 코드) OIDCProvider의 verifyPayload()

        • isshttps://kauth.kakao.com와 일치하는지
        • aud가 2-b 요청 시 사용한 client_id와 일치하는지
        • exp가 현재 시각보다 이후인지
      2. 서명 검증

        관련 코드) OIDCProvider의 verifySignature()

        • 카카오 인증 서버로부터 받은 공개키 목록에서 ID 토큰의 헤더의 kid에 해당하는 공개키 찾기
          관련 코드) OIDCProvider의 getOIDCPublicKey()
          ** kid는 카카오 인증 서버가 ID 토큰 서명 시 사용한 공개키 ID
          ** 이때 공개키 목록은 매일 새벽 5시에 조회하여 캐싱하며, kid에 해당하는 공개키가 없을 시 카카오 인증 서버로 공개키 목록을 다시 조회하여 캐싱 업데이트.
          관련 코드) KakaoProvier의 getOIDCPublicKeyList()

        • 위에서 얻은 공개키는 JWK 형식. JWK 공개키의 n, e값으로 RSA 공개키 생성

          관련 코드) OIDCProvider의 getRSAPublicKey()

        • RSA 공개키로 서명 검증 후 sub(kakaoId), nickname 반환

    5. DB User 테이블 갱신

      기존 회원) UPDATE request_date

      신규 회원) INSERT

      ** 기존/신규 회원 여부는 kakaoId로 확인

    6. JWT 형식의 refresh_token 발행 및 DB Token 테이블 갱신

      refresh_token의 페이로드는 다음 정보를 포함함

      iss: "https://kauth.kakao.com"
      sub: "${KAKAO_ID}"
      iat: 1661967952 // 토큰 발급 또는 갱신 시각
      exp: 1661967972 // 토큰 만료 시간
      userName: "JordyTest"
      

      ** 유효 기간은 1일로 설정 (논의 후 변경 가능)

    7. JWT 형식의 access_token 발행

      access_token의 페이로드는 다음 정보를 포함함

      iss: "https://kauth.kakao.com"
      sub: "${KAKAO_ID}"
      iat: 1661967952 // 토큰 발급 또는 갱신 시각
      exp: 1661967972 // 토큰 만료 시간
      refreshId: "123" // refresh_token의 ID (Token 테이블의 id)
      

      ** 유효 기간은 30분으로 설정 (논의 후 변경 가능)

    8. PuangFilm ServerPuangFilm Client 응답

      200 OK
      {
          "access_token": "${ACCESS_TOKEN}"
      }
      

JWT

사용 방식

  • 로그인

    자세한 내용은 위 '카카오 로그인/회원가입 플로우' 참고

    • access_token(30분), refresh_token(1일) 발급
    • DB에 refresh_token 저장 (Token 테이블)
    • PuangFilm Client에 access_token 전달
  • 로그인된 사용자의 요청 - 토큰이 유효한 경우

    1. PuangFilm ClientPuangFilm Server 요청
      헤더

      Authorization: Bearer <access_token>
      
    2. access_token 검증 - 유효

      관련 코드) JwtProvider의 validateToken()

    3. 페이로드의 sub(kakaoId)을 통해 사용자 정보 취득 후, Argument Resolver 활용하여 User 객체를 만들어 바인딩 (이 부분은 Argument Resolver 구현 후 보완 예정)

  • 로그인된 사용자의 요청 - 토큰이 만료된 경우

    1. PuangFilm ClientPuangFilm Server 요청

      헤더

      Authorization: Bearer <access_token>
      
    2. access_token 검증 - 만료

      관련 코드) JwtProvider의 validateToken()

    3. PuangFilm ServerPuangFilm Client 응답

      401 Unauthorized
      
    4. PuangFilm ClientPuangFilm Server 요청

      GET https://${PUANG_FILM_SERVER_URI}/auth/reissue
      Authorization: Bearer <access_token>
      
    5. access_token의 페이로드에 있는 refreshId를 이용하여 refresh_token 얻음

      관련 코드) JwtProvider의 getRefreshIdFromExpiredToken()

    6. refresh_token 검증

      관련 코드) JwtProvider의 validateToken()

    7. reissue 처리

      1. refresh_token이 유효한 경우

        access_token, refresh_token 재발급 후 DB Token 테이블 업데이트

      2. refresh_token이 만료된 경우

        DB Token 테이블에서 해당 refresh_token 삭제

    8. PuangFilm ServerPuangFilm Client 응답

      1. refresh_token이 유효한 경우

        200 OK
        {
            "access_token": "${ACCESS_TOKEN}"
        }
        
      2. refresh_token이 만료된 경우

        401 Unauthorized
        

    ** wiki 작성 중 7번 과정이 미완성이었다는 점을 발견하여 이번주 내로 보완 예정

  • 로그아웃

    • 별도 구현 없이 토큰이 만료되면 자동으로 로그아웃되는 방식.
    • 로그아웃이 따로 없어 만료된 refresh_token이 DB에 쌓일 수 있음
      • 매일 새벽 5시에 DB 조회해서 만료된 refresh_token 삭제 (구현 예정)

구조

Access Token Payload

  • Issuer
  • Subject
    • KakaoId값
      • 카카오에서 제공하는 회원번호 (식별자 역할)
  • IssueAt
    • 토큰 발급 시각
  • Expiration
    • 토큰 만료 시각
  • RefreshId
    • refresh_token의 ID (DB Token 테이블의 id)
    • reissue에 필요

Refresh Token Payload

  • Issuer
  • Subject
  • IssueAt
  • Expiration
  • UserName
    • 사용자 닉네임

Refresh Token DB

  • refresh_token_id
    • access_token 페이로드에 포함된 RefreshId가 이 값을 사용
  • refresh_token
  • kakao_id
  • expiresAt
Clone this wiki locally