✏️ [OAuth 2.0] Kakao 소셜 로그인을 이해하자

박상민·2025년 3월 14일
0

개념 정리!

목록 보기
21/22

⭐️ 서론

OAuth 2.0 기반 Kakao 소셜 로그인 서비스는 Kakao Developers 공식 문서에 잘 정리가 되어 있습니다. 이 글은 공식 문서를 나름대로의 방식으로 정리한 것입니다.

⭐️ 이해하기

애플리케이션 등록 과정은 생략

카카오 로그인은 다양한 서비스 개발 환경에서 손쉽게 사용할 수 있도록 REST API 뿐만 아니라 JavaScript, Android, iOS, Flutter 플랫폼용 Kakao SDK(SOftware Development Kit)를 제공한다.

로그인
로그인은 사용자가 자신을 인증해 서비스에 권한을 인가하는 절차이다. 로그인 완료 시 보안 데이터인 토큰을 서비스에 발급한다. 서비스는 토큰을 요청에 포함해 요청의 자격을 증명할 수 있다.

서비스 로그인 과정

  1. 사용자 클라이언트에서 사용자가 카카오 로그인 버튼을 선택하면, 서비스는 카카오 API 플랫폼 서버로 인가 코드 발급을 요청

  2. 카카오 API 플랫폼 서버는 사용자에게 인증을 요청하고, 성공 시 사용자에게 동의 화면으로 인가를 요청

  3. 인가 완료 후 카카오 API 플랫폼 서버는 인가 코드를 포함한 Redirect URI로 사용자를 리다이렉트

  4. 서비스가 Redirect URI에 포함된 인가 코드로 토큰 발급을 요청하면, 카카오 API 플랫폼은 사용자와 서비스 앱을 연결하고 서비스에 사용자의 토큰을 발급

회원 확인 및 등록

  1. 서비스가 발급받은 사용자의 토큰으로 사용자 정보 가져오기 API를 요청하면, 카카오 API 플랫폼에서 해당 사용자의 정보를 응답
  2. 서비스는 제공받은 사용자 정보로 사용자의 기존 회원 여부 절차를 수행, 결과에 따라 회원 등록/서비스 로그인 처리 실시

서비스 로그인

  1. 서비스 서버에서 사용자 클라이언트에 서비스 로그인 세션을 발급
  2. 사용자 클라이언트는 발급받은 세션으로 사용자를 로그인 완료 처리하고 로그인된 서비스 화면으로 이동

📌 토큰

토큰은 사용자의 인증과 권한 정보를 담은 문자열이다. 서비스는 인가 결과로 발급받은 인가 코드로 토큰 발급을 요청할 수 있고, 발급받은 토큰을 API 요청에 포함해서 기능 사용 권한이 있음을 증명한다.

카카오 로그인은 OAuth 2.0 표준 규격에 따라 액세스 토큰(Access token), 리프레시 토큰(Refresh token) 두 종류의 토큰을 발급한다. OpenID Connect를 활성화하면 ID 토큰을 추가로 발급받을 수 있다.

Supabase에서는 OpenID Connect 방식을 활성화해서 ID 토큰을 사용한다.

토큰별 역할과 만료 시간

📌 REST API를 사용한 로그인 구현 방법

REST API를 사용한 카카오 로그인 과정을 나타낸 시퀀스 다이어그램

인가 코드 받기

인가 코드 받기

  • GET Method
  • URL: https://kauth.kakao.com/oauth/authorize
  • OpenID Connect 활성화를 할 시 ID 토큰을 함께 발급받을 수 있는 인가 코드를 발급

[요청]
Quert Parameter: 일부만 정리

  • client_id: String, 필수
    • APP REST API Key
  • redirect_uri: String, 필수
    • 인가 코드를 전달받을 서비스 서버의 URI
  • response_type: String, 필수
    • code로 고정
  • scope: String
    • 추가 항목 동의 받기 요청 시 사용 사용자에게 동의 요청할 동의 항목 ID 목록
    • OpenID Connect를 사용하는 경우, openid를 반드시 포함
  • nonce: String
    • OpenID Connect로 ID 토큰을 함께 발급받을 경우, ID 토큰 재생 공격을 방지하기 위해 사용
    • ID 토큰 유효성 검증 시 대조할 임의의 문자열(정해진 형식 X)

