Weekoding

[Swift] Apple Login 구현하기 [1/3] - 기본 개념 본문

공부 노트(Swift)

[Swift] Apple Login 구현하기 [1/3] - 기본 개념

Weekoding 2023. 3. 20. 02:19

이번에 "Sign in with Apple"를 구현할 일이 있었는데,

생각보다 애를 먹었다. 

정확히는 revoke token부분이 Login with Kakao ID와는 다르게 엄~청 어려웠다.

다음번엔 좀 더 능숙하게 구현하기 위해 한번 정리하고 가려고 한다.

 

이번 포스팅은 Sign in with Apple 뿐만 아니라,

다른 SNS 로그인 API에서도 널리 통용되는 개념들을 간단히 정리해볼 것이다.

  • GET / POST
  • Session / Cookie / Token
  • OAuth 2.0

 

📂  Get / Post

두 방식은 HTTP 메소드 방식들이다. 즉, 클라이언트가 서버에 요청할 때 사용되는 방식이다.

GET POST외에 여러 방식이 더 존재한다. 

 

GET : 

데이터를 URL 주소의 끝에 파라미터로 포함되어 전송한다.

이를 Query String이라 칭하며, 형식은 아래와 같다.

?변수명=값&변수명=값・・・
ex) http://www.myURL.com/mypage?name1=value1&name2=value2

보통 정보를 요청할 때 사용된다.

 

POST

데이터를 HTTP의 body 부분에 담아 서버로 보낸다.(.php 등)

이때, body의 Type은 'Content-Type' 헤더에 따라 결정된다.(5가지 정도가 있는 것으로 보인다.)

response값을 체크할 때 유용하게 사용되는 App "Postman"

body는 길이 제한이 따로 없으며(대용량 데이터 전송가능), 데이터가 외부적으로 드러나지 않는다.

(하지만 개발자 도구만 켜도 body값을 확인할 수 있어서 엄청난 보안 기능은 아니다)

보통 리소스 생성 및 업데이트시 사용된다.

 

GET, POST 방식의 차이점을 정리하면 아래와 같다.

  GET POST
캐시 O X
브라우저 히스토리 O X
북마크 추가 O X
데이터 길이 제한 O X
idempotent(멱등성) O X
리소스 전달 방식 Query String HTTP Body
주된 사용 상황 리소스 요청 리소스 생성
HTTP 응답 코드 200(Ok) 201(Created)

idempotent(멱등성: 冪等性) - 연산의 성질 중 하나. 연산을 여러번 적용하더라도 결과가 달라지지 않는 성질을 의미함

GET 방식은 서버에 동일 요청을 여러번 전송해도 결과값이 변하지 않는다(응답이 동일하다).

POST 방식은 서버에 동일 요청을 여러번 전송해도 결과값이 변할 수 있다.

이 차이는 두 방법의 사용 목적과 연관이 있다.

이는 GET은 리소스 조회/요청 목적으로 주로 사용되지만, POST는 리소스 생성 및 변경등의 사용목적으로 사용되기 때문이다.

 

상황에 맞게 적절한 요청 방식을 사용하자 !

 

 

📂  Session / Cookie / Token 

위 세가지 개념은 HTTP의 Statless라는 특성으로 인해 등장한다.

= 각 통신의 상태를 따로 저장해 두지 않는다는 의미이다.

그렇다면 이론 상 로그인을 해도 다른 페이지로 이동할 때 마다 로그인을 해주어야 한다.

이러한 문제를 해결하기 위해 Session / Token 방식 중 하나를 사용한다. 

 

🍪  Cookie

: 브라우저(프로그램)에 저장되는 텍스트 조각

ㅂㄷㅂㄷ...

비회원 주문이나 검색 기록같은 기능들은 이 쿠키를 통해 이루어진다.

브라우저가 서버와 연결 되었을 때, 브라우저에서 자동적으로 쿠키를 생성한다.

이름, 값, 만료날짜, 경로 정보로 구성된다.

제 3자도 쿠키를 조회할 수 있어, 보통 간단한 정보들(개인정보가 아닌 기록들, 설정 값 등)을 담아둔다.

 

🪪  Session

: 사전적 의미는 특정 활동을 위한 시간. 여기서의 Session은 클라이언트와 서버와의 관계 정보(상태)를 의미한다.

 

Session의 인증 및 요청 flow는 아래와 같다.

1. 클라이언트가 자신을 인증(로그인 등)한다.

2. 서버는 DB에서 이를 확인하고, 성공 시 세션 저장소에 이를 저장하고 response로 Session ID를 전송한다.

2. 브라우저는 이를 'Cookie'에 저장하게 된다.

