개요

Apple 사용자로 로그인을 다른 팀으로 마이그레이션하는 방법을 설명한다. Apple 로그인 기능이 포함된 앱의 경우 다른 팀으로 이전해야할 경우 사용자 정보를 포함해서 이전해야 한다. ios 앱을 다른 계정으로 이전하는 방법은 이전에 작성한 글1에서 확인할 수 있다.

로그인 정보를 다른 팀으로 이전

client_secret 생성

기존 팀과 이전할 팀의 client_secret을 모두 생성한다.

require 'jwt'

key_file = 'key.p8'
team_id = 'TeamID'
client_id = 'AppID'
key_id = 'KeyID'

ecdsa_key = OpenSSL::PKey::EC.new IO.read key_file

headers = {
 'kid' => key_id
}

claims = {
'iss' => team_id,
'iat' => Time.now.to_i,
'exp' => Time.now.to_i + 86400*180,
'aud' => 'https://appleid.apple.com',
'sub' => client_id,
}

token = JWT.encode claims, ecdsa_key, 'ES256', headers

puts token

access_token 획득

기존 팀의 client_id(앱의 번들 아이디)와 client_secret를 이용해 access_token을 획득한다. grant_typescope는 수정 없이 아래 예시의 내용과 같게 요청한다.

  • client_id: 기존 팀에서 사용 했던 client_id
  • client_secret: 기존 팀의 client_secret

요청

POST /auth/token HTTP/1.1
Host: appleid.apple.com
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=user.migration&client_id={client_id}&client_secret={client_secret}

결과

결과로 수신한 access_token를 잘 기록해둔다.

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "access_token":"adg6105ed7a4de...0.nx.20LreF67Or9",
  "token_type":"Bearer",
  "expires_in":3600
}

전송 식별자 생성

이제 위에서 수신한 access_token를 이용하여 transfer_sub를 생성한다. 헤더에 바로 위에서 수신한 access_token를 전송하는 것을 참고하자.

  • sub: 기존 사용자의 Provider Id
  • recipient_team_id: 수신할 새로운 팀의 아이디
  • client_id: 기존 팀에서 사용 했던 client_id
  • client_secret: 기존 팀의 client_secret

요청

POST /auth/usermigrationinfo HTTP/1.1
Host: appleid.apple.com
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {access_token}

sub={sub}&target={recipient_team_id}&client_id={client_id}&client_secret={client_secret}

결과

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "transfer_sub":"760417.ebbf1c...ba852d492d8a.1827"
}

교환 식별자 생성

전송 식별자인 transfer_sub를 이용하여 교환 식별자를 생성한다. 위에서 수신한 access_token를 동일하게 계속 사용한다.

  • client_id: 새로운 팀의 client_id
  • client_secret: 새로운 팀의 client_secret

요청

POST /auth/usermigrationinfo HTTP/1.1
Host: appleid.apple.com
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {access_token}

transfer_sub={transfer_sub}&client_id={client_id}&client_secret={client_secret}

결과

수신한 교환 식별자 sub를 이용하여 데이터베이스의 provider Id 업데이트하여 이전을 최종 마무리 한다.

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache

{
  "sub":"820417.faa325...2d492d8a.0219",
  "email": "ep9...nph@privaterelay.appleid.com",
  "is_private_email" : true
}

참고로 이 단계는 소유자로 부터 이전된 앱에서만 응답 결과가 성공하고 그렇지 않은 경우 아래 처럼 invalid_client의 결과가 수신된다. provider Id 토큰은 소유 이전 날짜로부터 최대 60일 동안 유효하므로 기간내 처리되어야 한다2.

{
  "error": "invalid_client"
}

결론

앱이 정상적인 이전 절차가 진행될 경우 위에서 설명한 방법으로 provider Id를 새로운 팀으로 이전할 수 있었다.

하지만 내가 경험한 것은 특이한 상황으로 잘못된 설정으로 앱은 A팀이 소유하지만, 키 파일은 B팀에 있는 경우였고, 이 경우 위와 같은 방법으로는 이전이 불가능 하였다.

네이티브로만 구현된 앱은 client_id이 다르게 등록된 경우가 불가능 하지만, 네이티브 앱과 웹뷰가 혼합되어 있고 애플 아이디의 연동 부분이 웹뷰에 있는 경우 실수로 인해 잘못된 세팅을 하는 경우 앱과 다른 팀의 client_id가 사용될 수 있었다. A팀은 키 파일만 존재, B팀은 앱만 존재하는 경우이다. 이런 경우 가능한 이전 가능한 방법은 아래 두가지 생각해 볼 수 있다.

  • 기존 팀에 같은 client_id로 앱을 출시까지 진행하고 새로운 팀에서 이전 받는 것
  • 웹뷰를 통해서 사용자 요청을 새로 받아 신규 팀으로 로그인 하도록 유도하고 성공한 경우 저장된 기존 팀의 provider Id를 변경 (웹에서 사용자 이전을 위한 구현이 필요하다.)

References