[응답]
Query Parameter

  • code: Stirng
    • 토큰 받기 요청에 필요한 인가 코드
  • error: String
    • 인증 실패 시 반환되는 에러 코드
  • error_description: String
    • 인증 실패 시 반환되는 에러 메시지
  • state: String
    • 요청 시 전달한 state 값과 동일한 값

요청 예제

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

응답 예제

HTTP/1.1 302
Content-Length: 0
Location: ${REDIRECT_URI}?code=${AUTHORIZE_CODE}

서비스 서버는 기존에 설정한 redirect_uri로 HTTP 302 리다이렉트된 요청의 Location에서 인가 코드 또는 에러를 확인해 처리해야 한다.

  • 인가 코드 받기 요청 성공
    • 사용자가 모든 필수 동의항목에 동의하고 '동의하고 계속하기 버튼을 누른 경우'
    • codestate 전달
    • code의 인가 코드 값으로 토큰 받기 요청
  • 인가 코드 받기 요청 실패
    • 사용자가 동의 화면에서 '취소' 버튼을 눌러 로그인을 취소했거나, 다른 에러가 발생한 경우
    • error, error_description 전달
    • 에러 원인별 상황에 맞는 서비스 페이지나 안내 문구를 사용자에게 보여주도록 처리

토큰 받기

토큰 받기

  • POST Method
  • URL: https://kauth.kakao.com/oauth/token
  • 인가 코드로 토큰 발급을 요청, OpenID Connect를 사용하는 앱인 경우, 응답에 ID 토큰이 함께 포함

[요청]
Header

  • Content-Type: 필수
    • 요청 데이터 타입

Body

  • grant_type: String, 필수
    • authorization_code로 고정
  • client_id: String, 필수
    • APP REST API Key
  • redirect_uri: String, 필수
    • 인가 코드가 리다이렉트된 URI
  • code: String, 필수
    • 인가 코드 받기 요청으로 얻은 인가 코드
  • client_sceret: String
    • 토큰 발급 시, 보안을 강화하기 위해 추가 확인하는 코드

[응답]
Body

  • token_type: String, 필수
    • 토큰 타입, bearer로 고정
  • access_token: String, 필수
    • 사용자 액세스 토큰 값
  • id_token, String
    • ID 토큰 값
    • OpenID Connect 확장 기능으로 발급하는 ID 토큰
  • expires_in: Integer, 필수
    • 액세스 토큰과 ID 토큰의 만료 시간(초)
  • refresh_token: Stirng, 필수
    • 사용자 리프레시 토큰 값
  • refresh_token_expires_in: Integer, 필수
    • 리프레시 토큰 만료 시간(초)
  • score: String
    • 인증된 사용자의 정보 조회 권한 범위
    • OpenID Connect가 활성화 된 앱의 토큰 발급 요청인 경우, ID 토큰이 함께 발급되며 scope 값에 openid 포함

요청 예제

curl -v -X POST "https://kauth.kakao.com/oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded;charset=utf-8" \
    -d "grant_type=authorization_code" \
    -d "client_id=${REST_API_KEY}" \
    --data-urlencode "redirect_uri=${REDIRECT_URI}" \
    -d "code=${AUTHORIZE_CODE}"

응답 예제: ID 토큰 포함

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

ID 토큰 페이로드

{
  "aud": "${APP_KEY}",
  "sub": "${USER_ID}",
  "auth_time": 1661967952,
  "iss": "https://kauth.kakao.com",
  "exp": 1661967972,
  "iat": 1661967952,
  "nickname": "JordyTest",
  "picture": "http://yyy.kakao.com/.../img_110x110.jpg",
  "email": "jordy@kakao.com"
}

토큰 정보 보기

토큰 정보 보기

  • GET Method
  • URL: https://kapi.kakao.com/v1/user/access_token_info
  • 액세스 토큰의 유효성을 검증하거나 정보를 확인, 토큰 만료 여부나 유효기간을 알 수 있어 갱신 과정에 활용 가능

