OAuth 인증의 이해 3 - OIDC


이 시리즈의 모든 글 보기 : oauth


용어
OAuth : 별다른 언급이 없으면, OAuth 2.0 을 가리킵니다.
OIDC : OpenId Connect 1.0 Core 최신 판을 의미합니다.
Authorization : "사용자의 허락"을 의미하는 경우 "승인"으로, "자원에 대한 접근 제어 절차"를 의미하는 경우 "인가"로 번역했습니다. 후자가 더 넓은 의미입니다.

OAuth 의 세부 사항에 관해 설명하기에 앞서, 인가/인증이 Http 통신에서 어떻게 처리되는 지에 관한 배경 지식이 약간 필요합니다.

현실의 담배 판매 방식의 비유를 통해 알아 보겠습니다.

인가 (Authorization)

인가는 제한된 자원에 접근할 자격이 있는 지 확인하는 절차로, 인가의 결과는 금지(Forbid)와 자원(Resource) 둘 중에 하나입니다.

담배는 성인에게만 허락된 제품입니다.

누군가 담배를 달라고 하면, "성인"이라는 자격을 증명해야 하는데, 자격을 확인할 수 없는 사람이 대뜸 담배를 달라고 하면, 점원은 아무나 살 수 없다는 답변(Unauthorized 401)과 함께 성인임을 증명할 수 있는 방법을 알려 줍니다.

Http 인증 프레임워크에서는 401 응답 중, WWW-AUTHENTICATION 헤더를 포함한 것을Challenge Response 라고 합니다. 이 헤더에 자격을 증명할 수 있는 방법에 관한 정보가 포함됩니다.
참고로, Asp.Net Core 에서 쿠키 인증을 할 때, Challenge Response 대신에 인증이 가능한 주소로 Redirect(302) 하도록 설정되는데, 이 302도 Challenge 라고 부릅니다. 다만, Http 의 Challenge 응답 규정과 무관한 방식입니다.

엄격한 보안이 요구되는 경우, 증명의 수단을 알려 주는 행위도 보안 위협이 되기 때문에, WWW-AUTHENTICATION 헤더가 없는 401을 응답하거나, Bad Request(400) 답변을 할 수도 있습니다.

현실의 점원은 보통, 성인 증명 방식으로, 신분증을 제시를 요구합니다.

이러한 점원의 요구에도 불구하고, 신분증은 제시하지 않은 채, 계속 담배를 달라고하는 손님은 여전히 "자격을 확인할 수 없는 사람"에 불과해서, 점원도 같은 안내를 반복할 수 밖에 없습니다.

구매자가 안내에 따라 신분증을 제시하면, 점원은 아래의 절차를 수행합니다.

  1. 신분증이 위조된 것은 아닌 지를 검증하고,
  2. 신분증의 사진과 제시자의 얼굴을 비교하여 신분증 대상자임을 확인하고
  3. 신분증의 출생년도를 확인하여 성인 자격인지 확인합니다.

신분증 확인이 끝난, 3 번 단계에서 미성년자로 판명되면, 판매할 수 없다(Forbidden, 403)고 말하거나, 성인인 경우 담배를 제공합니다.(200)

보안 목적 상 Forbidden 대신, Not Found(404) 라고 답할 수 있습니다.

인증 (Authentication)

위 단계에서 신분증 확인(1번 ~ 2번) 과정이 인증에 해당합니다.

인증은 신분 증명이 위조된 것은 아닌 지와 제시자가 정당한 소유자인지 확인하는 절차로,
**인증의 결과는 신분 정보(Identity, User Profile)**입니다.

1 번 단계에서, 제시된 신분증이 위조되었다면, 신분증이 잘 못되었다고 답변을 합니다(Bad Request)

2번 단계에서, 신분증 대상자가 아니라는 판단이 들면, 이 또한 신분증이 잘 못되었다고 말합니다(Bad Request).

위 두 단계 중 하나라도 실패하면, 3번 단계를 진행할 수 없습니다.
이는 인가는 인증을 바탕으로 수행됨을 의미합니다.

이제, OAuth에 관한 내용을 이어나가도록 하겠습니다.

권한 부여(Authorization Grant)

OAuth 의 핵심은 접근이 제한된 데이터에 대한 접근 권한(Authorization)을 제 3 자에게 부여(Grant)하는 절차를 규정하는 것인데, 그 절차는 대체로 아래와 같습니다.

  1. 사용자클라이언트에게 접근 권한을 부여
  2. 클라이언트부여 받았다는 증명승인 서버(Authorization Server)에게 제출하고, 접근 토큰(Access Token)과 교환합니다.
  3. 클라이언트는 발급 받은 접근 토큰자원 서버에 제출하여 자원과 교환합니다.

OAuth 는 이 절차에 대한 구체적인 방법론 네 가지를 제시하고, 여기에 더해 추가로 확장할 수 있는 방법을 제시합니다.

  1. 승인 코드 부여 (Authorization Code Grant)
  2. 암묵적 부여 (Implicit Grant)
  3. 사용자 암호로 부여 (User Credentials Password Credentials Grant)
  4. 클라이언트 암호로 부여 (Client Credentials Grant)

여기에서, 가장 권고 되는 승인 코드 부여를 위한 흐름을 도식화하면 아래와 같습니다.

클라이언트가 웹 서버인 경우

웹 서버는 비공개 정보를 외부로부터 보호하는데 용이한 앱 형태입니다.
OAuth 에서는 이를 기밀 클라이언트(Confidential Client)라고 부릅니다.
기밀의 주 대상은 클라이언트 인증 정보와 접근 토큰으로, 이들을 자체적으로 저장하고 있어도 외부로 누출될 위험이 현저히 적습니다.

Authz code : Authorization Code(승인 코드)

위 절차는 두 번의 Http 세션(요청~응답)에 걸쳐 진행됩니다.

첫 번째 세션

사용자 : 요청(Http Request) → 클라이언트

승인 요청 단계 (Authorization Request)

클라이언트 : Redirect → 승인 엔드 포인트
승인 엔드 포인트: 응답(Http Response) { 동의 화면 } → 사용자

두 번째 세션

승인 요청 단계 계속

사용자 : 동의(Http Request) → 승인 엔드 포인트
승인 엔드 포인트 : Redirect { 승인 코드 } → 클라이언트

