OAuth 2.0 이란?
Oauth 2.0 이란?
사용자가 자신의 계정 자원에 대한 접근 권한을 제 3자 애플리케이션에게 안전하게 위임할 수 있도록 도와주는 권한 위임 프로토콜
사용자는 자신의 ID/PW 를 제 3자 앱에 직접 제공하지 않고, 신뢰할 수 있는 인증 제공자 (예를 들어 구글, 카카오)에서 로그인을 하고 권한을 승인함으로써 토큰기반으로 자원을 공유 하게 된다.
우리가 만든 서비스에서 카카오 로그인을 붙이는 경우,
사용자 인증은 카카오가 수행하고 우리는 카카오로부터 받은 Access Token 으로부터 사용자 정보를 조회해 로그인
처리를 하게 된다.
(여기서 한가지를 집고 넘어가자면 Oauth 2.0 프로토콜은 Access Token 의 발급까지만 관여한다.
사용자 정보를 조회하는 서비스는 이후 나올 OIDC, 인증 프로토콜 방식에 해당하게 된다.
많은 oauth 2.0 프로토콜이 JWT 인증 방식을 포함한 OIDC 를 활용한다. )
OAuth 2.0 은 권한 위임 프로토콜이며 이는 HTTP 프로토콜과 특징이 다르다.
HTTP 프로토콜은 TCP 수전의 네트워크에서 존재하는 약속으로 Application Layer 에서 사용하는
개발자 입장에서는 사용(전송 방식) 에 제약이 있을 수 있다.
(ex. GET, POST, PUT, DELETE 등의 전송 포맷과 요청 구조에 강제화가 된다)
하지만 인증/인가를 위한 Oauth 2.0 은 애플리케이션 계층의 인증/인가 프로토콜로 전송 방식이 강제되지는 않지만
HTTP 위에서 보안 안정성 안에서 작동하도록 설계된 규칙 세트라고 볼 수 있다. 규칙 특징은 아래와 같다.
- Authroization Code 는 반드시 Authrozation Server 에서 발급
- Access Token 은 Authrozatio: Bearer <Token> 형태로 HTTP 헤더에 포함
- Token 요청은 보통 POST 로 전송
- Redirect URL 은 HTTP 기반이어야 한다.
동작 흐름 ( Authorization Code Grant 예시)
1. 사용자가 Client 앱에서 로그인을 요청한다.
2. Client 는 Authorization Server 로 리다렉션
3. 사용자가 로그인 후 동의하면 Authorization Code 발급
4. Client 는 이 코드를 Authorization Server 에 보내고,
5. Access Token ( 필요 시 Refresh Token 도 ) 발급 받는다.
6. 이후 Client 는 Resource Server 에 Access Token 으로 API 요청
Authorization code 를 중간에 두는 이유는?
- 클라이언트와 리소스 서버를 분리 하기 위해, 사용자 인증은 Authrozation Server 에서 진행
- Access Token 은 민감하고 장기적인 권한이 담긴 값이므로 이를 사용자 브라우저(프론트 엔드) 에 직접 넘기면 보안상 위험이 된다
- 대신, 중간 단계로 Authrozation code 를 사용자 브라우저를 통해 전달 이 코드는 백엔드 서버에서 안전하게 Access token 으로 교환한다 ( 인증서버가 Access Token 을 사용자 브라우저로 바로 리다이렉션 하는것을 방지 한다.
이 방식을 Implicit Flow 라고 한다.)
- 즉 Access Token 은 클라이언트 서버 측(백엔드) 에서만 받도로 설계된다 .
- 브라우저를 통해 전달되는건 짧고 일회용인 Authorization Code 가 되며 Access Token 은 서버-서버간 POST 요청으로만 주고 받기 때문에 보안성이 강화된다.
OAuth 2.0 에서 사용되는 리다이렉트는 아래의 보안 요구사항이 존재한다.
1. 정확한 일치
- Oauth 2.0 에서는 클라이언트가 등록한 Redirect URL 와 요청 시 제공하는 URI 가정확히 일치해야 한다. 이는 등록되지 않는 URI 로의 리디렉션을 방지하여 보안을 강화하게 된다.
- Open Redirect 는 Redriect URI 를 동적으로 처리하거나, 사용자 입력을 기반으로 리디렉션을 수행하는 경우, 악의적인 사용자가 이를 악하여 파싱사이트로 유도 할 수 있으므로 , Redirect URI 는 사전에 등록된 값만 사용하고 동적 처리를 피해야 한다.
2. HTTPS 사용권장
Redirect URI 는 민감한 정보를 포함할 수 있으므로, HTTPS 를 사용하여 데이터 전송중 도청이나 변조를 방지한다.
3. 와일드 카드 사용 금지
별도로 State 파라미터를 사용하면 CSRF 공격을 방지할 수 있다.
인증 요청 시 state 파라미터를 사용하여 요청과 응답의 일관성을 확인한다
OAuth2 + JWT = OIDC
Oauth 2 인증 서버는 인증과 토큰 발급까지만 관여하게 된다. (위 내용에 대한 추가)
우리 서비스는 발급 받은 Access Token 만으로 사용자 인증을 처리함으로써
실시간으로 인증 서버에 의존하지 않아도 되는 구조를 설계하게 된다.
특히 JWT 기반 토큰을 쓰면, 토큰 자체로 사용자 식별 및 권한 확인이 가능해 서비스의 인증 독립성을 높일 수 있다.
OpenID 커넥트의 ID 토큰은 서명된 JWT 로서 일시적인 OAuth 액세스 토큰과 함께 클라이언트 애플리케이션에게 전달된다.
액세스 토큰과 달리 ID토큰은 RP 로 보내져 그것의 내용에 파싱된다.
RP = Relying Party ( 회사 서비스 )
OP = OpenID Provider ( 네이버, 카카오등의 인증 서비스)
Access Token VS ID Token
목적 : API 접근 권한 vs 사용자 신원 검증
사용처 :Resource Server vs Client (프론트/백엔드)
포함 정보 : scope , expired 등 vs sub, name, email, aud, iat, exp 등
소비 : API 서버 vs 클라이언트 애플리케이션
OIDC 로그인 인증 흐름
1. 클라이언트 -> 로그인 요청 ('scope = openid')
2. 사용자 -> 인증 + 동의
3. 인증 서버 -> Authrozation Code 리턴
4. 클라이언트 -> code 로 토큰 요청
5. 인증 서버 -> Access Token + ID Token 리턴
6. 클라이언트 -> ID Token 으로 사용자 신원 확인
프로토콜 | 역할 | 토큰 |
Oauth 2.0 | 인가 | Access Token, Refresh Token |
OIDC | 인증 | ID Token (JWT, UserInfo API) |
Access Token VS Id Token
access token 처럼 id 토큰에도 인증 세션에 대한 클레임이 포함된다. 즉, 사용자 식별자(sub) 와 토큰을 발급한 이슈 제공자를 위한 식별자 (iss) 그리고 생성된 토큰을 발급받는 클라이언트 식별자(aud) 클레임이 포함된다. 또한 ID 토큰은 토큰 자체의 유효시간 윈도우(exp와 iat 클래임으로) 에 대한 정보뿐만 아니라 클라이언트에게 전달되는 인증 컨텍스트에 대한 추가 정보를 포함한다. 예를들면 토큰을 통해 사용자가 얼마나 오래전에 인증 메커니즘상에 있었는지(auth_time) 또는 idp에 어떤 종류의 인증을 수행했는지(acr) 를 알 수 있다.
ID 토큰은 또한 JWT 클레임 뿐만 아니라 OPENID 커넥트 프로토콜을 위해 확장된 클레임도 포함 할 수 있다.
클레임 이름 | 설명 |
iss | (issuer) 토큰 발급자 : idp 의 URL |
sub | (subject) 토큰 대상, idp 에서 사용자의 대한 안정적이고 고유한 식별자. 일반적으로 프로그램이 인식 가능한 문자열이며, 사용자 이름으로 사용하면 안된다. |
aud | (audience) : 토큰 수신자 : RP 의 클라이언트 ID 를 포함해야 한다. |
exp | (expiration) 토큰의 만료 시간. 모든 ID 토큰이 만료되며, 일반적으로 매우 빨리 만료 된다. |
iat | 토큰이 발급된 시간 |
auth_time | 사용자가 idP 에 인증한 시간 |
nonce | 인증이 요청된때 RP 가 전달하는 문자열, 재전송 공격을 막기 위해 state 파라미터 처럼 사용한다. |
acr | (authentication context reference) 인증 컨텍스트 레퍼런스, 사용자가 idP에서 수행한 인증을 가리킨다. |
amr | (authentication method reference) 인증 방법 레퍼런스, 사용자가 idp에서 수행한 인증 방법을 가리킨다. |
exp | (authorized party) 토큰으로 인가된 대상. 이 클레임이 사용된다면 RP 의 클라이언트 ID가 포함돼야 한다. |
at_hash | 액세스 토큰의 해시 값 |
c_hash | 인가 코드의 해시값 |
ID 토큰은 토큰 엔드 포인트의 응답 데이터 내의 id_token 멤버로 엑세스 토큰과 함께 전달된다. 두 토큰은 각각 의도된 사용자와 사용 목적이 다르다. 토큰이 하나 추가돼 기능을 확장하는 것이기 때문에 액세스 토큰은 기존 OAuth 와 동일하게 클라이언트에게 불투명한 구조로 남을 수 있고, 반면 ID 토큰은 그 내용을 해석할 수 있다. 또한 두 토큰은 각기 다른 라이프 사이클을 가지며, ID토큰 유효기간이 보다 더 빨리 만료된다. ID토큰은 하나의 인증 이벤트만 나타내며. 다른 외부 서비스로는 절대 전달되지 않는다. 하지만 액세스 토큰은 사용자가 떠난 이후에도 보호된 리소스에 접속하는 데 사용된 수 있다. 엑세스 토큰을 사용해 처음에 누가 클라이언트를 인가했는지 물어볼 수 있지만, 그렇다고 해서 사용자가 여전히 있다고 말하는 용도로는 사용할 수 없다.
( 클레임 토큰이란? 사용자 정보나 데이터 속성들을 담고있는 토큰이, 클레임을 기반한 토큰 중 가장 대표적인 것이 JWT)
Okta Dafault Claims
https://developer.okta.com/docs/guides/validate-id-tokens/main/#verify-the-claims
Validate ID Tokens | Okta Developer
This guide explains how to validate ID tokens with Okta. Learning outcomes Retrieve and parse your Okta JSON Web Keys (JWK). Decode the ID token. Verify the signature that was used to sign the ID token. Verify the claims in the ID token. About ID token val
developer.okta.com
{
"access_token" : "987sdf...",
"token_type" : "Bearer",
"id_token" : "eyJ0eXAiOiJKV1Q..."
}
마지막으로, ID 토큰 자체가 식별 제공자의 키로 사명되기 때문에 토큰이 전달될 때 사용된 TLS 전송 보호 외에도 내부의 클레임 보호를 위한 계층이 하나 더 추가된다. 인가 서버가 ID 토큰을 서명하므로 인가 코드(c_hash), 액세스 토큰(at_hash)과는 분리된 독립적인 시그니처를 제공할 수 있다. 클라이언트는 여전히 인가 코드와 액세스 코드의 구조는 알지 못하지만 그것의 해시 값을 검증함으로써 전반적인 삽입 공격을 방지 할 수 있다.
서명된 JWT 를 검증할 때와 동일한 방법으로 ID 토큰을 검증함으로써 클라이언트는 많은 수의 일반적인 공격으로 부터 자신을 보호 할 수 있다.
1. ID 토큰을 파싱해 그것이 유효한 JWT 인지 확인 한다.
- "." 문자를 기준으로 문자열 섹션을 나눈다.
- 각 섹션을 Base64URL 디코딩 한다.
- 처음 두 섹션(헤더와 페이로드)을 JSON 객체로 파싱한다.
2. 공개된 idP 의 공개키로 토큰의 시그니처를 검증한다.
3. ID 토큰이 신뢰할 수 있는 idP 가 발급한 것인지확인한다.
4. 클라이언트 자신의 식별자가 ID 토큰의 수신자 리스트에 있는지 확인한다,
5. 만료 시간과 issued-at, not-before 타임스탬프 값이 현재 시간을 고려해 적절한지 확인한다.
6. nonce 값이 있다면 자신이 전달한 것과 동일한지 확인한다.
7. 인가 코드나 액세스 토큰의 해시 값이 있다면 그것을 검증한다.
각 검증 단계는 기계적이며, 어떤 조건을 결정하는 것이기 때문에 최소한의 코딩 작업만 필요로 한다. OpenID 커넥트의 일부 고급 모드에서는 ID 토큰을 암호화할 수 있어서 파싱과 검증 프로세스가 약간 변경되지만, 그 결과는 동일하다.
- UserInfo 엔드 포인트
ID 토큰에는 인증 이벤트를 처리하기 위해 필요한 모든 정보가 포함되기 떄문에 OpenID 커넥트 클라이언트는 성공적인 로그인을 위해 더이상의 정보를 필요로 하지 않는다. 하지만 현재의 사용자 관련 프로파일 정보를 포함하는 보호된 리소스의 UserInfo 엔드포인트에는 액세스 토큰이 사용될 수 있다.