티스토리 뷰

SwiftUI

Swift - Apple, Google 로그인

DevDiana 2024. 7. 22. 11:05

안녕하세요 Diana 입니다!

오늘은 앱 개발에서 거의 핵심적인 기능인 소셜 로그인 중 Apple과 Google 로그인에 대해 알아보려고 합니다.

바로 시작하겠습니다.

 

 


 

소셜 로그인 - Apple

Apple 로그인은 외부 SDK가 아닌 Apple 자체에서 제공하는 AuthenticationService 라는 FirstParty 프레임워크로 제공됩니다.

FirstPary 프레임워크이기 때문에 별도 라이브러리 의존성이 필요하지 않고 인증 UI 또한 시스템에서 직접 제공해준다는 부분에서 굉장히 편리합니다.

 

그리고 Apple 로그인은 프라이버시를 중시하는 Apple의 설계 철학을 그대로 반영하고 있습니다.

Apple 로그인을 시도하면 이메일을 가릴 수 있으며 이 경우 앱에는 xxxx@pricaterelay.appleid.com 와 같은 암호화 된 이메일 주소가 제공됩니다. 앱은 진짜 이메일을 알 수 없으며 이로 인해 서버측에서 아이디를 매개로 하는 로직 실행이 불가능해지죠.

 

그리고 이름/이메일의 경우 최초 로그인 1회만 전달되며 이후부터는 nil로 전달됩니다.

Apple 입장에서는 이미 한번 제공한 정보를 또 제공할 이유가 없다는 입장에서 설정된 방식인데 우리 입장에서는 이후 아이디와 이메일을 사용할 일이 있는 경우 따로 안전하게 저장해두어야 한다는 과제가 생긴 샘입니다.

 

Apple 로그인의 구현은 Delegate 콜백으로 돌아옵니다.

ASAuthorizationControllerDelegate는 성공 실패를 메서드로 제공해주며 코드는 아래와 같습니다.

func authorizationController(controller: ASAuthorizationController,
                                didCompleteWithAuthorization authorization: ASAuthorization) {
        guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else {
            resume(with: .failure(AuthError.noCredential))
            return
        }
        resume(with: .success(credential))
}
 
func authorizationController(controller: ASAuthorizationController,
                                didCompleteWithError error: Error) {
        if let authError = error as? ASAuthorizationError, authError.code == .canceled {
            resume(with: .failure(AuthError.cancelled))
        } else {
            resume(with: .failure(error))
        }
}

 

만약 이를 Swift Concurrency로 구현하고자 하는 경우 중간에 withCheckedThrowingContinuation 등과 같은 브릿지가 필요합니다.

 

Apple 로그인으로 반환되는 것은 ASAuthorizationAppleIDCredential로 내부의 구성 요소는 user, identityToken(서버 검증용 JWT), authorizationCode , 최초 1회의 이름(fullName)/이메일(email) 등이 있습니다.

 

여기서 user은 userID가 아닌 앱을 삭제하더라도 안전하게 유지되는 UserIdentifier로 식별키가 되는 의미 없는 문자열이 제공되며 같은 개발팀 기준에서 동일 사용자에게는 같은 값이 제공됩니다.

(설정 -> Apple ID -> 로그인 기록 에서 앱을 제거하고 다시 로그인하지 않는 이상 user은 바뀌지 않음)

 

참고로 앱에서 소셜로그인을 제공하는 경우 Apple 정책 상 Apple 로그인 구현은 선택이 아닌 필수입니다.

Apple 로그인 없이 타 소셜 로그인을 구현하는 경우 리젝 사유가 됩니다.

 

 소셜 로그인 - Google

FirstParty 프레임워크인 Apple 로그인과 달리 Google 로그인은 검증된 외부 SDK가 인증을 해주는 형식입니다.

Google 로그인은 GooleSignIn을 SPM 또는 CocoaPods 로 추가해 사용합니다. Apple 과 반대로 서드파티 의존성이 생기지만 SDK가 OAuth2.0 흐름의 복잡한 부분을 처리해준다는 장점이 있습니다.

 

다른 특성으로는 OAuth 기반으로 인증과 인가 가 섞여 있다는 점 입니다.

Google은 idToken뿐 아니라 accessToken까지 제공해주며 로그인과 더불어 Google Drive와 Calendar 등의 기능을 사용할 수 있는 확장성을 가지고 있습니다.

 

그리고 Apple과 다르게 숨겨진 이메일 같은 개념 없이 프로필 정보(이메일, 이름, 사진 URL) 등을 그대로 제공해줍니다.

또한 signIn(withPresenting:)이 이미 async 메소드이기 때문에 Apple Login과 같이 브릿징을 필요로 하지 않습니다.

 

그럼 위의 설명에서 나왔던 JWT와 OAuth의 개념도 같이 확인해보도록 하겠습니다.

 

✅ JWT(JSON Web Token)

JWT의 Flow

 