토큰 요청 단계(Token Request)

클라이언트 : Fetch { 승인 코드 } → 토큰 엔드 포인트
토큰 엔드 포인트 : 승인 코드 검증 후 Fetch 응답 { 접근 토큰 } → 클라이언트

자원 요청 단계(Resource Request)

클라이언트 : Fetch { 접근 토큰 } → 자원 서버
자원 서버 : 접근 코드 검증 후 Fetch 응답 { 사용자 데이터 } → 클라이언트

클라이언트 : 응답(Http Response) { 사용자 데이터 } → 사용자

위 과정 중에, 자원 서버는 승인 서버(의 토큰 엔드 포인트)가 발급한 접근 토큰을 검증합니다. 이는 두 서버는 접근 토큰 검증에 필요한 비밀 키를 공유할 수 있을 만큼 깊은 신뢰 관계에 있음을 의미합니다.

현실적으로, 두 서버는 같은 주체(회사)가 제공하는 다른 서비스인 경우가 대부분입니다.
예를 들어, 구글의 자원 서버에서 자원을 얻기 위한 접근 토큰의 경우, 구글의 승인 서버가 발행하는 것이죠.

참고로, 클라이언트가 네이티브 앱인 경우는 같은 절차에, 시스템 브라우저와 OS가 개입됩니다.

클라이언트가 Native 앱 인 경우

Native 앱의 특징은 사용자의 로컬 머신에서 구동된다는 것입니다.
이는 앱의 코드, 앱이 저장한 데이터가 사용자에게 노출될 확률이 매우 높습니다.

OAuth 에서는 이러한 클라이언트를 공개 클라이언트(Public Client) 라고 부릅니다.
공개 클라이언트는 어떤 수단으로도 기밀 정보를 보호할 수 없다는 특징이 있습니다.

네이티브 앱에는 Web View와 같이 브라우저와 유사한 User Agent 컴포넌트를 가진 경우도 포함됩니다. 이는 OAuth의 네이티브 앱 처리와 관련한 권고 규정(RFC 8252)을 따르기 위함입니다.

앞서 살펴 본 웹 서버 클라이언트와의 차이점은, 승인 요청이 (네이티브 앱) 클라이언트가 아닌 시스템의 웹 브라우저를 통해서 간접적으로 이뤄진다는 점과, 사용자 동의로 인해 생성된 승인 코드의 Redirect 응답은 브라우저가 아니라, 운영 체제에 의해, 클라이언트 앱에 전달된다는 점입니다.

시스템 브라우저를 사용하는 이유는, 승인 서버가 사용자 동의를 구할 때, 사용자는 승인서버에 로그인을 하기 때문입니다. 이 로그인 단계를 클라이언트 앱이 수행하면, 사용자 증명(아이디와 비밀번호)이 클라이언트 앱에 그대로 노출되는 위험이 있습니다.

Redirect 응답을 클라이언트 앱에 전달하는 과정은 각 운영체제가 제공하는 "URI를 앱으로 전달하는 기능"에 의존합니다. 이 기능을 사용하기 위해서는, 사전에 Redirect Uri와 클라이언트를 연결하는 설정을 해야 합니다. (이러한 설정은 운영체제 별로 다릅니다)

Redirect Uri와 클라이언트를 연결하는 설정도, 악의적 클라이언트가 Redirect Uri 에 의해 먼저 선택되도록 설정할 수 있는 보안 구멍이 있습니다. 이 글의 범위를 넘어서는 내용이라 다루지 않습니다.

승인 코드 부여 방식은 꽤 복잡하지만, 가장 안전한 방법으로 최우선 적용할 것을 권고하고 있으니, 기본 방식이라고 이해하는 편이 좋습니다.

그러나, 우리가 승인 서버를 구현해야 하는 입장이 아니라면, 그 흐름만 익혀 놓는 정도로 충분하니, 내용에 너무 많이 매몰되지 않아도 됩니다.

OIDC

그런데, 앞서 살펴 본 OAuth 의 절차들은 인증을 위한 것일까요? 인가를 위한 것일까요?

어떤 부여 방식을 따르더라도, 각 방식의 모든 단계가 성공적이면, 클라이언트는 자원 서버로부터

  • 자원을 획득하거나
  • 거부의 메시지를

받습니다. 이는, OAuth 는 인가 프로토콜이라는 점을 의미합니다.
OAuth 라는 이름도 Open standard for Authorization 의 약자로, "누군가가 어떤 자원에 접근할 권한이 있음을 증명"하는데 필요한 절차(Protocol)를 규격화 한 것입니다.

이러한 특성에도 불구하고, 이 슬로그의 제목은 "OAuth 인증의 이해"입니다.

인가 프로토콜로 인증하는 방식을 이해한다 정도로 해석할 수 있는데, 이것이 가능한 이유는 OIDC(OpenId Connect 1.0 Core)의 존재 때문입니다.

OAuth 확장

앞서 OAuth는 네 가지 기본 권한 부여 절차와 추가로 확장할 수 있는 방법을 규정하고 있다고 언급했습니다.

확장의 구체적인 방법은, 승인 요청 단계에서 '승인 요청’의 scope 파라미터에 특별한 값을 지정하는 것입니다.

scope={확장 이름}

scope 파라 미터는 원래 OAuth에서 "부여된 권한 범위"를 나타내기 위한 수단입니다.

예를 들어, 아래와 같이 범위를 profile 로 지정하면, 부여된 권한(Authorization Grant)이 사용자의 이름(성, 이름) 데이터라는 의미입니다.

scope=profile

OIDC는 이 파라미터의 값으로 "openid"를 지정했습니다.

OAuth 의 권한 부여 측면에서는, (승인 서버가 사용자에게 부여한) 사용자의 ID (Subject Id, sid, sub)에 접근할 수 있는 권한을 부여함을 가리킵니다.

사용자가 이 권한을 허락하면, 클라이언트는 이 ID 값을 얻을 수 있는데, OIDC에서는 이 값을 공개 ID(Open Id)라고 부릅니다. 이 값은 인증 서버에서는 유일하고, 모든 클라이언트 사이에 동일합니다.

인증 요청 (Authentication Request)

다시 말하면, 클라이언트가 승인 요청을 할 때, scope 파라미터에 “openid” 값을 설정하면, OAuth에서 규정한 인가 절차가 아니라, OIDC에서 규정한 인증 절차가 시작되는 것입니다.