응답

  • code: -1, HTTP Status: 400
    • 카카오 서비스의 일시적 내부 장애 상태, 토큰을 강제 만료 또는 로그아웃 처리하지 않고 일시적인 장애 메시지로 처리 권장
  • code: -2, HTTP Status: 400
    • 필수 인자가 포함되지 않은 경우나 호출 인자값의 데이터 타입이 적절하지 않거나 허용된 범위를 벗어난 경우
  • code: -401, HTTP Status: 401
    • 유효하지 않은 앱키나 액세스 토큰으로 요청한 경우, 토큰 값이 잘못되었거나 만료되어 유효하지 않은 경우로 토큰 갱신 필요

[요청]
Header

  • Authorization: 필수
    • Authorization: Bearer ${ACCESS_TOKEN}

[응답]
Body

  • id: Long, 필수
    • 회원번호
  • expires_in: Integer, 필수
    • 액세스 토큰 만료 시간(초)
  • app_id: Integer, 필수
    • 토큰이 발급된 앱 ID

요청 예제

curl -v -G GET "https://kapi.kakao.com/v1/user/access_token_info" \
  -H "Authorization: Bearer ${ACCESS_TOKEN}"

응답 예제

HTTP/1.1 200 OK
{
    "id":123456789,
    "expires_in": 7199,
    "app_id":1234
}

토큰 갱신하기

토큰 갱신하기

  • POST Method
  • URL: https://kauth.kakao.com/oauth/token
  • 액세스 토큰과 리프레시 토큰을 갱신, 리프레시 토큰 값과 필수 파라미터를 담아 POST로 요청
  • 응답은 토큰 받기와 마찬가지로 JSON 객체로 전달, 응답 중 refresh_token 값은 요청 시 사용된 리프레시 토큰의 만료 시간이 1개월 미만으로 남았을 때만 갱신되어 전달된다.

[요청]
Header

  • Content-Type: 필수
    • 요청 데이터 타입

Body

  • grant_type: String, 필수
    • refresh_token으로 고정
  • client_id: String, 필수
    • APP REST API Key
  • refresh_token: String, 필수
    • 토큰 발급 시 응답으로 받은 refresh_token
    • Access Token을 갱신하기 위해 사용
  • client_sceret: String
    • 토큰 발급 시, 보안을 강화하기 위해 추가 확인하는 코드

[응답]
Body

  • token_type: String, 필수
    • 토큰 타입, bearer로 고정
  • access_token: String, 필수
    • 사용자 액세스 토큰 값
  • id_token, String
    • 갱신된 ID 토큰 값
    • OpenID Connect 확장 기능으로 발급하는 ID 토큰
  • expires_in: Integer, 필수
    • 액세스 토큰과 ID 토큰의 만료 시간(초)
  • refresh_token: Stirng, 필수
    • 사용자 리프레시 토큰 값
  • refresh_token_expires_in: Integer, 필수
    • 리프레시 토큰 만료 시간(초)

요청 예제

curl -v -X POST "https://kauth.kakao.com/oauth/token" \
    -H "Content-Type: application/x-www-form-urlencoded;charset=utf-8" \
    -d "grant_type=refresh_token" \
    -d "client_id=${REST_API_KEY}" \
    -d "refresh_token=${USER_REFRESH_TOKEN}"

응답 예제

HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
{
    "access_token":"${ACCESS_TOKEN}",
    "token_type":"bearer",
    "refresh_token":"${REFRESH_TOKEN}",  //optional
    "refresh_token_expires_in":5184000,  //optional
    "expires_in":43199,
}

사용자 정보 가져오기

사용자 정보 가져오기

  • GET/POST Method
  • URL: https://kapi.kakao.com/v2/user/me
  • 로그인한 사용자의 정보를 불러온다.
  • 사용자 액세스 토큰 또는 어드민 키를 헤더에 담아 GET 또는 POST로 요청한다.

[요청-액세스 토큰 방식]
Header

  • Authorization: 필수
    • Authorization: Bearer ${ACCESS_TOKEN}
  • Content_Type: 필수
    • 요청 데이터 타입