3. 이후, 클라이언트는 새 정보를 요청할 때마다 Cookie에 담겨있는 Session ID를 함께 보내게 된다.

4. 서버는 세션 저장소를 통해 Session(상태)를 확인하여 매칭되어있는 요청 정보에 맞는 응답을 하는 것이다.

 

즉, Session은 Cookie를 기반으로 한다는 점! 인증절차에서의 Cookie, Session은 분리된 개념이 아니다.

Cookie(Session ID)는 서버에서 발급된 세션을 요청 시 다시금 인증하기 위한 'key'로써 사용되는 것이다.

 

🤷‍♂️ WHY?

• Cookie만으로 인증을 할 순 없는가?

   Cookie만으로 인증을 한다는 것은 서버 자원을 사용하지 않는다는 의미가 된다.

   이는 클라이언트가 인증 정보를 책임지는 것을 의미하여, HTTP 요청이 탈취되면 모든 정보가 탈취된다.

 

장점:

- Cookie(Session ID)가 담긴 HTTP Request가 노출되어도, Session ID는 유의미한 값이 아니다.

- 사용자별 고유 값이므로 Session ID만으로 사용자 정보에 접근이 용이하다.

 

단점:

- 세션 저장소가 사용되므로, 사용자가 많이 몰리게 되면(Session ID 확인 요청이 많아지면) 서버 메모리에 부하가 걸릴 수 있다.

- 사실은 Cookie도 탈취가 가능해서(놀라운 사실!), 해커가 해당 Cookie의 Session ID로 요청을 보내면

   서버는 이를 구별할 수 없다.

 

해결 방법 :

- HTTPS를 사용하는 것. 서버와 클라이언트 간 통신을 암호화하여 Request가 탈취되어도 정확한 값을 얻어낼 수 없다.

- Session이 일정 시간이 지나면 끊기도록 유효시간을 지장하는 것("세션이 만료되었습니다." 라는 문구를 많이 보았을 것이다.)

 

🪙  Token

: 사전적 의미는 증거, 상징. 인증을 위해 사용되는 정보들을 암호화한 문자열이다.

+) JWT: JSON Web Token의 약자.

 

맨몸으로 SNS Login API문서를 보면 눈앞이 캄캄-해질 수 있다.

좌: 카카오 로그인 API Index, 우: Apple 로그인 API Index

Token의 인증 및 요청 flow는 다음과 같다.

1. 클라이언트가 자신을 인증(로그인 등)한다.

2. 서버는 DB에서 이를 확인하고, 성공 시 Token을 생성하여 이를 response로 전송한다.

3. 클라이언트 이를 저장하게 된다.

4. 이후, 클라이언트는 새 정보를 요청할 때마다 Cookie에 담겨있는 Token 함께 보내게 된다.

5. 서버Token유효한지만을 검증 후 매칭되어있는 요청 정보에 맞는 응답을 하는 것이다.

 

Session ID는 간단한 값으로 이루어져 있어, 세션 저장소가 매칭된 정보를 올려놓고 이를 기억해야 했다.

반면 TokenHeader + Payload + Verify Signature의 구조로 이루어져 있다.

그래서 Token 자체가 유효기간, User의 고유 ID등 많은 정보를 포함하고 있기 때문에 세션 저장소가 필요없다.

따라서 훨씬 효율적으로 서버의 인증 운영이 가능하다.

또한 Secret Key를 알아야만 암호화된 정보에 접근할 수 있기 때문에 보안이 뛰어나다.

 

장점:

- 추가 저장소(세션 저장소)가 필요 없다.

- 확장성이 뛰어나다(써드파티에 프로필 정보 제공, 권한 요청 등).

 

단점:

- 이미 발급된 JWT는 악용되어도 유효기간 만료 전까지는 계속 사용이 가능하다.

   (Session / Cookie 방식은 Session을 지워버리면 된다.)

- JWT의 길이는 Session ID보다 길기 때문에, 인증에 필요한 요청이 많아질 수록 서버의 자원이 많이 필요해진다.

 

해결 방법 :

- Access Token의 유효기간을 짧게 하고, Refresh Token을 발급해서 사용한다.

  (위에 상술한 'Token'들은 Access Token을 의미하는 것이라고 생각하면 된다.)

 

🪙  Refresh Token?

: Refresh Token은 Access Token과 똑같은 형태의 JWT이다.

 

앞서 Access Token만을 이용한 인증 방식의 문제를 살펴보았다. Access Token을 제 3자에게 탈취당하면,

유효기간이 끝나기 전까지 해제나 삭제가 되지 않아 무방비 상태가 된다는 것이었다.

