상세 컨텐츠

본문 제목

[Swift] OAuth 인증 완벽 가이드: 안전한 앱 연동의 모든 것

iOS/Swift

by kimrindev 2025. 5. 28. 19:55

본문

OAuth가 뭔가요?

OAuth는 다른 앱이 사용자의 비밀번호를 몰라도, 사용자의 정보를 안전하게 가져올 수 있도록 만든 권한 위임(authorization delegation) 방식이에요.


용어 정리

OAuth를 이해하기 위해 먼저 핵심 용어들을 정리해보겠습니다:

  • Authentication (인증): "이 사람 맞아?" — 사용자 로그인 검증
  • Authorization (권한 부여): "이 사람이 이 기능까지 해도 돼?" — 범위 제한
  • Access Token: "로그인 완료 + 권한 확인"을 담은 열쇠 같은 토큰
  • REST API: 서버와 클라이언트 간의 약속된 데이터 요청/응답 방식 (HTTP 기반)
  • 사용자(User): 정보를 소유한 사람
  • 클라이언트 앱(Client): 정보를 가져오려는 제3자 앱
  • 인증 서버(Auth Server): 로그인과 토큰 발급 처리 (ex. GitHub, Google)
  • 리소스 서버(Resource Server): 실제 데이터를 가지고 있는 서버 (API 서버)
  • OAuth: 비밀번호 없이 인증을 위임받는 구조
  • Scope: 접근 가능한 데이터의 범위 제한
  • Token: 인증과 권한이 합쳐진 안전한 열쇠

 


OAuth가 생긴 이유

(Before OAuth):

  • 카카오톡이 페이스북 친구를 가져오고 싶을 때
    • 사용자에게 "Facebook 비밀번호 알려줘요"
    • 사용자 비밀번호를 제3자 앱이 보관함 → 보안 위협
    • 카카오톡이 내 페이스북 비밀번호를 알게 됨!

(After OAuth):

  • 카카오톡: "Facebook에서 로그인하고 권한만 주세요"
  • Facebook: "이 앱이 친구목록만 보게 할까요?"
  • 사용자: "응, 허용할게요"
  • Facebook: "그럼 이 토큰(token)만 줘서 친구목록만 접근하세요"

OAuth 사용의 보안적 이점

1. 사용자 인증 위임 (User Authentication Delegation)

OAuth를 사용하면, 사용자 인증(Authentication) 과정은 OAuth 제공자(GitHub, Google 등)가 담당하게 됩니다.

이로 인해 클라이언트 애플리케이션(Client Application)은 사용자의 인증 자격 증명(Credentials, 예: ID/Password)을 직접 수집하거나 저장할 필요가 없습니다.

 

✅ 보안 효과:

  • 자격 증명 유출 위험(Credential Leakage Risk) 최소화
  • 신뢰할 수 없는 앱에 비밀번호 노출 방지

2. 접근 권한 범위 제한 (Granular Access Control via Scope)

OAuth 요청 시, 클라이언트는 권한 범위(Scope)를 명시하여 사용자의 자원(Resource)에 대해 세분화된 접근 제어를 요청합니다.

예시:

  • read:user → 사용자 기본 프로필만
  • repo → 공개/비공개 저장소 읽기/쓰기 가능

✅ 보안 효과:

  • 최소 권한 원칙(Principle of Least Privilege) 준수
  • 클라이언트 앱이 사용자 전체 정보에 무분별하게 접근하는 것 방지

3. 제한된 접근 토큰 발급 (Limited-Lifetime Access Token)

OAuth 인증 후, 서버는 Access Token을 발급합니다.

이 토큰은 다음 특성을 가집니다:

  • 만료 시간(Expiration Time) 또는 사용 제한 조건
  • 특정 Scope에 국한된 접근 권한
  • 인증된 사용자 계정과 클라이언트 ID에 묶여 있음

✅ 보안 효과:

  • 인증 토큰 탈취(Token Hijacking) 피해를 줄임
  • 장기간 노출 시 위험한 Persistent Credential 대신 단기 토큰 사용

