개인 공부

[Python] Flask API서버에서 Apple 로그인 구현하기

leui 2022. 3. 3. 17:21

애플 앱 출시를 하기위해 애플 로그인이 필요하다고 해서 구현하였다. 애플로그인을 구현하기 앞서, 애플 api와 연동을 해야한다.

 

초기 연결하는 법이 적혀 있는 블로그이다.(감사합니다)

https://tyrannocoding.tistory.com/65

 

[초간단] 애플 로그인 API 연동 초기 설정 하기

 IOS 앱 배포를 위해 본인의 어플을 애플에 심사함에 있어, 소셜 로그인 기능을 사용하지만 애플 로그인이 없으면 reject사유가 되므로 전혀 고려하지 않은 애플 로그인을 만들게 되었습니다. 여

tyrannocoding.tistory.com

 

사용자의 데이터를 얻기 위해 필요한 데이터에는 TEAM_ID, CLIENT_ID, REDIRECT_URL, KEY_ID, *.p8 파일이다.

그리고 PyJWT 설치가 필요하다.

pip install pyjwt[crypto]

 

먼저 애플 로그인 화면을 띄어줄 코드이다.

from flask import jsonify

@app.route('/aauth/url', methods=['GET'])
def get_apple_oauth_url():
    return jsonify(
        oauth_url = "https://appleid.apple.com/auth/authorize?"+\
                    "client_id="+app.config['CLIENT_ID']+\
                    "&redirect_uri="+app.config['REDIRECT_URI']+\
                    "&response_type=code id_token&scope=name email&response_mode=form_post"\
    )

client_id와 redirect_uri에 각각 가져온 데이터를 넣어서 보내면 된다. response_type의 code는 사용자가 로그인을 성공했을 때 토큰 검증을 위해 redirect_uri로 form 형태로 넘어온다.

 

다음은 모델 코드이다.

class AppleAuth:
    ACCESS_TOKEN_URL='https://appleid.apple.com/auth/token'
    
    def __init__(self, code):
        self.code = code
    
    def request_apple_auth(self):
        client_secret = self.get_client_secret()
        
        headers = {'content-type': "application/x-www-form-urlencoded"}
        data = {
            'client_id': app.config["CLIENT_ID"],
            'client_secret': client_secret,
            'code': self.code,
            'grant_type': 'authorization_code',
            'redirect_uri': app.config['REDIRECT_URL']
        }
        
        res = requests.post(AppleAuth.ACCESS_TOKEN_URL, data=data, headers=headers)
        
        return res
        
        
    def get_client_secret(self):
        headers = {
            'kid': app.config['KEY_ID']
        }

        payload = {
            'iss': app.config['TEAM_ID'],
            'iat': datetime.now(),
            'exp': datetime.now() + timedelta(minutes=10),
            'aud': 'https://appleid.apple.com',
            'sub': app.config['CLIENT_ID']
        }
        
        key = self.get_auth_key()
        client_secret = jwt.encode(
            payload, 
            key,
            algorithm='ES256', 
            headers=headers
        )
        
        return client_secret
    
    
    def get_auth_key(self):
        base_dir = os.getcwd()
        f = open(f'{base_dir}/*.p8(파일)')
        key = f.read()
        f.close()
        
        return key

 

전체적인 흐름은 https://appleid.apple.com/auth/token 이 uri로 id_token 발급 및 토큰 검증하는 흐름이다.

(id_token에는 유저의 정보가 들어있다.)

 

# AppleAuth.get_client_secret

def get_client_secret(self):
        headers = {
            'kid': app.config['KEY_ID']
        }

        payload = {
            'iss': app.config['TEAM_ID'],
            'iat': datetime.now(),
            'exp': datetime.now() + timedelta(minutes=10),
            'aud': 'https://appleid.apple.com',
            'sub': app.config['CLIENT_ID']
        }
        
        key = self.get_auth_key()
        client_secret = jwt.encode(
            payload, 
            key,
            algorithm='ES256', 
            headers=headers
        )
        
        return client_secret

먼저 client_secret을 만들어야한다.  key의 경우 .p8의 내용을 담으면 된다. (get_auth_key함수 참고)

(payload에 exp는 토큰 만료시간이다. 너무 길게하면 400 error가 발생한다.)

 

이제 POST로 https://appleid.apple.com/auth/token 이 URI로 요청하면 된다.

# AppleAuth.request_apple_auth

     def __init__(self, code):
        self.code = code
        
     def request_apple_auth(self):
        client_secret = self.get_client_secret()
        
        headers = {'content-type': "application/x-www-form-urlencoded"}
        data = {
            'client_id': app.config["CLIENT_ID"],
            'client_secret': client_secret,
            'code': self.code,
            'grant_type': 'authorization_code',
            'redirect_uri': app.config['REDIRECT_URL']
        }
        
        res = requests.post(AppleAuth.ACCESS_TOKEN_URL, data=data, headers=headers)
        
        return res

로그인 성공을 하여 받은 code와 위에서 생성한 client_secret을 data에 넣어 요청하면 된다.

 

이제 미리 설정한 redirect uri의 코드만 짜면 된다.

@app.route('/aauth', methods=['POST'])
def apple_auth():
    
    code = request.form.get("code")
    
    apple_auth = AppleAuth(code)
    res = apple_auth.request_apple_auth()
    
    if res.status_code != 200:
        return res.status_code
    
    res_dict = res.json()
    key = apple_auth.get_auth_key()
    
    id_token = res_dict.get('id_token', None)

    if id_token:
        decoded = jwt.decode(id_token, key=key, algorithms='RS256', options={'verify_signature': False})
        email = decoded['email']
        
    
    # 이후 코드는 서비스에 맞게
    # 회원가입, 로그인 처리

먼저 로그인 성공시 받는 code를 form으로 받는다

AppleAuth 객체를 생성하고 애플로 요청을 보내면 된다. 넘어온 response에 있는 id_token을 복호화하면 email정보가 있다.

 

이 정보로 서비스에 맞게 회원가입 혹은 로그인 처리를 하면 될 듯하다.