그러나 유효기간을 줄이게 되면, 사용자는 그만큼 로그인을 자주 해야하는 불편함이 발생한다.

→ 이에 대한 고민의 해답으로 등장한 것이 Refresh Token이다.

 

Refresh Token은 처음에 로그인을 완료 했을 때 Access Token과 동시에 발급된다.

Refresh Token은 상대적으로 긴 유효기간을 가지면서, Access Token이 만료됐을 때 새로 발급해주는 열쇠가 된다.

 

Refresh Token을 이용한 인증 및 요청 flow는 다음과 같다 (점점 복잡해져서 머리가 아프다)

1. 클라이언트가 자신을 인증(로그인 등)한다.

2. 서버는 DB에서 이를 확인하고, 성공 시 Access Token, Refresh Token을 생성하여 이를 response로 전송한다.

    +) 회원 DB에 보통 Refresh Token을 저장한다고 한다. 그러나 Token 자체가 정보를 갖고 있어 저장하지 않아도 가능은 하다.

3. 클라이언트는 response 값을 잘 받아 저장한다. 

4. 이후, 클라이언트는 새 정보를 요청할 때마다 Access TokenHTTP Header에 실어 보내게 된다.

5. 서버 Access Token 유효한지만을 검증 후 매칭되어있는 요청 정보에 맞는 응답을 하는 것이다.

 

시간이 지나면 상대적으로 짧은 Access Token의 유효기간이 지날 것이다.

1. 클라이언트는 새 정보를 요청할 때 만료된 Access Token HTTP Header에 실어 보내게 된다.

2. 서버Access Token이 만료된 것을 확인하면, 클라이언트만료된 Access Token임을 알린다.

3. 클라이언트는 다시 Access TokenRefresh Token을 발급하여 함께 서버로 전송한다.

4. 서버받은 Refresh Token의 유효성(or 저장된 Refresh Token의 일치여부)를 검사한다.

5. 동일하다면 새로운 Access Token클라이언트에게 다시 response로 보내준다.

6. 클라이언트새 정보를 요청할 때마다 재발급 받은 Access Token HTTP Header에 실어 보내게 된다.

 

장점:

- Access Token을 단독으로 사용할 때보다 보안이 뛰어나다.

 

단점:

- 구현이 복잡하다(검증 프로세스 2배 이벤트)

- Access Token 만료 시, 새로 발급하는 과정에서의 HTTP 요청 횟수가 많아 자원 낭비가 될 수 있다.

 

🤷‍♂️ WHY?

• Refresh Token까지 탈취된다면 ?

결론적으로 두 Token 모두 탈취 가능성이 존재한다.

Access Token - 유효기간이 짧다지만 탈취당하면 유효기간 내에 마음껏 request를 보낼 수 있다.

Refresh Token - Token이기 때문에 탈취 당할 수 있으며 무효화 할 수 없다. (해커는 A.T를 마음껏 발급받을 수 있다!)

(Kakao 계정 해킹을 통한 보이스피싱 사례등이 이 Token방식에도 결점이 있다는 사례인 듯 하다.)

 

하지만, Client가 정보 요청시 사용하는 Access Token은 유효기간이 상대적으로 짧다.
이는 Access Token을 탈취당한다 해도 악용할 시간적 여유를 주지 않겠다는 의도.

 

반면, 유효기간이 상대적으로 긴 Refresh Token는  Access Token의 만료기간 Cycle 기준으로
최초 로그인 성공 시 서버로부터 한번이후 Access Token이 만료되었을 시 클라이언트로부터 한번 주고받게 된다.
데이터 요청 시에 쓰이지도 않아 상대적으로 적게 호출되니 탈취당할 가능성이 적어지는 것이다.

 

✔︎ Refresh Token을 이용한 인증 절차도 상대적으로 안정성이 있는 것 뿐, 한계점이 분명히 존재하는 방법이다.

Client도 각 Token을 탈취당하지 않도록 안전하게 보관할 필요가 있다.

 

• Access Token을 서버에서 검증하는 방식이 아닌 DB에 저장하여 바로 무효화할 수 있게 만들면?

위 생각은 그냥 Session을 쓴다는 말(DB = 별도의 저장공간 = 세션 저장소)과 동일하다...

Token 방식은 별도의 저장공간이 필요 없다는 특징 때문에, 확장성이 높다는 장점에서 오는 경제적 효율 측면을 무시할 수 없다.

 

[위 문제점을 극복하기 위한 노력들]

