애플 로그인

WinG·2024년 8월 3일
0

Spring

목록 보기
2/2
post-thumbnail

애플 로그인.. 정말 악명이 높다. 이번 프로젝트에서 소셜 로그인 구현을 담당하게 되었는데, 그 과정에서 학습한 애플 로그인부터 탈퇴까지 그 흐름에 대해서 정리해 보았다.

Apple OAuth의 흐름

사용자가 애플 계정으로 로그인을 하면 Apple 서버로부터 다양한 정보를 얻게 된다. 이 중 사용자 인증의 핵심 열쇠인 Identity TokenAuthorization Code 을 잘 기억해두자.

회원 정보 가져오기

사용자가 애플 로그인을 했을 때, 그 사용자의 정보(user information)을 가져오기 위해서는 말 그대로 신원 정보를 담고 있는 Identity Token이 필요하다.
Generate and validate tokens | Apple Developer Documentation

애플 공식 문서에 따르면 Identity Token 은 JWT형식으로 사용자의 정보(email, serial ID)를 얻을 수 있다. 이때, Identity Token 으로는 사용자의 이름 정보를 가져올 수는 없다. 때문에 사용자의 이름이 필요한 서비스라면 클라이언트에서 따로 처리하여(이름 추출) 서버에게 Identity Token 과 사용자의 이름 정보도 함께 보내주어야 한다.

서버에서 Identity Token 을 검증하기 위해서는 Apple 서버의 공개 키로 서명을 확인해야 한다.

Apple 서버에 공캐 키를 요청하면, 아래와 같이 여러 개의 키들을 보내준다.

{
    "keys": [
        {
            "kty": "RSA",
            "kid": "FftONTxoEg",
            "use": "sig",
            "alg": "RS256",
            "n": "wio-SFzFvKKQ9vl5ctaYSi09o8k3Uh7r6Ht2eJv-hSaZ6A6xTXVIBVSm0KvPxaJlpjYPTCcl2sdEyXlD2Uh1khUKU7r9ON3rpN8pFHAere5ig_JGVEShxmt5E_jzMymYnSfkoSW44ulevQeUwP_MiC5VC1KJjTfD73ghX0tQ0-_RjTJJ2cLyFC4VFNboBMCVioUrz8IA3c0KIOl507qswQvMsh2vBTMDDSJfippAGLzUiWXxUlid-vyOC8GCtag61taSorxCw14irk-tsh7hWjDDkSTFn2gChPMfXXj10_lCv0UG29TVUVCAsay4pszzgmc4zwhgSsqQRd939BJexw",
            "e": "AQAB"
        },
        {
            "kty": "RSA",
            "kid": "pyaRQpAbnY",
            "use": "sig",
            "alg": "RS256",
            "n": "qHiwOpizi6xHG8FIOSWH4l0P1CjLIC7aBFkhbk7BrD4s9KQAs5Sj5xAtOwlZMyP2XFcqRtZBLIMM7vw_CNERtRrhc68se5hQE_vsrHy7ugcQU6ogJS6s54zqO-zTUfaa3mABM6iR-EfgSpvz33WTQZAPtwAyxaSLknHyDzWjHEZ44WqaQBdcMAvgsWMYG5dBfnV-3Or3V2r1vdbinRE5NomE2nsKDbnJ3yo3u-x9TizKazS1JV3umt71xDqbruZLybIrimrzg_i9OSIzT2o5ZWz8zdYkKHZ4cvRPh-DDt8kV7chzR2tenPF2c5WXuK-FumOrjT7WW6uwSvhnhwNZuw",
            "e": "AQAB"
        },
        {
            "kty": "RSA",
            "kid": "pggnQeNCOU",
            "use": "sig",
            "alg": "RS256",
            "n": "xyWY7ydqTVHRzft5fPZmTuD9Ahk7-_2_IekZGy07Ovhj5IhYyVU8Hq5j0_c9m9tSdJTRdKmNjMURpY4ZJ_9rd3EOQ_WnYHM2cZIQ5y3f_WxeElnv_f2fKDruA-ERaQ6duov-3NAXC3oTWdXuRGRLbbfOVCahTjvnAA8YBRUe3llW7ZvTG14g-fAEQVlMYDxxCsbjtBJiUzKxbH-8KvhIhP9AJtiLDfiK1yzVJ7Qn6HNm5AUsFQKOAgTqxDMJkhi7pyntTyxhpkLYTEndaPRXth_LM3hVmaoFb3P3TsPCbDjSEbKy1wAndfPSzUk6qjyyBYhdXH0sgVpKMBAdggylLQ",
            "e": "AQAB"
        },
        {
            "kty": "RSA",
            "kid": "T8tIJ1zSrO",
            "use": "sig",
            "alg": "RS256",
            "n": "teUbLrwScsjVrcFAvSrfben3eQaEca3ESBegGh_wdGuLKw6QgwDxY3fC1_WeSVnkJXx72ddw3j2inoADnTyzuNa_PwDSmvJhOhmzOmoltmtKHteGdaXrqMohO6A85WxVKbN7pzDqwZJNrdY12LOltlI8PHIG-elAbKM2XOHiJaZnLpAVckKy6MQYsEExpPB3plGxWZElqwNZY6SUDVeN-o9qg5FJOFg7T7iTVVEagws4DM6uZNMDQGtqg9V9VqPQkUzC-sYd5eqbB9LqH4iN5F6OB7BmD3g3jCu9zgh3O9V24N43EruBCNrmP0xLP5ZliKqozoAcd1nv71HuVm6mgQ",
            "e": "AQAB"
        }
    ]
}