Query Parameter

  • secure_resource: Boolean
    • 이미지 URL 값 HTTPS 여부, true 설정 시 HTTPS 사용, 기본 값 false
  • property_keys: PropertyKeys[]
    • Property 키 목록, JSON Array를 ["kakao_account.email"]\과 같은 형식으로 사용

[응답]
Body

  • id: Long, 필수
    • 회원번호
  • kakao_account: KakaoAccount
    • 카카오계정 정보

KakaoAccount

  • profile: Profile
    • 프로필 정보
  • email: String
    • 카카오 계정 대표 이메일
  • gender: String
    • 성별(femail, male)

Profile

  • nickname: String
    • 닉네임
  • profile_image_url: String
    • 프로필 사진 URL

요청 예제: 액세스 토큰 방식으로 email 정보 받기

curl -v -X POST "https://kapi.kakao.com/v2/user/me" \
    -H "Content-Type: application/x-www-form-urlencoded;charset=utf-8" \
    -H "Authorization: Bearer ${ACCESS_TOKEN}" \
    --data-urlencode 'property_keys=["kakao_account.email"]'

응답 예제: 닉네임만 받는 경우

HTTP/1.1 200 OK
{
    "id":123456789,
    "connected_at": "2022-04-11T01:45:28Z",
    "kakao_account": { 
        "profile_nickname_needs_agreement": false,
        "profile": {
            "nickname": "홍길동"
        }
    },  
    "properties":{
        "${CUSTOM_PROPERTY_KEY}": "${CUSTOM_PROPERTY_VALUE}",
        ...
    }
}

📌 OpenID Connect(OIDC)

카카오 로그인은 OAuth 2.0 기반의 표준 인증 프로토콜인 OpenID Connect(OIDC)를 지원한다. OIDC를 서비스에 적용하면 사용자의 로그인을 더 안전하게 처리할 수 있다.

OIDC 설정 방법

  1. 메타데이터 확인하기
    OIDC: 메타데이터 확인하기 API
  • GET Method
  • https://kauth.kakao.com/.well-known/openid-configuration
  • 카카오 로그인의 OIDC 서비스 제공자 설정을 확인한다. 카카오 로그인은 OIDC Discovery 표준 규격에 따라 서비스 제공자 설정을 담은 메타데이터(Metadata) 문서를 제공한다.

[응답]
Body

  • issuer: String, 필수
    • 인증 기관 정보, https://kauth.kakao.com으로 고정
  • authorization_endpoint: String, 필수
    • 인가 코드 받기 요청 URL
  • token_endpoint: String, 필수
    • 토큰 받기 요청 URL
  • userinfo_endpoint: String, 필수
    • 사용자 정보 요청 URL
  • jwks_uri: String, 필수
    • OIDC: 공개키 목록 조회하기 요청 URL

... 등등 (자세한 응답값은 문서 확인)

요청 예제

curl -v -G GET "https://kauth.kakao.com/.well-known/openid-configuration"

응답 예제

