- Published on
포트원 다날 본인인증 api 연동하기
- Authors
- Name
- hongreat
- ✉️hongreat95@gmail.com
사용자 신원 확인 및 보안을 강화하기 위해, 다날의 본인인증을 포트원 API를 통해 구현할 수 있습니다.
이번 글에서는 Django에서 포트원v1 과 다날을 사용해 본인인증을 구현하는 방법을 간단하게 기록합니다.
1. View
로그인과 회원가입단에서 핸드폰만을 활용한 간단한 방식으로 구축했기 때문에,
아래와 같이 간단한 값으로 스키마를 설계합니다.
{
"impUid": "string",
"phone": "string"
}
class PortoneVerifierCreateView(CreateAPIView):
"""
포트원 다날 본인인증
---
"""
serializer_class = PortoneVerifierConfirmSerializer
비즈니스 로직으로 클라이언트의 IP 주소를 context
에 추가하여, 해당 데이터를 나중에 본인인증 처리 시 사용할 수 있도록 처리 할 수 있습니다. 개인적으로.. 이 과정에서 보안 적인 요소를 적용하는 것이 좋다고 생각합니다.저는 해외 Ip 차단 로직을 추가했습니다. 검증과 함께 get_serializer_context
를 활용하는 것도 좋은 방법 인 것 같습니다.
2. Serializer 및 로직
PortoneVerifierConfirmSerializer
는 본인인증에 필요한 정보를 받아들여, 포트원의 인증 정보를 확인하고 유저 데이터를 처리하는 과정입니다.
class PortoneVerifierConfirmSerializer(serializers.Serializer):
imp_uid = serializers.CharField(write_only=True, label="포트원 서버인증요청 imp_uid")
phone = serializers.CharField(write_only=True, label="폰 번호 01000000000")
access_token = serializers.CharField(read_only=True)
refresh_token = serializers.CharField(read_only=True)
is_registered = serializers.BooleanField(read_only=True, label="가입완료 여부")
@transaction.atomic
def validate(self, attrs):
request_user = self.context.get("request").user
ip_address = self.context.get("ip_address")
imp_uid = attrs["imp_uid"]
phone = attrs["phone"]
# portone
certifications_info = get_certifications(imp_uid)
ci = certifications_info.get("unique_key")
di = certifications_info.get("unique_in_site")
name = certifications_info.get("name")
gender = certifications_info.get("gender")
birthday = certifications_info.get("birthday")
phone = certifications_info.get("phone", phone)
kst = pytz.timezone(settings.TIME_ZONE)
birth_date = datetime.strptime(birthday, "%Y-%m-%d")
birth_date = kst.localize(birth_date)
if ci and di:
user_obj, _ = User.objects.get_or_create(ci=ci, di=di)
self._validate_other_confirm(request_user, di)
user_obj.name = name
user_obj.gender = gender
user_obj.birth_date = birth_date
user_obj.phone = phone
user_obj.save()
refresh = RefreshToken.for_user(user_obj)
attrs.update(
{
"access_token": str(refresh.access_token),
"refresh_token": str(refresh),
}
)
attrs["is_registered"] = user_obj.is_registered
return attrs
validate
메서드에서 가장 중요한 부분은 포트원 인증 서버로부터 받은 imp_uid
를 이용해 본인인증 정보를 조회하는 과정입니다.
인증 서버에서 받은 ci
, di
, name
, gender
, birthday
등의 정보를 바탕으로 새로운 유저를 생성하거나, 기존 유저 정보를 업데이트하게 됩니다.
JWT 토큰(access_token
및 refresh_token
)을 생성하여 인증이 완료된 후 클라이언트에게 반환합니다.
2.1 portone 요청
portone(포트원) api token을 가져오는 부분은 생략합니다. (AWS의 secret manager를 이용했습니다.)
api token 을 이용해 access token 을 세팅하고, 인증된 자격으로 본인인증(confirm 과 유사) 확인 api 를 발송합니다.
imp_uid 는 클라이언트 사이드에서 생성되기 때문에, 동적으로 입력받은 값으로 처리합니다.
def get_access_token():
url = "https://api.iamport.kr/users/getToken"
portone_secrets = get_secret("portone")
payload = {**portone_secrets}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
response_json = response.json()
if response.status_code == 200:
return response_json.get("response").get("access_token")
raise ValidationError(f"portone getToken api error : {response}")
def get_certifications(imp_uid: str):
access_token = get_access_token()
url = f"https://api.iamport.kr/certifications/{imp_uid}"
headers = {"Authorization": access_token}
response = requests.get(url, headers=headers)
response_json = response.json()
if response.status_code == 200:
return response_json.get("response")
raise ValidationError(f"portone certifications api error : {response}")
2.2 참고_검증 예시
이 메서드(_validate_other_confirm
)는 로그인한 사용자가 인증된 사용자와 동일한지 확인하는 예시 입니다. 만약 다른 사용자의 본인인증 정보를 이용하려는 시도가 있을 경우, 이를 차단하고 에러를 반환합니다.
def _validate_other_confirm(self, request_user, confirmed_di: str):
"""
로그인 된 유저의 di와 포트원 인증 후 반환된 di가 일치하는지 확인합니다.
"""
if request_user and getattr(request_user, "is_authenticated"):
if user_di := getattr(request_user, "di", None):
if confirmed_di != user_di:
raise serializers.ValidationError("타인의 본인인증 에러")