이 키들 중에서 Identity Token 헤더에 포함된 kid, alg가 일치하는 키를 가져오면 된다.

Fetch Apple’s public key for verifying token signature | Apple Developer Documentation

회원 탈퇴

애플 심사 지침에는 앱 내에서 애플 로그인을 제공하는 경우 계정 탈퇴 시 Apple REST API를 호출하여 사용자 토큰을 만료하라는 지침이 있다. 처음에 애플 로그인을 하면 Authorization Code 을 발급 받을 수 있다고 말했었다. 이를 사용해서 우리는 애플 서버의 token(애플 서버에서 발급한 accesss token 또는 refresh token)을 받아올 수 있고 이는 애플 서버에 탈퇴(revoke) 요청을 보낼 때 필수적으로 필요한 토큰 정보이다. 이에 더해서 추가적으로 client_id, client_secret, token_type_hint 이 3가지 정보가 필요하다. 여기서 token_type_hint 는 요청 보낸 token 의 종류를 명시해주는 것이고 client_id 는 애플 개발자 계정에서 얻을 수 있다. 따라서 우리가 따로 생성하거나 얻어와야 하는 정보는 결과적으로 client_secrettoken 이 2가지 이다.

Revoke tokens | Apple Developer Documentation

애플 로그인이 악명이 높은 이유가 이 회원 탈퇴 로직이 복잡하기 때문이 아닐까 싶다. 찬찬히 살펴보자.

Client Secret 생성하기

Creating a client secret | Apple Developer Documentation

먼저 client_secret 은 다음과 같은 형식을 따르고 있다.

{
    "alg": "ES256",
    "kid": "ABC123DEFG"
}
{
    "iss": "DEF123GHIJ",
    "iat": 1437179036,
    "exp": 1493298100,
    "aud": "https://appleid.apple.com",
    "sub": "com.mytest.app"
}

차례대로 각 구성요소를 살펴보면,

  • alg : alg는 애플 로그인을 위한 토큰 알고리즘
  • kid: 애플 개발자 계정과 연결된 Sign in with Apple private key에 대해 생성된 10자리 key 식별자
  • iss: 애플 개발자 계정과 연결된 팀 ID
  • iat: 클라이언트 암호를 생성한 시간
  • exp: 클라이언트 암호가 만료되는 시간
  • aud: 클라이언트 암호는 유효성 검사 서버로 전송되기 때문에, “https://appleid.apple.com" 을 값으로 입력하면 됨
  • sub: 애플 개발자 계정의 client_id값을 사용

즉, client_secret 을 생성하기 위해서는 우리가 애플 개발자 계정에서 애플 로그인 사용을 위해 앱을 등록하면서 발급 받은 keyId, teamId, clinetId 를 준비해야한다. 만료 시간의 경우, 임의로 설정해도 되지만 30일을 넘어가도록 설정할 경우 오류가 뜬다는 말이 있다. 나의 경우, 회원 탈퇴를 위해 사용하는 Authorization Code의 만료 기한이 5분이고 우리의 서비스에서는 client_secret를 회원 탈퇴를 위해서만 사용하기 때문에 똑같이 만료 기한을 5분으로 설정하였다.