로그인 시 우리는 서버에 "이 사용자는 로그인된 사람이다"를 매번 요청하게 되고 서버는 이를 확인하기 위해 DB에 세션을 저장해두고 조회합니다. JWT는 이를 뒤집어 토큰 자체에 정보를 담고 그 정보가 위조되지 않았음을 증명하게 만듭니다.

서버가 DB 조회 없이 토큰만 검증하면 되기 때문에 stateless 특성을 가지게 되며 이게 바로 JWT의 핵심 가치 입니다.

 

구조는 header.payload.signature 로 구성되어 있으며 각각 Base64URL 로 인코딩되어 있습니다.

 

Header은 어떤 알고리즘이 서명했는지에 대한 정보를 가지고 있으며 {"alg": "RS256", "typ": "JWT"} 와 같은 형식입니다.

Apple의 경우 identityTokendms RS256(RSA 서명)을 사용합니다.

 

Payload는 실제 담긴 정보를 나타내며 Apple IdentityToken의 경우 iss(발급자, https://appleid.apple.com), sub(subject, user 식별자), exp(만료 시각), email, nonce 같은 정보를 포함하고 있습니다.

 

Signature은 header과 payload를 합쳐서 발급자의 비밀키로 서명한 값입니다. 검증하는 쪽은 발급자의 공개키로 이 서명이 맞는지 확인하는 과정을 거칩니다.


여기서 확실히 해야하는 부분은 JWT는 암호화가 아닌 무결성을 보호하는 것이라는 겁니다. 내용을 바꾸게 되면 서명이 깨지기 때문에 비밀번호 같은 민감 정보가 포함되어 있으면 안됩니다.

 

서명 검증 원리는 아래와 같습니다. 우선 Apple이 비밀키를 가지고 있습니다. 이때 공격자가 email을 변경하여 서명을 다시 만들어야 하는데 이때 Apple이 들고 있는 비밀키가 필요하지만 공격자는 이를 알 방법이 없죠.

 

위에서 알아본 Apple의 identityToken이 바로 이 JWT 입니다. 앱이 이걸 서버로 보내면 서버는 Apple의 공개키로 서명을 검증하고 값들을 대조하며 정확한지를 확인하죠.

 

✅ OAuth 2.0

OAuth는 권한 위임 프로토콜로 비밀번호 없이 제 3자에게 내 리소스 일부에 대한 접근 권한을 부여하는 것 입니다.

 

예를 들어 어떤 앱이 사용자의 Google 연락처를 알고싶어한다고 생각해 봅시다. 옛날 방식이라면 해당 앱에 Google 비밀번호를 제공해줘야 했지만 이는 정말 위험한 방식입니다. OAuth는 이를 개선해 비밀번호가 아닌 "연락처 읽기 권한만 담긴 토큰(Access Token)"을 발급해줍니다.

 

사용되는 요소들은 아래와 같습니다.

 

  1. Resource Owner: 사용자, 리소스의 주인을 의미.
  2. Client: 권한을 원하는 앱을 의미.
  3. Authorization Server: 로그인을 시키고 토큰을 발급하는 서버(Google 계정 서버)를 의미.
  4. Resource Server: 실제 리소스를 가진 서버(Google 연락체 API)를 의미.

 

가장 표준적인 흐름은 Authorization Code Flow로 앱에서 Google 로그인을 눌렀을 때 아래와 같이 동작합니다.

 

  1. 앱이 사용자를 인증 서버 로그인 페이지로 보냄(브라우저/시스템 웹뷰).
  2. 사용자가 Google에 로그인하고 "이 앱에 프로필 접근 허용"에 동의.
  3. 인증 서버가 authorization code(임시 코드)를 앱으로 리다이렉트해서 돌려줍니다.
  4. 앱이 이 code를 다시 인증 서버로 보내서 access token(+ refresh token, + idToken)으로 교환.
  5. 앱이 access token으로 리소스 서버 API를 호출.

 

이 때, 토큰을 바로 안주고 code 를 거쳐 교환하는 이유는 code는 리다이렉트 URL(브라우저 히스토리, 로그 등)에 노출될 수 있는데 진짜 토큰은 백채널로만 오가게 하려는 보안 설계입니다.

앱의 경우 여기에 PKCE 라는 추가 방어(code를 가로채더라도 사용하지 못하게 하는 장치)를 얹는데 GoogleSignIn SDK에서 내부적으로 이를 처리해줍니다. 

 

마지막으로 두 종류의 토큰에 대해 알아보면

  • Access Token: 실제 API 호출에 사용되는 토큰이며 수명이 1시간 정도로 짧습니다. 토큰이 만료되면 401 에러가 발생합니다.
  • Refresh Token: Access Token이 만료됐을 때 재 로그인 없이 새로운 Access Token을 받아오는데 사용되는 토큰으로 수명이 길다는 특징을 가지고 있습니다. 

이렇게 오늘은 Apple, Google 소셜 로그인에 대해 알아봤습니다.

 

감사합니다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/08   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함