• Refresh Token을 무조건 JWT(JSON Web Token) 아니라, UUID나 Random String으로 생성하는 경우
JWT base : 서버 내에서 검증하는 것 만으로도 유효성 검증 가능
Other base(Random String, UUID, etc) : 사용자에 매핑되도록 DB에 추가 저장이 필요
대신 other base는 DB수정을 통해 Refresh Token를 즉시 무효화시킬 수 있다. 

• RTR (Refresh Token Rotation) 방식
Access Token을 재발급할 때마다 Refresh Token도 새로 발급하는 방식.
유효기간이 만료될 때 마다 Token을 2중으로 검증하는 방법인 것이다.
사용되지 않은 Refresh Token를 탈취당하면 Access Token를 발급받을 수 있다는 문제점은 동일하다.

( + 잦은 HTTP 요청으로 인한 서버 자원낭비가 심할 것 같다)


📂  OAuth 2.0 

: Open Authorization의 약어. 인증을 위한 Open Standard Protocol이다. 또한 SaaS 형태를 띈다.

SaaS - Software as a Service

 

간단히는 외부서비스의 인증권한부여를 관리하는 범용 프로토콜이라고 할 수 있다.

프로토콜이므로, 특정 프로그램이 아닌 '규격'을 의미한다.

Auth가 Authentification(인증)Authorization(허가) 모두를 포함한 중의적 의미를 갖고 있기 때문이다.

 

즉, OAuth 기능을 통해

- SNS에서 User로부터 아이디/비밀번호를 요구하지 않아도 회원정보를 안전하게 주고받고,

- 동시에 User가 '허용한' 일부 정보에만 접근이 가능해지는 것이다.

 

2.0은 OAuth 기능의 버전을 의미하며, 대부분의 서비스 기업이 2.0을 채택한다. 향상된 부분은 아래와 같다.

  • 모바일 애플리케이션에서도 사용이 용이해짐
  • 반드시 HTTPS를 사용해야 함(보안 강화)
  • Access Token의 만료기간이 생김

쉽게 말하자면, '우리의 서비스'가 우리 서비스를 이용하는 유저의

타사 플랫폼 정보에 접근하기 위해서 권한을 타사 플랫폼으로부터 위임 받는 것.

 

그리고 이를 인증하고, 허가하는 과정에서, 앞서 공부했던 Access Token과 Refresh Token를 통해 이루어진다.

이 프로토콜은 어디까지나 규격이기 때문에, Session/Cookie나 Token인증 방식을 완전히 대체하는것이 아니기 때문이다.

 

실제 SNS 로그인을 하게 되면, flow는 다음과 같다.

1. User서버에게 로그인을 요청

2. 서버User에게 특정 URL을 사용자에게 보냄

3. User는 해당 URL에서 로그인 후, Authorization Code Grant를 받아 서버에 전달

4. 서버Authorization Code GrantSNS사의 Authorization Server로 요청

5. SNS사의 Authorization Server는 이를 확인 후,

    User 고유 ID + 권한 설정에 따른 User의 정보 + Access Token, Refresh Token을 서버에 제공한다.

6. Authorization Server에서 받은 고유ID를 key값으로, DB에 해당 값을 갖는 User가 존재하면 로그인, 없을 시 회원가입

7. 로그인 성공 시, 서버에서 인증 방식(Session / Cookie, Token)을 통해 사용자 인증 처리

 

결론적으로 다른 점은, 사용자-서버 간 인증이 아니라 타 플랫폼(SNS 회사)에서 정보를 받아오는 것이므로

사용자-서버-OAuth서버의 구조로 인증 절차가 이루어지는 것이다.

 

App Store Connect나, Kakao Developer등에 Bundle ID나 Redirect URL 같은 앱 정보를 기입하는 이유가

OAuth에 등록하는 과정인 것이다!

 

 

 

 

📌 마치며 

정리하면서 서버-클라이언트간 인증 방식의 변천사를 훑어보니

인간은 어떻게든 답을 찾는구나.. 라는 생각이 들었다.

또, Token 인증방식보다 더 진화된 방법이 등장할 수도 있겠다 라는 생각도 들었다.

개인적으로 웹 개념은 언제나 기가 빨려..

 

 

오류 및 지적사항은 댓글로 남겨주시면 감사하겠습니다!

참고 : 

https://whales.tistory.com/120

https://velog.io/@kingth/%EC%84%9C%EB%B2%84-%EC%9D%B8%EC%A6%9D-%EB%B0%A9%EC%8B%9D%EC%84%B8%EC%85%98%EC%BF%A0%ED%82%A4-%ED%86%A0%ED%81%B0#session

https://tansfil.tistory.com/60?category=475681 

https://hudi.blog/refresh-token/

Comments