개발

22년 6월 30일 이후 앱 심사 시 Sign in with Apple 적용 대상 앱에 회원탈퇴 시 revoke api 적용 지침 가이드

소소ing 2022. 6. 28. 14:53
반응형

[별첨]

아래 글에 앞서 Sign in with Apple 연동 방법이 궁금한 분들의 경우 자세히 나와 있는 아래 링크로 대신합니다.

또한 아래 글에서 client_secret을 생성하는 부분에 대해서 자세히 알고 싶은 분들은 아래 글 안 관련 부분을 보시면 됩니다. 

 

What the Heck is Sign In with Apple?

Sign In with Apple is based on OAuth 2.0 and OpenID Connect, and provides a privacy-friendly way for users to sign in to websites and apps

developer.okta.com

 

[본문]

Sign in with Apple로 회원가입 및 로그인 기능을 적용한 앱이라면 6월 30일 이후 앱 업데이트 시 애플에서 제공하는 revoke api를 이용하여 토큰을 만료 시켜야 합니다.

 

Account deletion requirement starts June 30 - Latest News - Apple Developer

As a reminder, apps that support account creation must let users initiate deletion of their account within the app starting June 30, 2022, as described in App Store Review Guideline 5.1.1(v). This deadline was extended to provide additional time for imp

developer.apple.com

 

이 부분은 앱 개발자와 서버 개발자 모두 협업이 필요한 부분으로 절차를 정리해 보았습니다. 

여기서는 Sign in with Apple이 적용된 상태로 가정하고 진행합니다.

 

1. Sign in with Apple로 로그인을 하게 되면 ASAuthorizationControllerDelegate 안 func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) 에서 유저 관련 정보를 전달 받게 됩니다.

아래 코드를 살펴보자. 현재 적용된 서비스에 맞게 확인해 보면 좋을듯 합니다.

case 1. 기존에 email만 전달을 했다면.. 추가로 User Code에 해당되는 String(decoding: appleIDCredential.authorizationCode!, as: UTF8.self) 값을 서버로 전달해야 합니다.

case 2. 기존에 서버로 identityToken과 code를 모두 전달하여 서버에서 사용자 정보를 확인하게 구성했다면 이 부분에서 해줄것은 없습니다.

// Authorization Succeeded
    @available(iOS 13.0, *)
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        if let appleIDCredential = authorization.credential as? ASAuthorizationAppleIDCredential {
            // Get user data with Apple ID credentitial
            let userId = appleIDCredential.user
            let userFirstName = appleIDCredential.fullName?.givenName
            let userLastName = appleIDCredential.fullName?.familyName
            let userEmail = appleIDCredential.email
            
            print("User ID: \(userId)")
            print("User First Name: \(userFirstName ?? "")")
            print("User Last Name: \(userLastName ?? "")")
            print("User Email: \(userEmail ?? "")")
            print("User Code: \(String(decoding: appleIDCredential.authorizationCode!, as: UTF8.self))")
            print("User token: \(String(decoding: appleIDCredential.identityToken!, as: UTF8.self))")
        } 
    }

 

 

2. 1번 과정이 끝이 났다면 서버 개발자에게 추가 정보를 전달해 주어야 한다. 이 추가 정보란 서버개발자가 애플 API로 통신을 하기 위해 필요한 파라미터에 해당됩니다.

<1번 과정에서 case 1>에 해당될 경우 https://appleid.apple.com/auth/token 통신을 하지 않았기 때문에 이 통신에 필요한 파라미터를 전달해 주어야 합니다.

애플 공식 문서 

 

Apple Developer Documentation

 

developer.apple.com

파라미터에 해당되는 데이터를 보면 

- client_id : 이 값은 App ID 즉 Team ID라고 보면 편합니다. 이 값은 https://developer.apple.com 사이트로 접속 후 로그인 하여 Account => Membership 안에서 확인이 가능합니다. 

 

- client_secret : 이 값은 https://developer.apple.com 사이트로 접속 후 로그인 하여 Account => Certificates, Identifiers & Profiles => keys 로 이동 후 Sign in with Apple 사용을 위해 만든 키를 다운로드(.p8 확장자) 및 Key ID 값도 전달 합니다. (1번 다운로드 후 재 다운로드가 되지 않기 때문에 키를 잘 가지고 있어야 합니다.)