승인 요청 단계 (Authorization Request)

클라이언트 : Redirect to /…?scope=openid → 승인 엔드 포인트 : 동의 화면 응답(Http Response)

OIDC 에서는 이러한 승인 요청을 인증 요청이라고 부릅니다.
인증 요청이 OIDC 인증 절차의 시작점이라고 할 수 있습니다.

위의 내용은 OIDC 1.0 Core 규정을 바탕으로 한 내용입니다.
그런데, 현실의 OIDC 구현은 scope 쿼리 파라미터를 사용하는 방식을 따르지 않을 수도 있습니다. 예를 들면, 아래와 같이 인증 엔드 포인트를 별도로 제공할 수도 있습니다.

클라이언트 : Redirect → 인증 엔드 포인트 (/connect/authorize/…)

클라이언트를 구현하는 입장에서는 이 부분에 대해, 각 OIDC 구현 서버의 개별 가이드를 따라야 합니다.

ID Token

OIDC 의 다른 특징 중 하나는 JWT의 사용의 강제입니다.

OAuth의 인가 절차에 나타나는 승인 코드접근 토큰은 클라이언트가 그 의미를 알 수 없는 암호화된 문자열입니다.

OIDC는 인증 엔드 포인트(인증을 수행하는 승인 엔드 포인트)가 수행한 사용자 인증 결과를 JWT에 담아 응답할 것을 강제합니다. JWT는 페이로드가 오프된 토큰이라, 클라이언트는 인증 결과를 즉시 알 수 있게 된 것입니다.

OIDC에서는 이 JWT를 ID 토큰이라고 명명했는데, ID 토큰에는 인증에 관한 메타 데이터, 인증의 결과, 그리고 기타 클레임이 포함됩니다.

ID 토큰의 페이로드는 보통 아래와 같습니다.

  {
   "iss": "https://www.an-openid-provider.com",
   "sub": "24400320",
   "aud": "s6BhdRkqt3",
   "nonce": "n-0S6_WzA2Mj",
   "exp": 1311281970,
   "iat": 1311280970,
   "auth_time": 1311280969,
   "acr": "urn:mace:incommon:iap:silver"
  }

역할 (OIDC Roles)

OIDC는 OAuth 인가 절차에 기반하기에, OAuth 에서 나타나는 플레이어들이 그대로 등장합니다.
다만, 인증 이라는 문맥에 맞게 호칭 또는 역할을 다르게 규정합니다.

OIDC 플레이어들은 아래와 같습니다.

OpenId 제공자 (Provider, OP)

OIDC 를 지원하는 OAuth 승인 서버입니다.
"인증 제공자(Authentication Provider)"라고 부르기도 합니다.

OpenId 인증 의뢰자 (Relying Party, RP)

사용자 인증을 OP에 의존하는 주체(서비스, 서버)를 가리킵니다.
OAuth 의 클라이언트에 해당합니다.

사용자 (End-User)

OP에 계정을 가진 사람을 가리킵니다.

특히, OP의 계정으로 RP의 서비스를 이용, 다시 말하면, OP의 계정으로 RP에 로그인하기를 원합니다.

한 서비스의 계정으로 여러 다른 서비스에 로그인하는 것을 Sigle Sign-On(SSO)이라고 합니다.

OIDC 인증 절차

OIDC에서 규정하는 인증 절차는 대체로 아래와 같습니다.

(RP 가 웹 서버인 경우)

OAuth 그림에서 용어만 바뀐 듯이 보이지만, 핵심은 자원 서버가 사라져 전체적으로 단출해졌습니다. 이 구조에서는 승인 서버가 인증과 관련한 모든 것을 책임지게 됩니다.

OAuth가 4가지 권한 부여 방식(Grant Type)을 제시하였듯, OIDC도 이 절차의 구체적인 방법을 제시합니다.

OIDC 인증 흐름 (Authentication Flows)

OIDC 가 제시하는 인증 절차(Flows)는 아래와 같이 3 가지입니다.
(OAuth 에서 Grant type 선택에 따라 인가 절차가 결정되기 때문에, Flow 라는 단어가 의미적으로 더 자연스러워 보입니다.)

Authorization Code Flow

인증 요청의 결과로 승인 코드가 발급 됩니다.
이 승인 코드로 ID 토큰을 발급 받을 수 있습니다.

Implicit Flow

인증 요청의 결과로 ID 토큰이 발급 됩니다.
때로는 접근 토큰(과 재발급 토큰)도 함께 발급됩니다.

Hybrid Flow

승인 코드와 ID 토큰(과 접근 토큰)이 모두 발급됩니다.

인증 흐름의 선택

인증 흐름은 RP가 선택할 수 있는데, 구체적인 방법은 인증 요청에 아래 쿼리 파라미터를 넣는 것입니다.

response_type={인증 흐름을 나타내는 값}


이 파라미터에 할당할 수 있는 값은 OIDC에 지정되어 있습니다.
아래 표는 할당할 수 있는 값과 그에 따라 선택되는 인증 흐름을 보여줍니다.

(복수의 값은 공백으로 구분됩니다)

흐름
code Authorization Code Flow
id_token Implicit Flow
id_token token Implicit Flow
code id_token Hybrid Flow
code token Hybrid Flow
code id_token token Hybrid Flow

Authorization Code Flow

이 흐름 중 가장 권고 되는 승인 코드 흐름의 예를 살펴 보겠습니다.