HTTP/1.1 200
Content-Type: application/json;charset=utf-8
{
    "issuer": "https://kauth.kakao.com",
    "authorization_endpoint": "https://kauth.kakao.com/oauth/authorize",
    "token_endpoint": "https://kauth.kakao.com/oauth/token",
    "userinfo_endpoint": "https://kapi.kakao.com/v1/oidc/userinfo",
    "jwks_uri": "https://kauth.kakao.com/.well-known/jwks.json",
    "token_endpoint_auth_methods_supported": ["client_secret_post"],
    "subject_types_supported": ["public"],
    "id_token_signing_alg_values_supported": ["RS256"],
    "request_uri_parameter_supported": false,
    "response_types_supported": ["code"],
    "response_modes_supported": ["query"],
    "grant_types_supported": [
        "authorization_code", "refresh_token"
    ],
    "code_challenge_methods_supported": ["S256"],
    "claims_supported": [
        "iss",
        "aud",
        "sub",
        "auth_time",
        "exp",
        "iat",
        "nonce",
        "nickname",
        "picture",
        "email"
    ]
}
  1. 카카오 로그인 구현하기
    Open ID Connect를 활성화 후, 카카오 로그인을 구현한다.
    OIDC를 활성화한 앱은 사용자 인증 정보가 담긴 ID 토큰을 액세스 토큰과 함께 발급하고, OIDC: 사용자 정보 가져오기로 표준 규격을 준수한 형태의 사용자 정보를 제공받을 수 있다.

  2. ID 토큰 유효성 검증하기
    발급받은 ID 토큰은 서비스 보안을 위해 유효성을 검증하고 사용해야 한다. 페이로드 값 확인 시, 필요에 따라 디버깅 목적으로 제고아는 ID 토큰 정보 보기 API를 사용할 수 있다. 아래 순서로 ID 토큰을 검증한다.

  • 페이로드 검증
    1. ID 토큰의 영역 구분자인 온점(.)을 기준으로 헤더, 페이로드, 서명을 분리
    2. 페이로드를 Base64 방식으로 디코딩
    3. 페이로드의 키별 값 검증
    • iss: https://kauth.kakao.com와 일치해야 함
    • aud: 서비스 앱 키와 일치해야 함
    • exp: 현재 UNIX 타임스탬프보다 큰 값 필요
    • nonce: 카카오 로그인 요청 시 전달한 값과 일치해야 함
  • 서명 검증
    1. ID 토큰의 영역 구분자인 온점(.)을 기준으로 헤더, 페이로드, 서명을 분리
    2. 헤더를 Base64 방식으로 디코딩
    3. OIDC: 공개키 목록 조회하기 API로 카카오 인증 서버가 서명 시 사용하는 공개키 목록 조회
    4. 공개키 목록에서 헤더의 kid에 해당하는 공개키 값 확인
    • 공개키는 일정 기간 캐싱(Caching)하여 사용할 것을 권장하며, 지나치게 빈번한 요청 시 요청이 차단될 수 있으므로 유의
    1. JWT 서명 검증을 지원하는 라이브러리를 사용해 공개키로 서명 검증
    • 라이브러리를 사용하지 않고 직접 서명 검증 구현 시, RFC7515 규격에 따라 서명 검증 가능

ID 토큰

ID 토큰은 서비스의 로그인 세션 대신 사용할 수 있는 JWT 형식의 토큰이다. 서비스는 ID 토큰에 포함된 사용자 인증 정보를 서비스에 활용하거나, ID 토큰의 유효성을 검증할 수 있다. ID 토큰의 만료 시간은 액세스 토큰과 동일하다.

ID 토큰의 세 가지 영역

  • Header: ID 토큰의 규격 정보, 카카오 로그인으로 발급받는 ID 토큰의 헤더는 alg, typ, kid 3가지 정보를 포함함

    • alg: ID 토큰에 적용된 엄호화 방식 RS256으로 고정
    • typ: ID 토큰의 형식, JWT로 고정
    • kid: ID 토큰 암호화 시 사용된 공개키 ID
  • Payload: 사용자 인증 정보

    • iss: ID 토큰을 발급한 인증 기간 정보, https://kauth.kakao.com으로 고정
    • aud: ID 토큰이 발급된 앱의 앱 키
    • sub: ID 토큰에 해당하는 사용자의 회원번호
    • iat: ID 토큰 발급 또는 갱신 시각
    • auth_time: 사용자가 카카오 로그인으로 인증을 완료한 시간
    • exp: 만료 시간
    • nonce: 카카오 로그인 요청 시 전달받은 임의의 문자열
    • nickname: 닉네임
    • picture: 프로필 사진
    • email: 이메일
  • Signature: 카카오 인증 서버에서 kid에 해당하는 공개키로 서명한 값, RS256 방식으로 암호화된 값으로, ID 토큰 유효성 검증 시 서명

ID 토큰은 세 영역의 값을 Base64 인코딩 한 후 온점(.)으로 이어 붙인 하나의 문자열로 생성된다. 따라서 온점을 기준으로 각 영역을 분리하고, Base64 디코딩하여 내용을 확인할 수 있다. 페이로드와 서명의 검증 방법은 이전에 소개한 ID 토큰 유효성 검증하기를 따르면 된다.


출처
https://developers.kakao.com/docs/latest/ko/kakaologin/common

0개의 댓글

관련 채용 정보