public String generateClientSecret() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
    Date expirationDate = Date.from(LocalDateTime.now().plusMinutes(5)
            .atZone(ZoneId.systemDefault()).toInstant());
    return Jwts.builder()
            .setHeaderParam("alg", SignatureAlgorithm.ES256)
            .setHeaderParam("kid", keyId)
            .setIssuer(teamId)
            .setIssuedAt(new Date(System.currentTimeMillis()))
            .setExpiration(expirationDate)
            .setAudience(AUDIENCE)
            .setSubject(clientId)
            .signWith(ApplePrivateKeyGenerator.getPrivateKey(), SignatureAlgorithm.ES256)
            .compact();
}

위의 정보들로 만들어진 client_secret 는 반드시 private key로 서명이 되어야 한다. 이 private key는 애플 개발자 계정에서 앱을 등록하면 다운로드 받을 수 있다. 이렇게 하면 client_secret이 완성된다.


💡 약간의 꿀팁! 내가 생성한 client_secret이 제대로 서명됐는지 확인하고 싶다면?

https://jwt.io/

  1. 위의 사이트에 출력된 client_secret 값을 'Encoded'에 붙여넣는다.

  2. Public Key 파일을 만들어 준다

    Terminal을 켠 후, private key 파일이 설치된 위치로 가서 아래 명령을 실행해준다.

openssl ec -in {private key 파일명.p8} -pubout -out {생성할 public key 파일명.p8}
  1. 해당 Public Key를 통째로 긁어서 Verify Signature에 복붙한다.

    이렇게 verified라고 뜬다면 정상적으로 발급된 것이다.

Token 발급 받기

Generate and validate tokens | Apple Developer Documentation

토큰을 발급 받기 위해서는 client_id, client_secret, grant_type, code가 필요하다.

우리는 Authorization Code 을 이용하여 토큰을 발급 받을 것이기 때문에 grant_type을 “authorization_code”로 설정해주면 된다.

요청이 성공하면 회원 탈퇴에 필요한 access token과 refresh token을 얻을 수 있다. 둘 중 무엇을 사용해도 상관이 없지만 만료 시간이 짧은 access token과 달리 refresh token의 경우 만료 기한이 없기 때문에 탈퇴 요청 시에 refresh token을 사용하는 것이 좀더 안정적이라 생각하여 현재 프로젝트에서는 탈퇴 요청시 refresh token을 사용하여 애플 서버에 탈퇴 요청을 하였다.

사용자 토큰 해지하기

마지막 단계이다. 지금까지 얻은 정보들을 가지고 https://appleid.apple.com/auth/revoke 로 요청을 보내면 된다. 우리는 refresh token을 보내 회원 탈퇴 요청을 진행할 것이기 때문에 token_type_hint는 “refresh_token”으로 설정해주면 된다.

public void requestRevoke(final String refreshToken, final String clientSecret) {
     appleFeignClient.revoke(refreshToken, clientId, clientSecret, "refresh_token");
}

회원 탈퇴를 구현하는 2가지 방법

필자의 서비스에서는 인증만 Apple OAuth를 따르고 자체적으로 jwt 토큰을 사용해 인가를 처리하였다.

만약 로그인을 하자마자 바로 Apple 서버로부터 token 정보를 받아오도록 구현을 하였다면 후에 사용자가 탈퇴할 때 사용자 토큰을 만료하기 위해서 애플 서버에서 발급해준 refresh token을 db에 저장해 놓는 로직이 필요하다. (Authorization Code의 만료 기한은 5분으로 매우 짧기 때문에 코드 그 자체를 db에 저장하는 것은 가입하자마자 탈퇴를 하는게 아닌 이상 효력이 없다.)

다만 탈퇴 기능만을 위해 애플 서버에서 발급 받은 refresh Token을 db에 저장하는 것은 비효율적이라 생각했기에 아래와 같은 로직으로 회원 탈퇴를 구현하였다.

🔑 탈퇴 로직
1. 회원 탈퇴 버튼 클릭
2. 애플 APP 재로그인 진행 (이때, Authorization Code를 다시 발급 받아옴)
3. 재로그인이 완료되면 새로 발급된 Authorization Code를 이용하여 탈퇴 요청을 보냄
4. 회원 탈퇴 완료

profile
공부하는 감자😎

0개의 댓글