참고로, 이 흐름은 OAuth의 `승인 코드 부여’ 방식의 절차와 거의 동일하기에, 도식 보다는 Http 메시지를 통해 각 단계를 표현하겠습니다.

사용자가 OP를 통해 RP에 로그인 하기를 원합니다.

1. 인증 요청

RP는 사용자를 OP의 승인 엔드 포인트로 Redirect하면서 인증 요청 단계가 시작됩니다.

이때, repsponse_type 파라미터를 "code"로 지정하여 Authorization Code Flow 를 선택합니다.

(읽기 편의 상, 줄 바꿈과 Url-Encoded 값을 문자로 변경했습니다.)

 HTTP/1.1 302 Found
 Location: https://www.op.com/{승인 엔드 포인트}?
    response_type=code
    &scope=openid profile email
    &client_id=s6BhdRkqt3
    &state=af0ifjsldkj
    &redirect_uri=https://www.rp.com/on-authz-code-issued

redirect_uri 파라미터 값은 RP가 OP에 클라이언트로 등록할 때, 사전에 지정한 것이어야 합니다.

OP의 승인 엔드 포인트는 이 파라미터 값(https://www.rp.com/on-authz-code-issued)이 사전에 지정된 항목인지 검사합니다. 만약 등록되어 있지 않은 값이면, 에러 메시지를 응답합니다.

이 Redirect를 받은 사용자의 User Agent는 Http 프로토콜에 의해, 아래와 같은 요청을 OP에 보냅니다.

(읽기 편의 상, 줄 바꿈과 Url-Encoded 값을 문자로 변경했습니다.)

GET /{승인 엔드 포인트}?
    response_type=code
    &scope=openid profile email
    &client_id=s6BhdRkqt3
    &state=af0ifjsldkj
    &edirect_uri=https://www.rp.com/on-authz-code-issued HTTP/1.1
  Host: www.op.com

1.1 인증 요청 검증 / 사용자 동의 / 승인 코드 발급

승인 요청을 수신한 OP는 승인 요청을 자체를 검증하고, 사용자 인증과 동의 과정을 개시합니다.
이에 대한 구체적인 내용은 OIDC 3.1.2.2 ~ 3.1.2.4 까지의 내용을 참조하시기 바랍니다.

이 과정을 마치면, OP는 승인 코드를 발급하여 Redirect 응답합니다.

HTTP/1.1 302 Found
Location: https://www.rp.com/on-authz-code-issued?
    code=SplxlOBeZQQYbYS6WxSbIA
    &state=af0ifjsldkj

보시다시피 승인 코드는 응답의 바디가 아니라, Redirect Uri(https://www.rp.com/on-authz-code-issued)에 쿼리 파라미터로 붙인 형태입니다.

만약 이 과정에서 문제가 있다면 Authentication Error Message를 Redirect 합니다.

HTTP/1.1 302 Found
Location: https://www.rp.com/on-authz-code-issued?
    error=invalid_request
    &error_description=Unsupported response_type value
    &state=af0ifjsldkj

보시다시피, RP의 /on-authz-code-issued 핸들러는 승인 코드를 받는 경우와 에러 메시지를 받는 경우를 모두 처리해야 합니다.

참고로, 승인 코드의 발급 단계 이후부터는 OAuth와 동일합니다.

2. 토큰 요청

승인 코드를 수신한 RP의 /on-authz-code-issued 핸들러는 승인 코드를 OP의 토큰 엔드포인트로 보내 ID 토큰(과 때로는 접근 토큰)을 (Fetch)요청합니다.

curl --request POST
–url ‘https://www.op.com/{토큰 엔드 포인트}’
–header ‘content-type: application/x-www-form-urlencoded’
–data grant_type=authorization_code
–data ‘client_id={RP의 클라이언트 ID}’
–data client_secret={RP의 비밀번호}
–data code=SplxlOBeZQQYbYS6WxSbIA
–data ‘redirect_uri=https://www.rp.com/on-authz-code-issued

보시다 시피 폼 데이터로 토큰 요청에 필요한 정보를 제공합니다.

grant_type=authorization_code
OAuth 의 Authorization Code Grant를 지정하여, 승인 코드를 보내고 있음을 알립니다.

client_id, client_secret
RP가 OP에 등록할 때 부여 받은 것입니다.

code
승인 엔드포인트로 받은 승인 코드입니다.

redirect_uri=https://www.rp.com/on-authz-code-issued
아래에 다시 설명하겠지만, OP의 토큰 엔드 포인트는 Redirect 가 아닌 OK(200) 을 응답합니다.
따라서, 이 값은 OP의 응답과 직접적인 연관이 없습니다.

그럼에도 불구하고 RP가 이 값을 제공하는 이유는 토큰 요청은 승인 요청과 별개의 Http 세션이기 때문입니다. 토큰 요청 세션에서는 승인 요청 시 제출한 return-uri 값과 토큰 요청에 포함된 값이 일치하는 지 검사하여, 승인 요청과 토큰 요청의 연관성을 확인할 필요가 있기 때문입니다.

토큰 요청의 검증이 완료되면, OP는 토큰을 응답합니다.

HTTP/1.1 200 OK
  Content-Type: application/json
  Cache-Control: no-store

  {
   "access_token": "SlAV32hkKG",
   "token_type": "Bearer",
   "refresh_token": "8xLOxBtZp8",
   "expires_in": 3600,
   "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc
     yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5
     NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ
     fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz
     AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q
     Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ
     NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd
     QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS
     K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4
     XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"
  }

Cache-Control: no-store
Http 응답의 Cache-Control 를 통해, 이 응답에 포함된 값이 캐시되지 않도록 합니다.

보시다 시피, 접근 토큰, 재발급 토큰, 그리고 ID 토큰이 발급되었고, ID 토큰은 JWT 형식입니다.
만약, 토큰 요청에 문제가 있다면, 아래와 같이 에러 메시지를 응답합니다.

  HTTP/1.1 400 Bad Request
  Content-Type: application/json
  Cache-Control: no-store

  {
   "error": "invalid_request"
  }

RP는 ID 토큰에 충분한 ID 정보, 예를 들어, Email 이 있다면 여기서 인증을 끝낼 수 있습니다.
그렇지 않다면, OP의 사용자 정보 엔드 포인트에 접근 토큰을 보내, 사용자 정보를 추가적으로 얻을 수 있습니다.

이 과정은 접근 토큰을 얻는 과정과 동일하니 생략하도록 하겠습니다.

Implicit Flow

이 흐름은 Authorization Code Flow 와 완전히 동일한데, 인증 엔드 포인트가 인증 요청에 대한 응답으로 승인 코드 대신, 모든 토큰을 Redirect 하는 차이만 있습니다.

즉, 승인 요청 => 승인 코드 => 토큰의 과정이, 승인 요청 => 토큰으로 압축된 형태라고 볼 수 있습니다.

1. 승인 요청

RP의 Redirect 응답을 받은 사용자의 브라우저는 아래의 요청을 인증 엔드 포인트로 보냅니다.

GET {승인 엔드 포인트}?
    response_type=id_token token
    &client_id=s6BhdRkqt3
    &redirect_uri=https://www.rp.com/on-idtoken-issued
    &scope=openid profile
    &state=af0ifjsldkj
    &nonce=n-0S6_WzA2Mj HTTP/1.1
  Host: www.op.com

response_type=id_token token
Implicit Flow 를 선택했고, ID 토큰과 접근 토큰을 모두 요청합니다.

client_id
압축된 과정이기에, 토큰 요청 단계에서 제출할 클라이언트 ID를 승인 요청 단계에서 제출해야 합니다. 이때, client-password 는 제출하지 않습니다.

1.1 승인 엔드 포인트의 응답

승인 요청의 검증, 클라리언트 확인, 사용자 인증, 동의 확인을 끝낸 OP는 승인 코드 대신, 아래의 Redirect 응답을 해야 합니다.

(읽기 편의를 위해, Id 토큰 값 줄임)

HTTP/1.1 302 Found
  Location: https://www.rp.com/on-idtoken-issued#
    access_token=SlAV32hkKG
    &token_type=bearer
    &id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso
    &expires_in=3600
    &state=af0ifjsldkj

보시다시피, Redirect Uri 에 모든 토큰이 발급되어 있습니다.

Fragment 파라미터 논란

승인 엔트 포인트는 토큰들을 Return Uri에 추가했는데, (?로 시작하는) 쿼리 파라미터가 아닌 (#으로 시작하는) fragment 파라미터가 사용되었습니다. .

그러나, fragment 파라미터를 승인 엔드포인트 URI에 사용하는 것은 OAuth 2.0 3.1 에서 금지하는 방식이라는 의견도 있는데, 이는 문맥 없이 자구만 따지는 협의의 해석 같습니다.


The (Authorization) endpoint URI MAY include an “application/x-www-form-urlencoded” formatted (per Appendix B) query component ([RFC3986] Section 3.4), which MUST be retained when adding additional query parameters. The endpoint URI MUST NOT include a fragment component.

위 글 마지막 부분에 "The endpoint URI"를 OAuth 에 등장하는 모든 endpoint URI로 해석하면, 어떤 경우라도 fragment 파라미터가 쓰이면 안된다는 의미이지만, 이 내용(3.1)은 승인 엔드 포인트에 관한 것이라서, Authorization endpoint URI만 가르킨다고 볼 수도 있습니다.
이 경우, Return Uri 에는 해당이 없는 것이죠.(써도 된다는 의미)


말로만 설명하는 부분은 이 글이 마지막입니다.
다음 글에서는 구글의 OAuth 인증을 코드로 어떻게 구현하는 지에 대해 알아 보도록 하겠습니다.

(혹시나 틀린 내용이 있다면, 많은 딴지 부탁드립니다)

9개의 좋아요

시간나시면 Private 한 스터디 같은것 생각없으신가요?

2개의 좋아요

저는 업무적인 미팅을 오프라인으로 진행하는 것을 선호하지 않습니다.

특히, 깊은 주제에 관한 것이라면, 전화, 문자, 카톡도 비효율이라고 생각하며, 가급적 메일로 통신하는 것을 선호하는 편입니다.

제가 이 포럼에 글을 올리는 이유도 메일과 같은 맥락입니다.

제가 쓴 글은 시간이 지나도 여기에 있을 것이고, 그 시간 중 누군가 볼 것이고, 그 중에 누군가는 딴지를 걸며 답장을 할 것입니다.

그 답장을 통해서 저도 배우기를 바라는 것이죠.

다만, 제가 글 쓰는데 많은 공을 들이듯 딴지를 거는 분도 가급적 공을 들였으면 하는 바램은 있습니다.

어디 모여서 스터디를 하기 보다는 댓글을 통해 서로의 의견이나 지식을 나눴으면 합니다.
물론, 술 한잔 먹자는 요청은 언제나 환영입니다. ^^

6개의 좋아요

왜 쿼리 파라미터를 쓰는 걸까요? HTTPS를 쓰면 중간에 가로챌 일은 없다지만 제 생각으로는 적법한 엔드유저에게 조차도 코드가 노출되면 안되는 것 아닌가 싶거든요. 엔드유저 클라이언트에 심어진 악성코드가 있을 수도 있고 말이죠.

예를들어 브라우저 확장 js코드는 url등 데이터를 볼 수 있겠지만 http-only 쿠키에는 접근하지 못하죠.

JWT토큰의 페이로드 부분(롤, user 신원등) 도 누구나 복호화해 볼 수 있는데 이 또한 이해되지 않더라고요.

왜 냐면 어떤방식으로든 자신의 토큰 구성 요소를 알 수 있다면 조작의 시도가 한타래 풀리는 것이고요. 보통 인가를 위한 Role을 커스텀 문자열로 지정하는데 필요한 롤이 무엇인지 알 수 있겠죠.

제가 너무 방어적으로 생각하는 것일뿐인지 모르겠습니다.

3개의 좋아요

요즘 Auth 서버 구성고민이 왜 어려운가 했는데 이글을 보니 OIDC 에는 리소스서버 등록과 인증/인가에대한 내용이 없늘걸까요? 아니면 엔드유저 인증인가에대해서만 다루고 있는 내용이라서 일까요?

퍼스트파티(주체가 같은 회사)내 에서는 Auth Server와 Resource Server의 비밀공유, 그리고 엔드유저의 OP 인증등 문제는 프로토콜이 어렵지만 이렇게 공유가 되는데,
저는 최근 고민이 여러 세컨 파티의 리소스서버 승인절차까지 하려다보니 잘 이해가 안되더라고요.

2개의 좋아요

이게 한국이나 외국이나 이정도로 인증을 정리하신분이 없을것 같네요
제가 인증때문에 고생해서 안찾아본 자료가 없는데 귀한 자료 감사합니다.

2개의 좋아요

저도 누군가의 설명을 찾아 헤맸었고 많은 글을 읽었습니다.
그런데, 그 자료들이 약간 씩 오류도 있고, 같은 내용을 완전히 다르게 설명하는 부분도 적지 않았습니다. 심지어 MS 가 제공하는문서에서도요.
What Is OpenID Connect (OIDC)? | Microsoft Security

그래서, 스펙 원문을 찾아 보게 된 것입니다.
이 글의 예제는 대부분 스펙에서 발췌한 것입니다.

그러나, 스펙을 직접 읽다 보니, 스펙 자체가 뭔가 날림으로 만들었다는 느낌이 들었습니다.
그 허술함이 읽는 사람, 구현하는 사람들에게 혼동을 주게 된 것 같더군요.

그래서 누군가에 의해 정제된 내용 보다는 직접 정제하기로 한 것입니다.
이 글 또한 제가 정제한 것이기 때문에, 시간이 나시면 군데군데 박혀 있는 링크를 따라 스펙 원문을 읽어 보기를 추천합니다.

인증/인가에 대한 이해를 간절히 소망하는 개발자라면, 충분히 투자할 가치가 있을 것입니다.

2개의 좋아요

엔드 유저에게 노출하지 않겠다는 의도는 맞는데, 그 의도를 위한 장치는 쿼리 파라미터라기 보다는 Redirect 자체라고 봐야 할 것 같습니다.

OIDC에서는 자원 서버(Resource Server)라는 Role을 지정하지 않고 있습니다.
이는 OIDC는 OAuth 의 절차 중 인증만을 다루기 때문입니다.

다음 글에 언급하려 했던 내용인데, OAuth 인증 서버를 직접 구현하여 운영하는 것은 상당히 많은 개발 자원이 투입되는 반면, 그 효익은 크지 않을 확률이 높습니다.

따라서, 외부 패키지를 사용하는 것이 좋을 듯 합니다.
다음 글에서 몇 프레임워크를 소개할 예정입니다.

여러 세컨 파티의 리소스서버 승인절차가 어떤 걸 의미하는 지 이해가 잘 되지 않는군요.

세컨 파티가 자원을 보유한 서버이든 아니든, 내 승인 서버에게 승인 요청을 보낸다면, OIDC 에서는 Relying Party, OAuth 에서는 Client 의 지위를 가질 뿐입니다.

만약, 내 승인 서버가 발급한 접근 토큰의 비밀 키를 그들에게 공유한다는 의미라면, 상당한 신뢰 관계를 확립할 방안을 마련하는 게 핵심일 것 같은데, 쉽지는 않을 것 같습니다.

2개의 좋아요

이 이유에 대해 실마리를 찾았습니다.

일단 뭔가 데이터를 리다이렉팅으로 다른 도메인에 보내는데는 웹브라우저와 서버의 보안정책때문에

http 헤더, 로컬/세션스토리지등 안되는등 한정적이잖아요? url 쿼리나 post body 만 남는데
url쿼리에 인증 코드를 노출해도 매우짧은시간 1회성 난수 이기때문에 노출되어도 문제가 없다는 것같습니다.
이과정에 약속된 리다이렉팅 url로도 제한되고요.

그리고 웹페이지가 타겟사이트로 옮겨온뒤로는 토큰전송/응답은 서버통신으로 이루어지기때문에 토큰은노출이 안되니 안전하고
그걸 타겟사이트 내에서 사용자 인증등록(HttpContext.User)하면 사용자 세션 전역적으로 인증처리 할수 있기 때문같습니다.

2개의 좋아요

이 글은 OIDC의 기본 스펙을 기반으로 했는데, 분량 상 상당 부분을 생략했습니다.

@네임스페이스 님의 글을 보니까, 생략해서는 안 되는 내용이 생략된 것 같아 추가합니다.

Response Mode

승인 엔드 포인트가 승인 결과를 응답할 때, 그 결과를 나타내는 값을 Http 응답의 파라미터에 넣게 되는데, 그 파라미터를 지정하는 것이 response_mode 입니다.

response_mode= { 승인 응답의 값을 응답하는 방법 }

…response_type=code&response_mode={지정된 값}…

OIDC에서 지정한 값들은 아래와 같고, 향 후 HTML5 에 맞게 새로운 값도 추가될 수 있음을 명시했습니다.

query
fragment
form-post

이 파라미터를 지정하지 않으면, 기본값이 적용되는데, 기본 값은 인증 흐름에 따라 다릅니다.

승인 코드 흐름에서는 query 이고, 승인 코드가 결부되지 않은 간소화된 흐름에서는 fragment 입니다. 본문의 예제에서 이 파라미터를 지정하지 않았기 때문에, 기본 값이 적용된 것입니다.

그러나, query 파라미터는 브라우저에 의해 URI와 함께 기록되기 때문에 안전하지 않습니다.
따라서, 보안 상 이유로 form-post 가 권고 됩니다.

form_post를 지정하는 Http 요청 흐름의 예를 살펴 보면,

1. 승인 요청 (RP가 브라우저를 Redirect)

응답 모드를 form_post 로 지정합니다.

response_type=id_token 은 OIDC에서 추가된 것으로, ID 토큰을 승인 요청 단계에서 발급하도록 합니다.

GET www.op.com/authorize?
   response_type=id_token
   &response_mode=form_post
   &scope=openid
   &redirect_uri=https%3A%2F%2Fhttps://www.rp.com/on-form-submit
   &state=DcP7csa3hMlvybERqcieLHrRzKBra
   &nonce=2T1AgaeRTGTMAJyeDMN9IJbgiUG HTTP/1.1

2. 승인 엔드 포인트의 응답

승인 엔드 포인트 form 이 포함된 html 문서를 응답합니다.
이 form 의 메서드는 post로 지정되고, action attribute 에 RP의 주소가 들어갑니다.

HTTP/1.1 200 OK
  Content-Type: text/html;charset=UTF-8
  Cache-Control: no-cache, no-store
 Pragma: no-cache

  <html>
   <head><title>Submit This Form</title></head>
   <body onload="javascript:document.forms[0].submit()">
    <form method="post" action="https://www.rp.com/on-form-submit">
      <input type="hidden" name="state"
       value="DcP7csa3hMlvybERqcieLHrRzKBra"/>
      <input type="hidden" name="id_token"
       value="eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiw
         iYXVkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLC
         Jpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5M
         DMxMTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1B
         SnllRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0F
         NTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2Mz
         kwMDg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqq
         wwq-Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fk
         IufbGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcq
         XVEKhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_h
         nrQ5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc
         0uVAwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA"/>
    </form>
   </body>
  </html>

3. 브라우저의 POST 요청

위 문서를 응답받은 브라우저는 아래 태그에 의해,

<body onload="javascript:document.forms[0].submit()">

form 을 submit 합니다.

  POST https://www.rp.com/on-form-submit HTTP/1.1
  Content-Type: application/x-www-form-urlencoded

  id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiwiYX
         VkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLCJpc
         3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5MDMx
         MTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1BSnl
         lRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTD
         oyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2MzkwM
         Dg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqqwwq
         -Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fkIuf
         bGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcqXVE
         KhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_hnrQ
         5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc0uV
         AwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA&
  state=DcP7csa3hMlvybERqcieLHrRzKBra

이 Post 요청을 받은 RP의 on-form-submit 엔드 포인트는 토큰 요청 단계를 시작하거나, ID 토큰에 충분한 ID 정보가 포함되어 있다면, 토큰 요청을 하지 않고, 자체적인 인증 티켓을 (쿠키로) 발급하여 응답합니다.

인가 와 인증의 구분이 잘안가네요 무지성으로 쓰고 있긴 하지만

인가 신분증을 제시 요구 (일련의로그인 과정?)
인증 신분증을 제시하고 권한 부여 ??
그러면 인증만으로 다 할수있는것 아닐까요??
두가지 개념이 대단히 유사해보이고 구분해야 하는 절차인지
확실히 정의가 안가네요

혹시 인가가 로그인 과정을 통해서 ? 해당 자격증명이 부여된 유저이고
(세션에 키가 존재 )
인증은 자원에 접근할때 로그인 과정 통해 얻은 키로
권한체크하는 과정인지

신분증은 로그인의 결과물, 다시 말하면 로그인을 통해 발급됩니다.

현실의 신분증, 예를 들면 민증은 기한이 없다고 할 수 있어, 재발급이 흔치 않지만, 웹에서는 신분증의 기한이 매우 짧게 정해져 있어, 재발급을 자주 받아야 합니다.

민증 재발급 받을 때, 최초에 등록된 지문을 검사하듯, 웹 신분증을 재발급 받을 때도, 최초 가입(Register, Sign up)시에 등록된 Credentials(id, password) 를 제시해야 하는 것입니다.

방문자는 이렇게 (재)발급 받은 신분증을 포함하여 웹서버에 새로운 요청을 보냅니다.
만약 신분증이 쿠키라면, 브라우저가 알아서 포함시켜 주고, 토큰이라면 보내는 쪽에서 직접 포함시켜야 합니다.

인증은 요청에 포함된 신분증을 검사하는 절차입니다.
요청에 신분증이 없다면, 인증은 그 요청에 "신분증 확인 안됨"으로 처리합니다.
(HttpContext.User.Identity.IsAuthenticated = false)

신분증은 제시 받은 측(인증을 수행하는 측)이 검증이 가능한 상태이어야 합니다.
비밀 토큰 형태의 신분증만 검증할 수 있는데, JWT를 제시한다든지, 쿠키로 제시해야 하는데, 파라미터로 제시하면 인증을 통과할 수 없습니다.

인증이 통과되면, 신분증이 가리키는 사람과 요청을 보낸 사람이 같다고 확신할 수 있는 것입니다.

인증 이후로는 신분증이 곧 그 사람이 되는 것입니다.
그런 의미로 Asp.Net Core 에서 인증이 통과되면, HttpContext.User 가 그 신분증 값으로 설정되는 것이죠. 그 결과로 (HttpContext.User.Identity.IsAuthenticated = true 가 되는 것입니다.

인가는 인증에서 검증된 신분증의 항목(HttpContext.User.Identity.Claims)을 바탕으로, 자원에 접근할 수 있는 권한이 있는지 확인하는 것입니다.
유저 등급 항목이 노멀(“userClass”:“normal”)인 유저가 관리자 등급(“userClass”:“admin”)에게만 허락된 자원에 접근하려고 하면 금지(Forbid) 시키고, 관리자 등급이면 자원을 제공(Ok)하는 것이죠.

만약, 인증을 거치지 않은 사람이 인가에 의해 보호되는 자원을 요청한다면, 인가 절차는 Forbid 나 Ok를 응답하는게 아니고 Challenge 를 응답하게 됩니다. (인증부터 통과하세요)

1개의 좋아요

https://httpd.apache.org/docs/2.2/ko/howto/auth.html

링크의 내용중 일부를 발췌한 부분입니다.

“인증(authentication)은 자신이 누구라고 주장(Claim)하는 사람을 확인하는 절차이다. 권한부여(authorization)는 가고 싶은 곳으로 가도록 혹은 원하는 정보를 얻도록 허용하는 과정이다.”

asp.net core를 사용하셔서 로그인처리 부분을 하시게 되면 아이디와 비밀번호만으로 인증처리해서 @BigSquare 님의 윗글 처럼 HttpContext.User.Identity.IsAuthenticated이 true 인지만 확인해서 사용하는 경우도 있을 겁니다.

헌데, 특정 메뉴등의 자원에 대한 접근을 제어할려면 Claim에 정의된 Role등으로 제어할 수 있습니다.

제 생각에 authentication의 일부로 authorization이 따라가겠지만, 특정기능을 통해서 Claim을 변경할 수도 있으니 별개로도 볼 수 있을 것 같습니다.

1개의 좋아요

이 부분은 로그인 요청과 자원 요청이 별개의 요청으로 진행된다는 점을 간과하신 것 같습니다.

로그인 요청의 응답에 신분증이 포함되고, 그 신분증이 그 다음 요청에 포함되는 것이죠.

Asp.Net core 에서는 인증 미들웨어는 응답을 수행하지 않습니다.
HttpContext.User 만 할당하고, 요청 파이프라인으로 넘깁니다. 이 값은, 그 다음 미들웨어나 요청 핸들러, 특히 인가 미들웨어가 사용되는데, 인가 미들웨어는 응답을 수행합니다.

해당 글의 내용은 요청이 하나로 처리된다는 뜻이 아닙니다. 해당글이 어찌해서 간과인지요?

한국어(한자어)나 영어나 용어가 헷갈린데, 반대입니다.

신원인증(Authentication): 누구인가?
권한인가(Authorization): 이 사람은 어딜 들어갈 수 있나?

참고로 HTTP Authorization 헤더의 이름은 위 구분이 생기기 전에 정해진 거라 그런 이름일뿐이라고 @BigSquare 님이 조사해주셨다고 하셨고요.

회사 정문을 통과해도(신원인증), 권한이 없으면 보안구역에 접근이 불가(권한인가)한 그런 구분이라고 생각해도 될 것 같습니다.

이러한 개념은 커뮤니티 사이트에서도
로그인 (인증): 회원인지?
레벨(인가): 특정 게시판에도 접근가능한지?
이런 형태로 구분되겠고요.

예전엔 구분을 안했나 본데, OIDC 프로토콜로 확장하면서 나누어진건지 모르겠습니다.

아이디와 비밀번호는 신분증 발급에 필요한 것인데, "인증 처리"라고 표현하신 부분 때문에, 로그인과 인증이 동시 진행되는 듯한 뉘앙스로 받아 들였습니다.

만약 동시에 이뤄지도록 설계한다면, 사용자는 매 요청마다 아이디와 비밀번호를 제시해야 하는데, 그렇게 설계된 웹서버는 현실적으로 존재하기 힘들죠.

보충 설명 감사합니다만, 로그인 != 인증이라는 점을 강조하고 싶습니다.

로그인 : 신분증 발급
인증 : 신분증 검사

1개의 좋아요

제 글이 너무 어렵나 봅니다.
(그냥 지우고 새로 쓸가요? ㅠㅠ)

우리 웹서버가 자체 인증을 수행하는 경우, 신분증 발급(로그인), 신분증 검사(인증), 자격을 확인(인가)하는 책임은 전부 우리 서버에 있습니다.

우리 웹서버가 발급한 신분증은 암호화 기술로 인해, 우리 웹서버만 검사할 수 있습니다.
이렇게 인증 절차에 신분증을 사용하는 이유는 사용자가 매 요청 마다 사용자 비밀 정보를 제시하지 않도록 하기 위한 것입니다.

만약 신분증 시스템을 채택하지 않으면, 보호된 자원을 요청할 때, 사용자는 항상 비밀 정보를 제시해야 합니다.

신분증 없는 인증

웹 클라이언트 : Request (/profile) (+ 사용자 Id, password) => 인증 → 인가 → /profile 엔드 포인트

웹 클라이언트 : Request (/picture) (+ 사용자 Id, password) => 인증 → 인가 → /picture 엔드 포인트

이때 인증은 사용자 비밀 정보를 바탕으로 사용자를 확인하고, HttpContext.User를 할당합니다.

이렇게 하면, 매번 아이디와 비밀번호를 입력해야 해서 사용자의 불편이 크다는 문제점 뿐만 아니라, 모든 요청에 비밀 정보가 포함되어 유출의 위험도 커진다는 보안 위협도 있습니다.

그래서, 최초 로그인 시에 우리 웹서버만 검사 가능한 신분증을 발급하는 것입니다.

로그인을 통한 신분증 발급

웹 클라이언트 : Request (/login) => 인증 미들웨어 → 인가 미들웨어 → /login 엔드 포인트

/login 엔드 포인트 : Response with 신분증

인증 미들웨어는 무조건 요청에 포함된 신분증을 검사합니다.
그런데, 로그인 요청에 신분증이 있을 리가 없죠.
그래서, 단순히 "인증 안됨"으로 처리하고, 요청을 그 다음 파이프라인으로 넘깁니다.
(응답을 수행하지 않습니다)

또한, 인가 미들웨어는 /login 엔드 포인트를 보호하지 않습니다. (로그인을 보호하면 아무도 로그인을 할 수 없죠)

이는 인증 미들웨어가 어떤 값을 설정해도, 그 값을 전혀 참조하지 않는다는 의미입니다.

결론적으로, 신분증 발급(로그인) 단계에서는 신분증 검사(인증)도 자격 확인(인가)도 일어 나지 않습니다.

로그인 후 자원 요청

웹 클라이언트는 이전에 보낸 로그인 요청으로, 신분증을 발급받았습니다.
그 다음 모든 요청에 (사용자의 비밀 정보 대신) 이 신분증을 포함시켜, 자신이 누구인지를 알게 합니다.

웹 클라이언트 : Request (/data) + 신분증 => 인증 미들웨어 → 인가 미들웨어 → /data 엔드 포인트

/data 엔드 포인트 : Response with 데이타

이때, 인증 미들웨어는 요청에 포함된 신분증을 검사합니다. 그 검사 결과를 HttpContext.User 에 할당합니다. 적절한 신분증이라면, 올바른 사용자 정보로 설정할 것입니다.

인가 미들웨어는 HttpContext.User 에 포함된 Claim 중에 /data 엔드 포인트가 요구하는 조건에 부합하는 것이 있는지 확인합니다.
부합하지 않은 경우, Forbid 응답하고, 부합하는 경우, /data 엔드 포인트를 실행시켜 응답을 하게 합니다. (OK)

로그인 없이 자원 요청

로그인을 하지 않고 보호된 자원을 직접 요구할 수도 있습니다.
Http 요청은 주소만 알면 누구나 보낼 수 있기 때문입니다.

이때, 당연히 요청에는 신분증이 포함되어 있지 않게 됩니다.

웹 클라이언트 : Request (/data) => 인증 미들웨어 → 인가 미들웨어 → /data 엔드 포인트

인증 미들웨어는 자신이 검사해야할 신분증을 확인하지 못했으므로, “인증 안됨” 으로 처리합니다.

인가 미들웨어는 신분 정보가 없기 때문에 무슨 자격이 있는 지조차 확인할 수 없습니다.
이 경우, Challenge 를 응답합니다.

그럼 OIDC 는?

앞서 내용의 전제는 인증을 우리 서버가 직접 수행하는 것입니다.
OIDC는 인증을 우리가 수행하지 않고, 외부 인증 제공자가 수행하게 하는 것입니다.

우리 서버는 그 인증 제공자가 수행한 결과- 사용자 정보만 사용할 뿐입니다.
자체 인증의 결과로 사용자 정보를 얻든, 외부 인증의 결과로 사용자 정보를 얻든, 인증의 결과물이라는 점은 동일합니다.

그럼 그 방식은 어떻고, 그 과정에서 Http 요청의 흐름은 어떻게 되느냐, 그걸 위해서 코드를 어떻게 적어야 하느냐… 등이 다음 글의 주제입니다.

1개의 좋아요

아니요 지우다니요 감사합니다

1개의 좋아요