[1] OAuth
OAuth
OAuth는 결국 구글 / 인스타와 같은 다양한 플랫폼의 특정한 사용자가 데이터에 접근하기 위해서 제3자 클라이언트 ( 우리의 서비스 가 사용자의 접근 권한을 위임받을 수 있는 프로토콜이다.
정리하면, 서비스를 이용하는 유저의 타사 플랫폼 정보 접근하기 위해 타사 플랫폼으로부터 위임 받는것이다
OAuth2.0
사용자가 다른 웹 사이트의 자신의 정보를 웹이나 애플리케이션에 접근 권한을 부여할 수 있는 개방형 표준이다.
OAuth는 로그인뿐만 아니라 데이터 접근 권한도 포함하기에 "인증" 뿐만 아니라 "인가" 도 포함하고 있다.
구글링을 하다보면 여러 flow를 볼 수 있는데, 개인적으로 용어에 대해서 혼동이 있었다.
그래서 먼저 용어에 대해서 정리를 하겠다.
용어
[ 용어 정리 ]
1. Resource Owner : User( 보호된 자원에 대해서 접근 권한 부여할 수 있는 사용자 = 우리 )
2. client : 우리가 개발하려는 서비스 ( OAuth를 이용하기 위해 Resource 서버에 등록한 애플리케이션이나 웹 서비스 )
-> resource / authentication server의 입장에서 우리가 개발하려는 서비스는 client 이다.
3. Resource Server : 리소스 소유자 ( 엑세스 토큰을 사용하여 보호된 리소스 요청에 응답한다 )
-> 우리의 서비스를 이용하면서 구글, 페이스북 등의 플랫폼에서 리소스를 소유하는 사용자
4. Authentication Server : 네이버 / 구글 등,, ( 성공적으로 인증을 마친 클라이언트에게 엑세스 토큰을 발급 )
참고로 공식 문서에서는 Resource server와 Authorization Server를 구분짓지만, 일반적으로 Resource Server로 통용
OAuth Flow
1. User( 우리 )가 Client( 인스타 / 페북 )의 로그인이 필요한 자원에 접근한다.
2. [ cliend_id, redirect_uri, reponse_type, scope ] 를 포함해 브라우저를 Authorization server로 리다이렉션 시킨다.
3. Authorization server는 [ cliend_id, redirect_uri ] 이 사전에 등록된 정보와 일치 검증하고 일치하지 않으면 요청 거절한다.
어플리케이션 등록
카카오 같은 경우 kakao developer에서 내 애플리케이션 등록하고 해당 사이트에서 cliend_id & client secret 도 받고,
redirect_uri를 등록할 수 있습니다.
매개변수가 가리키는 내용 정리
- response_type : 반드시 code 로 값을 설정해야한다. 인증이 성공할 경우 클라이언트는 후술할 Authorization Code를 받을 수 있다.
- client_id : 애플리케이션을 생성했을 때 발급받은 Client ID
- redirect_uri : 애플리케이션을 생성할 때 등록한 Redirect URI
- scope : 클라이언트가 부여받은 리소스 접근 권한. [ 아래 참조 ]
scope
Scope를 통해서 리소스에 대한 접근 범위를 제한할 수 있습니다.
Scope는 여러개가 가능하고, 대소문자를 구분하는 문자열을 공백으로 표현한다. [ 문자열은 OAuth2.0 인증 서버에 의해 정의됨 ]
4. 로그인 페이지를 열고 User가 Client가 등록한 scope에 대한 정보 제공 동의 허용 여부를 나타낸다.
5. 로그인하고 동의된 정보에 한해서 Client가 User의 정보에 접근하여 보여준다.
[ ex) ~에서 사용자의 프로필 이미지, 이름에 접근하고자 합니다. ]
6 - 12.
로그인한 유저를 검증하고 Authorization server는 Client에게 Authorization code를 발급한다.
Client는 다시 Authorization server에게 Authorization code, client Id, secret을 다시 전송한다
[ 인증되어 로그인이 되었다면, Redirect URI로 사용자를 리다이렉트 시킬 것이다. ]
Authorization server는 전달 받은 데이터를 검증하고 "Access Token"을 Client에게 발급한다
이제 Access Token을 통해서 Resource server에 데이터 요칭하고 검증되면 scope 범위의 데이터를 응답한다.
Refresh Token
[ A ] ~ [ E ] 까지는 OAuth Flow와 같습니다.
[ F ] : access token이 만료되어 데이터 접근 거부.
[ G ] : refresh token을 통해 Access token 및 [ 구현 방식에 따른 ] Refresh Token도 재발급 합니다.
[ 프로젝트를 진행하면서 생각해 보았던 주제 ]
Q. access token이 만료가 되었을때, access token을 재발급하면서 refresh token 또한 재발급을 하나요?
기존에는 "refresh token 만료 며칠전을 기준으로 두고 access token을 재발급할때 refresh token을 재발급 하면 되지 않을까? " 이렇게 생각을 했습니다.
하지만 이 경우는 무한 로그인이 가능하다고 생각이 되며, 이는 refresh token로써의 역할을 못한다고 생각합니다.
refresh token은 해커의 탈취를 막기 위해 access token의 짧은 만료시간을 가지며, 이에 대해 편리성을 증대하기 위해서 refresh token을 두었는데, refresh token이 계속 유효하다면 access token에 만료기간이 없는 것과 같다고 생각되기 때문입니다
그래서 저는 access token이 만료된다면, refresh token을 통해 재발급 하지만, refresh token이 만료가 된다면 재로그인 하게끔 해줘야 한다고 생각합니다.
위와 같은 token들의 인증방식은 JWT를 통해서 이루어 집니다.
JWT ( Json Web Token )
토큰 관련된 인증은 대부분 로그인할 때 사용되며, JSON 객체를 사용하여 정보를 전달합니다.
그리고 필요한 모든 정보를 한 객체에 담아서 전달하기에 JWT 한가지로 인증을 마칠 수 있습니다.
가장 중요하게 JWT를 왜 쓰는지 생각해 보면
HTTP의 특징인 state-less 즉, 무상태이기에 2-tier 구조에서 서버가 클라이언트의 상태를 가지고 있지 않는데 이를 보완하기 위해서 JWT를 통해서 데이터 압축 및 암호화를 위해서 사용한다고 생각하면 될 것같다.
header(헤더).payload(내용).signature(서명)
이렇게 .(dot)을 이용해서 3가지를 구분합니다.
헤더 / 내용 / 서명 정리
헤더
두가지
- typ : 토큰타입 → JWT이기에 JWT
- alg : 해싱 알고리즘 ex) HMAC, SHA256, RSA
{
"typ" : "JWT",
"alg" : "HS256"
}
정보(payload)
토큰의 정보들을 name/value 쌍으로 클레임이라는 단위에 담아서 보낸다
클레임 : 정보의 한조각 ( 토큰에 들어 있는 정보의 조각 : name/value 쌍 )
- 등록된 클레임
- 토큰에 정보를 담기 위해 이름이 이미 정해진 클레임 ( 선택적임 )
- iss : 토큰 발급자 (issuer)
- sub : 토큰 제목 (subject)
- aud : 토큰 대상자 (audience)
- exp : 토큰의 만료시간(expiration), 시간은 NumericDate 형식으로 되어있어야 하며 언제나 현재 시간보다 이후로 설정되어 있어야 합니다.
- nbf : Not before을 의미하며, 토큰의 활성 날짜와 비슷한 개념입니다. 여기에도 NumericDate형식으로 날짜를 지정하며, 이 날짜가 지정하며, 이 날짜가 지나기 전까지는 토큰이 처리되지 않습니다.
- iat : 토큰이 발급된 시간(issued at), 이 값을 사용하여 토큰의 age가 얼마나 되었는지 판단 할 수 있습니다.
- jti : JWT의 고유 식별자로서, 주로 중복적인 처리를 방지하기 위하여 사용됩니다. 일회용 토큰에 사용하면 유용합니다.
⇒ 이 클래임은 위에서 말한 것처럼 선택적으로 작성이 가능하며, 사용할 것을 권장하고, JWT를 간결하게 표현하기 위해서 key 는 모두 길이가 3인 string 이며, sub ( subject )는 unique 한 값을 사용하는데, 주로 사용자 이메일을 사용
- 공개 클레임
- 사용자 정의 클래임으로, 공개용 정보를 위해 사용
- 충돌이 방지된 이름 갖기 by URI 형식의 이름 -> 예시 { "<https://techj9972..tistory.com/js_admin>" : true } #
- 비공개 클레임
- 사용자 정의 클래임으로, 서버 - 클라이언트 사이에 임으로 지정한 정보 저장
- 양 측간에(보통 클라이언트 <-> 서버) 합의하에 사용되는 클레임 이름들입니다. 공개 클레임과는 달리 이름이 중복되어 충돌이 될 수 있으니 사용할때에 유의해야합니다.
- { "username": "velopert" }
서명
헤더 인코딩값 + 정보 인코딩 값 합쳐서 비밀키로 해쉬해서 생성을 하고 base64형태로 표현
끝으로 궁금증이나 같이 생각해보고 싶은 점이 있다면 댓글 남겨주세요! 같이 상의 해보고 싶습니다!!