요약하면, OAuth를 도입함으로써 클라이언트 애플리케이션은 사용자 비밀번호를 직접 다루지 않고, 제한된 범위 내에서 단기 유효한 액세스 토큰을 통해 신뢰성 있는 인증 및 권한 위임 절차를 수행할 수 있습니다.


앱에서 진행하는 OAuth 인증 과정

1. 인증 요청 (GitHub 로그인 창 열기)

클라이언트 앱이 GitHub에게 "로그인 화면을 띄워줘"라고 요청합니다.

이때 다음 정보를 포함시켜야 합니다:

  • client_id: GitHub에 등록된 앱의 ID
  • redirect_uri: 인증 후 돌아올 앱의 URL Scheme
  • scope: 요청할 데이터 권한 범위
  • state: CSRF 공격 방지를 위한 랜덤 문자열 (선택)
 

 

let authURL = "https://github.com/login/oauth/authorize?client_id=abc123&scope=read:user&redirect_uri=letsgitit://callback"
UIApplication.shared.open(URL(string: authURL)!)

 

→ 이제 사용자는 GitHub 로그인 화면을 보고 로그인합니다.


2. 로그인 성공 시 콜백 URL로 돌아옴

  • 사용자가 로그인하고 "허용"을 누르면 GitHub은 redirect_uri로 우리 앱을 열어줍니다
  • URL Scheme을 이용해서 앱으로 돌아오게 됩니다

3. 인증 코드(code)를 Access Token으로 교환

받은 code를 가지고 GitHub에게 토큰 발급을 요청합니다:

  • client_id: GitHub에 등록된 앱의 ID
  • client_secret: 앱의 비밀키
  • code: 콜백 URL에서 받은 인증 코드
  • redirect_uri: 일치해야 함 (optional)

OAuth 인증 앱 연동 Flow 예시

✅ 예: OAuth 인증 앱 연동 flow

  1. 우리 앱이 URL Scheme으로 인증 앱 호출 → kakaotalk://oauth?client_id=abc&state=xyz
  2. 인증 앱에서 로그인하고 결과 반환 → myapp://callback?code=xyz123&state=xyz
  3. ⚠️ 우리 앱에서는 이 code를 곧바로 신뢰하지 않음
  4. GitHub / 카카오 서버에 이 code를 보내 Access Token을 받아야 진짜 인증 완료됨

이 과정에서 서버는 다음을 확인합니다:

  • 이 code는 유효한지?
  • 원래 발급된 client_id와 일치하는지?
  • state값이 원래 보낸 요청과 맞는지?

✅ 즉, code가 탈취당해도 쓸모 없습니다 (비밀키, client_secret, state 모두 없으면 무용지물)

 


Code → Access Token 교환 과정

바로 그 "code → Access Token" 교환 과정OAuth 보안의 핵심입니다.

우리 앱이 code와 함께 돌아오고 그 code와 함께 앱에서:

 
POST https://github.com/login/oauth/access_token
Content-Type: application/json

{
  "client_id": "내 앱의 ID",
  "client_secret": "내 앱의 비밀키",
  "code": "xyz123",
  "redirect_uri": "myapp://callback"
}

이렇게 client app의 정보를 포함한 내용을 인증서버에서 검증합니다.


검증 항목

항목 검증 내용
code 유효성 아직 만료되지 않았는지, 이미 사용된 건 아닌지
client_id 일치 여부 이 code가 어떤 앱(client)에게 발급된 건지 확인
client_secret 이 요청이 정말 등록된 앱이 맞는지 확인
redirect_uri OAuth 요청 시 등록한 주소와 일치하는지 검증
state (옵션이지만 강력 추천) 요청 시 보낸 값과 응답 시 받은 값이 일치하는지로 위조 공격 방지 (CSRF)

모든 항목이 일치한다면 Access Token을 발급합니다:


{
  "access_token": "gho_abcd1234567890",
  "token_type": "Bearer",
  "scope": "read:user"
}

이제 이 Access Token으로 진짜 API 호출이 진행됩니다.

그래서 code는 단지 "1회용 교환권"일 뿐, 진짜 자격증(Access Token)은 서버가 발급합니다.