(참고) Team ID의 경우 Key 상세 화면 CONFIGURATION 부분 앞 부분에 표시가 되어 있기 때문에 여기서 한번에 확인도 가능합니다. 

실제 서버 개발자에서 전달된 정보를 가지고 client_secret 값을 생성하게 됩니다. 

aClientSecret가 이 파라미터 변수에 해당되며, sKey는 .p8을 String 값으로 불러온 데이터 이며, sKeyId는 앞서 전달한 Key ID, sTeamId는 client_id 값에 해당됩니다. 이 3가지 데이터를 혼합하여 알고리즘화 해서 생성되는 값 입니다. 

<!-- 서버 개발자 파트 -->
$aClientSecret = $this->apple_client->createClientSecret($aIosInfos['sKey'], $aIosInfos['sKeyId'], $aIosInfos['sTeamId']);
$this->apple_client->setClientSecret($aClientSecret);

 

- code : 이 값은 1번 단계에서 서버로 전달하는 코드에 해당되는 값입니다. 

- grant_type : 이 값은 authorization_code 모드와 refresh_token 모드로 사용할 수 있으며, refresh_token이 있다면 refresh_token으로 access_token을 만들 수 있으나 이 작업과는 무관하기 때문에 authorization_code로 넣어 줍니다. 

 

이후 https://appleid.apple.com/auth/token을 POST 방식으로 통신 하여 리턴되는 데이터 중 refresh_token 값을 저장합니다. 

<1번 과정에서 case 2에 해당될 경우> 이 작업이 선행되어 있다고 생각이 되며, https://appleid.apple.com/auth/token 이 통신을 유저 정보를 가져오기 위해 선행하고 있기 때문에 여기서에 리턴되는 refresh_token 값을 저장해 줍니다. 

 

(질문) https://appleid.apple.com/auth/token에서 access_token이 아니라 refresh_token을 저장하는 이유는 무엇인가요?

access_token의 경우 만료 기간이 정해져 있기 때문에 회원탈퇴 액션이 발생되기까지 유지되기가 힘들기 때문입니다.

refresh_token의 경우 만료 기간이 정해져 있지 않고, revoke api 통신 시 access_token과 마찬가지고 사용이 가능하기 때문입니다. 

 

 

3.  1번과 2번 과정의 경우 유저가 로그인 또는 회원가입 시 이루어 지는 절차 입니다. 이후 사용자가 회원탈퇴 액션을 실행 시키면 서버에서는 애플에서 제공하는 https://appleid.apple.com/auth/revoke api를 동작시켜 토큰을 만료 시켜야 합니다. 

이때 필요한 파라미터들은....

애플 공식 문서 

 

Apple Developer Documentation

 

developer.apple.com

- client_id, client_secret : 2번의 client_id, client_secret 와 동일한 값입니다. 

- token : 2번 과정에서 얻은 refresh_token 값을 넣어 줍니다. 

- token_type_hint : refresh_token과 access_token 중 택 1일이기 때문에 사용하는 토큰 종류를 넣어주면 됩니다. 여기서는 refresh_token 이라고 넣어 줍니다. 

 

파라미터를 모두 기입하고 POST로 https://appleid.apple.com/auth/revoke  통신 후 정상 통신을 받게 되면 Sign in with Apple 토큰 만료가 완료 되었습니다. 

 

 

4. 정상적으로 revoke api가 동작했는지 확인은 어떻게 할까요?

앱에서 다시 Sign in with Apple로 로그인 및 회원가입 시도를 해보면 뜨는 화면에서 확인이 가능합니다. 

단, 해당 단말기에서 한번이라도 Sign in with Apple로 앱에 로그인을 했다고 가정합니다. 

- revoke api 정상 통신 시 아래와 같이 처음 Sign in with Apple을 요청했을 때 화면이 다시 뜨게 됩니다. 

(이 화면은 사용자가 아이폰 설정 => 계정 => 암호 및 보안 => Apple ID를 사용하는 앱 => 앱 목록 중 앱 선택 후 => Apple ID 사용 중단 을 한 것과 같은 효과로 생각 됩니다.)

- revoke api 통신이 제대로 되지 않을 경우 아래와 같은 화면으로 노출이 됩니다. 

 

 

반응형