til
til copied to clipboard
OAuth
https://www.oauth.com/
OAuth
Background
- 기존에 앱을 사용하기 위해 아이디, 비밀번호를 앱에 제공한다.
- 비밀번호를 평문으로 저장하기 때문에 보안에 문제가 있다.
- 앱이 서비스에 접근하는것을 취소하려면 비밀번호를 바꾸는 방법 밖에 없었다.
- 플리커, 구글, 페이스북, 야후 등 여러 서비스가 각각의 인증 방식을 만들었고, 서로 호환되지 않았다.
- 2006년 11월 무렵 트위터의 Blaine Cook 수석 아키텍트가 서드파티 앱에 사용자의 패스워드를 제공하지 않는 방법을 찾기 시작했다.
- 2007년 OpenID를 개발했던 사람들이 참여해서 API 표준을 만들기 위한 메일링 리스트를 만들었다.
- 2007년 8월 OAuth 1 초안이 발표되었고, 7번의 업데이트를(draft) 통해 연말에 Internet Identity Workshop 에서 확정(final)됐다.
- 1.1 버전은 IETF working group에서 논의를 시작했으나, 2.0에 집중하기 위해 중단됐다.
- 1 버전은 모바일 앱에서 안전하게 구현할 수 없다.
- 2 버전은 최근의 모바일 앱 유즈 케이스를 지원하고, API를 단순화 했다.
- 2 버전은 IETF 워킹 그룹에서 시작되었고, 웹과 엔터프라이즈 컨트리뷰터 사이의 갈등을 겪었다.
- 이러한 부분은 별도의 문서로 분리되었고, 프로토콜에서 프레임워크로 이름이 변경됐다.
- 오늘날 2.0을 구현하려는 사람은 여러 RFC 문서와 초안을 봐야하며, 스펙이 정해주지 않은 사항을 스스로 결정해야 한다.
- 이를 구현한 서비스들이 대개 비슷한 방식을 사용하고 있다.
Getting Ready
- OAuth 2.0 서비스를 사용하려면 개발자로 계정을 생성하고 앱을 등록해야한다.
- 앱을 등록할 때 Redirect URL을 등록해야한다. 그렇지 않을 경우 악의적인 어플리케이션이 사용자 데이터를 탈취할 수 있다.
- Redirect URL은 여러개를 등록해서 웹, 모바일 앱에서 사용하거나, 개발, 운영 환경에서 사용할 수 있다.
- Redirect URL은 https를 사용해야 한다.
-
Redirect URL은 정확히일치해야 한다. 쿼리 파라미터(
?destionation=account
)를 사용하지 않는게 Best Practice - 요청마다 Redirect URL을 다르게 지정하는 대신
state
를 사용해서 인증 과정이 끝난 뒤 적절한 위치로 이동할 수 있다.
Accessing Data in an OAuth Server
- 깃헙 개발자 페이지에서 새로운 앱을 등록할 수 있다.
- 깃헙은 하나의 앱에 하나의 Redirect URL만 지원하기 때문에, 개발, 운영을 분리하고 싶을 경우 두개의 앱을 만들어야 한다.
- Client ID 는 공개적인 정보다.
- Client Secret 은 코드에 입력하거나 저장소에 커밋하면 안된다.
Authorization Request
-
state
: 클라이언트가 생성하고 세션에 저장하는 랜덤한 문자열이다. 클라이언트는 깃헙으로부터 다시 전달 받은state
값을 통해 위변조 여부를 확인할 수 있다. - 인증이 완료되면 사용자는
redirect_url
로 이동하며code
,state
쿼리 파라미터가 함께 전달된다.
Obtaining an Access Token
-
state
값을 세션에 저장된 값과 비교한다. -
code
를 가지고 Access Token 발급 API를 호출한다. 이때client_secret
을 입력한다. - 발급된 토큰을 세션에 저장하고 적절한 페이지로 이동한다.
- 사용자가 다음에 페이지에 다시 방문할 경우, 세션에 토큰이 있으므로 로그인한 사용자를 위한 뷰를 보여줄 수 있다.
- 깃헙이 반환하는 에러에 맞는 적절한 대처를 해야한다.
Making API Requests
-
Authorization
헤더에Bearer ACCESS_TOKEN
을 지정해서 깃헙 API를 호출할 수 있다.
Signing in with Google
-
Authentication
: 자신이 누구라고 주장하는 사람을 확인하는 절차 (인증) -
Authorization
: 가고 싶은 곳으로 가도록 혹은 원하는 정보를 얻도록 허용하는 과정 (허가) - OAuth은
Authorization
을 위해 설계 되었기때문에, OAuth 플로우의 결과물은 사용자 계정의 어떠한 것을 접근하거나 수정하는 Access token 이다. - Access Token 자체는 사용자가 누구인지에 대한 정보를 포함하고 있지 않다.
- 사용자가 누구인지 확인하는 방법으로
user info
API를 만들어서 사용자의 프로필을 전달하는 방식이 널리 쓰이지만, 이 방식은 OAuth 표준이 아니다. - 좀 더 진보적이고 표준화된 접근 방식은 OAuth 2.0 익스텐션인 OpenID Connect를 사용하는 것이다.
Create an Application
- 깃헙과 마찬가지로 구글 개발자 콘솔에서 앱을 생성하고 Client ID, Secret을 발급 받는다. Redirect URL도 등록 한다.
Authorization Request
- 사용자의 데이터에 접근하지 않고, 누구인지만 확인하기 위해 Authorization 요청에
scope
값을openid email
로 지정한다. - 사용자는 권한과 관계 없이 계정을 선택하는 화면을 보게 된다.
- 사용자가 계정을 선택하면
redirect_url
로 이동하며code
,state
쿼리 파라미터가 함께 전달된다.
Getting an ID Token
-
code
를 사용해서 구글 토큰 API를 호출한다. - 구글은 요청을 검증하고,
access_token
과id_token
값을 응답한다. -
access_token
은 API 요청을 만들 때 사용할 수 있다는 점을 제외하고 우리의 앱에 큰 의미가 없다. -
id_token
은 JWT다.
Verifying the User Info
- 일반적으로 ID Token은 내부의 정보를 신뢰하기 전에 토큰을 먼저 검증을 해야 한다.
- 요청을 인증하기 위해 Client Secret을 사용하여 Google에 대한 HTTPS 연결에서 ID Token을 얻었으므로, 획득한 ID Token이 공격자가 아닌 서비스에서 온 것이라고 확신 할 수 있다.
- ID Token을 서비스 내부의 다른 컴포넌트에서 사용할 경우에는 유효성을 검증해야 한다.
- JWT 토큰은
Header.Payload.Signature
로 구성된다. Payload 부분은 BASE64로 인코딩된 JSON 문자열이다. -
ID Token의 Payload에서
sub
(subject) 항목은 사용자를 식별하는데 사용되며, 이를 세션에 저장한다. -
access_token
과id_token
도 나중에 사용하기 위해 세션에 저장한다.
Using the ID Token to Retrieve User Info
- 구글은 ID Token의 값을 조회하기 위한
tokeninfo
API 를 제공한다. -
https://www.googleapis.com/oauth2/v3/tokeninfo
- 추가적인 네트워크 호출이 발생하므로, 개발용으로만 사용하는 것이 좋다.
- 응답은 ID Token의 Payload 와 유사하다.
Using the Access Token to Retrieve User Info
- Access Token을 사용해서 사용자 정보를 조회하는
userinfo
API 를 제공한다. -
https://www.googleapis.com/oauth2/v3/userinfo
- 이 사항은 OpenID Connect 표준의 일부다.
-
sub
를 포함한 사용자 이름, 프로필 사진, 이메일, 지역 정보와 같은 추가적인 정보를 확인할 수 있다.
Server-Side Apps
- 서버사이드 앱은 소스 코드가 공개되지 않기 때문에 Client Secret 을 안전하게 보관할 수 있다.
- 사용자가 앱을 허가하면
authorizaton_code
를 전달 받는다.
Authorization Code Grant
- Authorization Code 는 Access Token 을 발급 받기 위한 임시 코드다.
- Authorization Code + Client Secret => Access Token
OAuth Security
- 새로운 서버를 구현한다면, PKCE 를 지원하도록 구현해야한다.
Authorization Request Parameters
Authorization 요청은 다음과 같은 쿼리 파라미터가 필요하다.
-
response_type=code
-
client_id
-
redirect_url
(optional) -
scope
(optional) -
state
-
PKCE
Exchange the authorization code for an access token
Access Token 발급은 POST 요청으로 이루어지며, 다음과 같은 파라미터가 필요하다
-
grant_type
(required):authorization_code
-
code
(required): Authorization Code -
redirect_uri
: Authorization 요청에서 전달 되었을 경우, Access Token 요청에서도 전달해야한다. -
Client Authentication
(required):client_id
,client_secret
를 사용해서 자격을 증명한다.
Possible Errors
Authorization 과정에서 오류가 발생하면 error_description
, error_uri
파라미터와 함께 리다이렉트 된다.
-
redirect_url
오류 -
client_id
오류 -
error=access_denied
: 사용자가 접근을 거부함 - 파라미터 오류
Security Considerations
- Client Secret 값을 보호하지 못하는 경우(모바일 앱, SPA 앱) PKCE를 사용해야 한다.
Single-Page Apps
- SPA는 Authorization Code 발급까지 동일하고, Access Token 발급에 Client Secret을 쓰지 않는다.
- 사용자는 브라우저를 통해서 API 서버(OAuth 서버)와 직접 통신한다.
- Client Secret을 사용하지 않기 때문에, 보안은
state
파라미터와redirect_url
을 사용해서 이루어진다. - OAuth 2.0 서비스에서
redirect_url
을 등록해야 하는 이유다.
Mobile And Native Apps
- SPA와 비슷하게 Client Secret 을 사용할 수 없다.
- 외부 브라우저를 실행해서 Authorization Flow 를 사용하는 것이 최선이다.
-
redirect_uri
를com.example.app://auth
와 같은 방식으로 지정해서 앱이 실행되도록 한다. -
WebView
를 사용하는 것은 보안에 좋지 않다. 모바일 기본 브라우저(사파리)를 사용하면 기존의 쿠키를 사용할 수 있다.
Making Authenticated Requests
- Access Token 을 사용하는 방법으로는 HTTP 요청의 Authorization 헤더를 사용하는 방법과, Post 파라미터를 사용하는 방법이 있다.
- Access Token 은 API 호출하는 용도로만 사용해야 한다. 토큰이 항상 같은 구조를 가진다는 보장이 없으므로 복호화해서 사용하는 것은 위험하다.
- 토큰이 만료되는 시점을 발급 받을 때 저장하거나, 일단 토큰을 사용해서 요청을 하고 오류가 발생할 경우 갱신하는 방법이 있다.
Refreshing an Access Token
-
refresh_token
과expires
값을 사용하면access_token
을 사용한 요청이 실패하기 까지 기다리지 않아도 된다. -
grant_type=refresh_token
으로 지정해서 새로운access_token
과refresh_token
을 발급 받을 수 있다. - 갱신이 실패할 경우 사용자에게 다시 로그인하도록 처리해야한다.
https://www.youtube.com/watch?v=996OiexHze0 https://curity.io/resources/architect/oauth/spa-best-practices/#storing-tokens-for-spas