웹 및 모바일 애플리케이션에서 안전하게 서드파티 애플리케이션에 대한 접근을 관리하기 위한 개방형 표준 인증 및 권한 부여 프로토콜 - ChatGPT
주변에서 가장 흔히 볼 수 있는 사례로 예를 들어 네이버(카카오, 구글)로 로그인
과 같이 다른 서비스의 계정으로 인증하고 회원가입 할 수 있다.
현재는 OAuth2.0 이 제일 널리 쓰이고 있으며 과거에는 OAuth1.0이 있었고 OAuth2.0의 후속으로 OAuth2.1, OIDC 등이 있다.
먼저 OAuth1.0에 대해 간단하게 알아보면
OAuth1.0에는 2.0에 비해 몇가지 단점이 있었다.
1. 스코프(권한) 개념 없음 -> 유저 입장에서 접근을 원치 않은 다른 리소스에도 접근 가능
2. 토큰 유효기간 문제 -> 탈취 시 단기간이 아닌 계속 사용자인척 데이터 부정 접근 가능
3. 제한적인 사용환경 -> 웹브라우저에서만 사용 -> 2.0에서 Grant라는 개념을 추가하며 해결. Grant 타입에는 크게 4개가 있음
4. 클라이언트 구현 복잡성-> 2.0에서 Bearer Token, TLS로 해결
5. 역할이 확실히 나누어 지지 않음 -> 2.0에서 인증 서버, 리소스 서버로 분리됨
OAuth1.0에서 경험한 문제점들을 해결하며 여러 글로벌 참여자들이 모여서 표준을 만들었다.
1. Authorization code
2. Implicit
3. Password
4. Refresh Token
+) 추가로 PKCE(마지막에 추가로 기술)
일반적인 것은 1번 Authorization code이며 1.0과 유사하다.
그럼 OAuth2.0에서 Authorization code를 사용할 때 흐름을 알아보자
그림으로 표현하면 아래와 같고 그림에서는 이해하기 쉽게 User, App으로 표현했지만 각 주체는 OAuth2.0에서 각각 역할로 불린다.
User -> Resource owner
APP -> Client
Authorization Server -> 그대로
Your API -> Resource Server
위에서 아래로 흐름이 진행된다.
여기에서는 웹 브라우저에서 ㅇㅇㅇ으로 회원가입 절차를 진행한다고 가정하자
Access token을'만' 이용한 인증 방식의 문제는 만약 토큰이 탈취당했을 때, 보안이 취약하다는 것. 그렇다고 유효기간을 짧게하기에는 유저가 자주 재인증을 해야해서 번거롭고, 유효기간을 길게하자니 탈취 당하면 오랜 유효기간동안 토큰을 부정적으로 사용할 수 있었다.
그래서 Refresh token이 등장
처음 인증하면 동시에 발급되는 Access, Refresh token을 클라이언트에서 보관한다.
Access token이 유효한 동안은 Access token을 이용하고 Access token이 만료됐을 경우 갖고 있던 Refresh token으로 Auth Server로 부터 새로운 Access token을 받는다.
Access token보다 통신빈도가 적기 때문에 Refresh token은 유효기간을 길게 설정해서 보관한다.
하지만 Refresh token도 탈취됐을경우에 대비하기 위에 Access token을 발급해주고나면 만료되는 방식을 채택하기도 한다.
출처-[NHN FORWARD 22] 로그인에 사용하는 OAuth : 과거, 현재 그리고 미래
OIDC는 Oauth을 대체하는 새로운 기술은 아니고 OAuth의 플러그인과 같은 방식으로 OAuth를 보완하기 위해 추가되는 개념이다.
그렇다면 왜 OAuth2.0에 어떤 부분을 보완하기 위해 OIDC가 등장했을까?
OAuth2.0의 주 목적은 인가(Authorization)이다. 즉, 권한을 주는데 초점이 맞춰져있다.
예시로 OAuth2.0에서 받는 Access token은 에버랜드 입장권이다.
에버랜드에서 티켓을 사면 Access token을 준다.
에버랜드 입장권(Access token)을 가지고 있으면 에버랜드 입장할 수 있는 권한이 있다는 것은 안다.
하지만 입장권(Access token)만보고 입장권의 소유자가 누구인지 신원을 알 수는 없다.
입장권으로 신원을 파악하려면 입장권에 신원을 같이 인쇄하던가, 입장권의 고유번호로 누구한테 판매된건지 조회해야 할 것 이다.
Access token에 신원을 같이 찍는 것은 불가능하고 Access token이 누구한테 발급된건지 확인하려면 다시 서버랑 한번 통신을 해야한다.
그래서 주민등록증 역할을 하는 ID Token도 같이 발급하는 티켓기계인 OIDC를 도입 한 것이다.
그래서 사용자 정보가 필요할 땐 ID Token을 복호화하면 되는 것이다.
위의 Authorization code flow에서 인증서버로부터 Access token을 받을 때 JWT 기반의 ID token을 추가로 받는다.
OAuth2.0에 비해 1/2의 통신으로 리소스를 가져올 수 있다.
아래는 토큰에 포함된 내용이다.
출처-[NHN FORWARD 22] 로그인에 사용하는 OAuth : 과거, 현재 그리고 미래
그리고 아래는 OIDC 가 진행될때 흐름이다.
출처 - https://learn.microsoft.com/ko-kr/entra/identity-platform/v2-protocols-oidc
아래 링크에서 체험해볼 수 있다.
OIDC Playground
Client secret을 사용하던 것을 일회용 암호로 대체한 것이다.
모바일 앱에서 감염된 앱이 있을 경우 redirect uri를 mobile app의 scheme url을 정상적인 앱과 동일하게 설정할 경우
감염된 앱으로 Authorization code 탈취될 수 있다. 그러면 이후에 감염된 앱이 정상적인 앱인척 Access token을 발급받을 수 있는 것이다.
아래에서 PKCE 과정을 체험 해볼 수 있다.
PKCE Playground
아래는 공식 스펙
RFC 7636
code_verifier - 임의로 생성된 43~ 128 길이의 값. A-Z, a-z, 0-9, and the punctuation characters -.~ (hyphen, period, underscore, and tilde)
code_challenge - SHA256 Hash 값을 BASE64-URL-encoded 스트링으로 변환한 값 = base64url(sha256(code_verifier))
SHA256 암호화는 단방향으로 복호화 할 수 없다.
위 그림에서 흐름을 설명해보겠다
1. app은 code_verifier
와 code_challenge
를 생성한다.
예시
{
"code_verifier":"M25iVXpKU3puUjFaYWg3T1NDTDQtcW1ROUY5YXlwalNoc0hhakxifmZHag",
"code_challenge":"qjrzSW9gMiUgpUvqgEPE4_-8swvyCtfOVvg55o5S_es"
}
code_challenge
와 code_challenge_method
를 싣어 보낸다.https://${yourOktaDomain}/oauth2/v1/authorize?
client_id=0oabygpxgk9lXaMgF0h7&
response_type=code&scope=openid&
redirect_uri=yourApp%3A%2Fcallback&state=state-8600b31f-52d1-4dca-987c-386e3d8967e9&
code_challenge_method=S256&
code_challenge=qjrzSW9gMiUgpUvqgEPE4_-8swvyCtfOVvg55o5S_es
code_verifier
를 인증서버로 보낸다. 여기서는 처음 생성했던 code verifier
원본을 보내는 것이 포인트다. curl --request POST \
--url https://${yourOktaDomain}/oauth2/v1/token \
--header 'accept: application/json' \
--header 'cache-control: no-cache' \
--header 'content-type: application/x-www-form-urlencoded' \
--data 'grant_type=authorization_code&client_id=0oabygpxgk9lXaMgF0h7&redirect_uri=yourApp%3A%2Fcallback&code=CKA9Utz2GkWlsrmnqehz&code_verifier=M25iVXpKU3puUjFaYWg3T1NDTDQtcW1ROUY5YXlwalNoc0hhakxifmZHag'
plain code verifier
를 SHA256
으로 암호화해서 처음 받았던 code challenge
와 일치하는지 확인한다.code verifier
가 일치한다면 처음 요청을 보냈던 클라이언트가 맞으므로 ID token을 클라이언트에 돌려준다. (만약 중간에 Authorization code를 탈취했다하더라도 code verifier는 알 수 없다. 단방향 암호화 기법으로 암호화 했기 때문)
{
"access_token": "eyJhb[...]Hozw",
"expires_in": 3600,
"id_token": "eyJhb[...]jvCw",
"scope": "openid",
"token_type": "Bearer"
}
PKCE의 code_verifier, code_challenge도 사용하고 OIDC의 ID token도 사용한다.
OAuth2.0은 널리 쓰이고 있지만 OIDC는 지원하지 않는 경우도 있기 때문에, 사용하려는 서드파티 서비스의 지원 범위에 따라 다르게 사용해야 한다.
참고자료
https://developer.okta.com/docs/concepts/oauth-openid/ (OIDC, PKCE)
https://dropbox.tech/developers/pkce--what-and-why- (PKCE )
https://hudi.blog/open-id/ (OIDC)
https://blog.postman.com/pkce-oauth-how-to/ (PKCE)
https://velog.io/@gruzzimo/OAuth-2.0-flow-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-Backend-GitHub
https://www.youtube.com/watch?v=Mh3LaHmA21I
https://www.youtube.com/watch?v=DQFv0AxTEgM
https://youtu.be/Gtbm5Fut-j8?si=hF_Ahv9F4yNq_oSf (PKCE ⭐⭐⭐)