OAuth 인증이란 결국 "Access Token을 안전하게 받아내는 절차"이고, 그 Token을 가지고만 API에 접근할 수 있는 구조입니다.

 


Redirect URI의 중요성

Redirect URI는 OAuth 인증이 끝난 후 사용자를 되돌려보낼 주소입니다.

  • 인증 URL 요청 시 포함됩니다
  • GitHub에 앱 등록할 때도 미리 정의합니다
  • 이 주소로 code가 함께 전달됩니다

redirect_uri 고정 및 검증이 중요한 이유

악성 앱이 code를 자기 URI로 받아가게 설정한다면?

  • 사용자가 로그인하면 인증 코드는 공격자에게 전달
  • 이후 공격자는 그 코드를 이용해 access token을 발급받을 수 있음 (물론 client_secret도 필요하긴 하지만, web client처럼 노출된 환경에서는 충분히 가능)

그래서 OAuth 앱 등록 시 redirect_uri를 미리 고정 등록해 두고, 실제 요청 시 받은 값과 완전 일치해야만 토큰 발급을 허용합니다.

쉽게 비유하면, 티켓을 받기 위해 줄을 서는데, 티켓이 발급되면 어디로 배달할지 주소를 적어야 하잖아요? 그 주소가 redirect_uri입니다.


 

토큰의 저장

왜 Access Token을 저장해야 하나요?

앱에서 자동 로그인을 구현하려면:

  • OAuth 로그인이 끝나면 Access Token이 발급됩니다
  • 이 Access Token을 앱 내에 안전하게 보관해두면
  • 앱을 다시 켤 때마다 다시 로그인하지 않아도 자동 로그인이 가능합니다!

👉 토큰 저장 = 로그인 상태 유지


UserDefaults vs Keychain

UserDefaults는 보안이 취약한 이유로 Keychain을 사용합니다

Keychain이란?

  • Apple에서 제공하는 보안 저장소
  • 민감한 데이터를 암호화해서 OS 차원에서 안전하게 저장
  • 암호/토큰/지문인증 같은 보안 자산을 보관하기에 최적화

간단한 비유:

  • UserDefaults = 누구나 열 수 있는 서랍
  • Keychain = 잠금장치 있는 금고

사용자 입장에서는 Keychain이나 UserDefaults나 차이를 거의 느끼지 못하지만, 다만 내부 동작 구조와 보안 레벨은 완전히 다릅니다.

 


실제 동작 흐름

로그인 버튼을 눌렀을 때

1. 사용자: "로그인" 버튼 클릭
2. 앱: Safari로 GitHub 로그인 페이지 열기
3. 사용자: GitHub에서 ID/PW 입력
4. GitHub: "LetsGitIt 앱이 접근해도 될까요?"
5. 사용자: "허용" 클릭
6. GitHub: 앱으로 돌아가기 (코드와 함께)
7. 앱: 받은 코드로 진짜 토큰 요청
8. GitHub: 토큰 발급
9. 앱: 토큰 저장하고 메인 화면으로

프로필 화면에서 데이터 로딩

1. 프로필 화면 열림
2. 앱: "저장된 토큰으로 사용자 정보 주세요"
3. GitHub: JSON 데이터 응답
4. 앱: JSON → Swift 객체 변환
5. 앱: UI에 데이터 표시 (이름, 아바타 등)

 

 

사실 되게 간단하게 구현되어 있을것이라고 생각했습니다. 

사실 이런부분은 실제 사용하면서 보였던 부분이라 좀더 재미있게 다가왔습니다.

평소에 아무생각없이 쓰던 소셜 로그인이나 인증 처리를 할때 이런처리가 있었구나 라는 느낌이 신선했습니다.

실제론 제가 정리 한것보다 더 디테일한 부분이 있을거라고는 생각합니다. 

그래도 이런 과정이 있었다는것이 스스로 사고를 할수있는 힘을 길러주는것은 아닐까 싶습니다. 

 

'iOS > Swift' 카테고리의 다른 글

[Swift] URL Scheme: iOS 앱 간 연결의 핵심 기술  (0) 2025.05.29
[Swift] Data Race  (0) 2025.05.27
[Swift] CoreML  (3) 2024.12.25

관련글 더보기