이 페이지에서

OAuth 2.0 개요

RFC 6749 - OAuth 2.0 인증 프레임워크는 타사 애플리케이션을 사용하여 서비스에 대한 인증을 수행하는 프로토콜을 규정합니다. OAuth 2.0은 토큰을 사용하여 서비스와 사용자 간의 인증 과정을 추상화합니다. 이 방법은 서비스 소유자가 사용자 인증 정보를 직접 처리할 필요가 없으므로 더 안전합니다. 이는 RFC 5849 OAuth 1.0을 대체하는 표준입니다.

OAuth 2.0 프레임워크는 공개(public) 또는 비공개(confidential)의 두 가지 클라이언트 유형과, 인증 코드 흐름 (authorization code flow), 암시적 코드 그랜트(implicit code grant) 등 여러 가지 인증 흐름을 정의합니다. 일반적인 Qt 애플리케이션은 공개 네이티브 애플리케이션으로 간주됩니다. 공개 클라이언트 애플리케이션은 비밀번호와 같은 민감한 정보를 보유할 수 없어, 배포된 바이너리 내에 포함될 수 없는 애플리케이션을 말합니다.

RFC 8252 ‘네이티브 앱을 위한 OAuth 2.0’은 네이티브 애플리케이션에 대한 모범 사례를 더욱 상세히 정의합니다. 특히, RFC 8252는 브라우저를 통한 인증 흐름을 권장합니다. 따라서 ` QtNetworkAuth ` 클래스는 이 흐름의 구체적인 구현을 제공합니다.

Qt 6.9의 새로운 기능인 QtNetworkAuth 는 RFC 8628 - OAuth 2.0 장치 인증 그랜트(Device Authorization Grant)를 지원합니다. 이 장치 흐름은 입력 기능이 제한적이거나 실용적이지 않은 장치를 대상으로 합니다. 이 흐름에서 인증 그랜트는 해당 기기 대신 스마트폰과 같은 보조 기기를 사용합니다. 이러한 기기의 예로는 텔레비전, 미디어 콘솔, 기계용 HMI 및 IoT 기기가 있습니다. 사용자는 스마트폰의 애플리케이션을 사용하여 기기에 대한 인증을 수행할 수 있습니다.

다음 표는 Qt Network 인증에서 지원하는 두 가지 OAuth 2.0 흐름을 보여줍니다:

측면인증 코드 흐름기기 인증 흐름
네트워크 연결예예
사용자 상호작용동일 기기의 브라우저/사용자 에이전트다른 기기의 브라우저/사용자 에이전트
리디렉션 처리 필요예아니요
기기 내 입력 기능풍부한 입력 기능입력 기능이 제한적이거나 없음
대상데스크톱 및 모바일 앱TV, 게임 콘솔, HMI, IoT 기기

OAuth 2.0을 사용하려면 일반적으로 브라우저인 사용자 에이전트를 사용해야 합니다. 자세한 내용은 Qt OAuth2 브라우저 지원을 참조하십시오.

OAuth 2.0 클래스

Qt Network Authorization은 구체적인 OAuth 2.0 클래스와 추상적인 OAuth 2.0 클래스를 모두 제공합니다. 추상 클래스는 사용자 정의 흐름을 구현하기 위한 것이며, 구체적인 클래스는 구체적인 구현을 제공합니다.

C++ 클래스 목록은 ‘ QtNetworkAuth ’ 페이지를 참조하십시오.

Qt Network Authorization에는 OAuth 2.0 흐름을 구현하기 위한 두 개의 추상 클래스가 있습니다:

인증 코드 흐름

이 섹션은 네이티브 애플리케이션을 위한 RFC 6749 - 인증 코드(Authorization Code ) 및 RFC 8252 - 네이티브 앱의 인증 요청(Authorization Request from a Native App )에 기반한 인증 코드 흐름에 대한 개요입니다.

다음과 같은 예시 설정을 고려해 보겠습니다:

QOAuth2AuthorizationCodeFlow m_oauth;
QOAuthUriSchemeReplyHandler m_handler;

m_oauth.setAuthorizationUrl(QUrl(authorizationUrl));
m_oauth.setTokenUrl(QUrl(accessTokenUrl));
m_oauth.setClientIdentifier(clientIdentifier);
m_oauth.setRequestedScopeTokens({scope});

connect(&m_oauth, &QAbstractOAuth::authorizeWithBrowser, this, &QDesktopServices::openUrl);
connect(&m_oauth, &QAbstractOAuth::granted, this, [this]() {
    // Here we use QNetworkRequestFactory to store the access token
    m_api.setBearerToken(m_oauth.token().toLatin1());
    m_handler.close();
});

m_handler.setRedirectUrl(QUrl{"com.example.myqtapp://oauth2redirect"_L1});
m_oauth.setReplyHandler(&m_handler);

// Initiate the authorization
if (m_handler.listen()) {
    m_oauth.grant();
}

인증 흐름 단계

RFC 6749 인증 코드 흐름에는 두 가지 주요 단계가 있습니다. 첫 번째는 리소스 인증(필요한 사용자 인증 포함)이며, 그 다음은 액세스 토큰 요청입니다. 이 단계들 이후에는 선택적으로 액세스 토큰 사용 및 액세스 토큰 갱신 단계가 이어집니다. 다음 그림은 이러한 단계들을 보여줍니다:

인증 서버와 브라우저를 활용한 Qt 애플리케이션의 간소화된 인증 절차

  • 인증 단계에서는 사용자의 신원이 확인되고, 사용자가 리소스에 대한 접근을 승인합니다. 이 과정에는 사용자의 브라우저 상호작용이 필요합니다.
  • 인증 후, 수신된 인증 코드를 사용하여 액세스 토큰을 요청하며, 선택적으로 갱신 토큰도 요청할 수 있습니다.
  • 액세스 토큰을 획득하면 애플리케이션은 이를 사용하여 대상 리소스에 접근합니다. 액세스 토큰은 리소스 요청에 포함되며, 토큰의 유효성을 검증하는 것은 리소스 서버의 몫입니다. 베어러 토큰을 사용하는 요청에 토큰을 포함시키는 방법에는 여러 가지가 있습니다. HTTP Authorization 헤더에 토큰을 포함시키는 것이 가장 일반적인 방법이라고 할 수 있습니다.
  • 액세스 토큰 갱신. 액세스 토큰은 일반적으로 1시간 후와 같이 비교적 빠르게 만료됩니다. 애플리케이션이 액세스 토큰 외에도 갱신 토큰을 수신한 경우, 이 갱신 토큰을 사용하여 새로운 액세스 토큰을 요청할 수 있습니다. 애플리케이션은 수명이 더 긴 갱신 토큰을 저장해 두어 새로운 인증 단계(그리고 이에 따른 추가적인 브라우저 상호작용)가 필요하지 않도록 할 수 있습니다.

세부 사항 및 사용자 정의

OAuth 2.0 흐름은 동적이며, 처음에는 사양을 구현하는 것이 까다로울 수 있습니다. 아래 그림은 성공적인 인증 코드 흐름의 주요 세부 사항을 보여줍니다.

특정 이벤트 호출을 보여주는 OAuth 2.0 흐름의 세부 정보

명확성을 위해 그림에서는 일부 신호를 생략했으나, 전체적으로 세부 사항과 주요 사용자 정의 지점을 잘 보여줍니다. 사용자 정의 지점에는 애플리케이션이 사용할 수 있는 다양한 신호와 슬롯은 물론, ` QAbstractOAuth::setModifyParametersFunction()` 및 ` QAbstractOAuth2::setNetworkRequestModifier()`를 통해 설정할 수 있는 콜백도 포함됩니다.

응답 핸들러 선택

어떤 핸들러를 사용할지는 redirect_uri 요소에 따라 결정됩니다. redirect_uri 는 인증 단계가 완료된 후 브라우저가 리디렉션될 위치로 설정됩니다.

네이티브 애플리케이션에서 인증 응답을 수신하기 위해, RFC 8252는 응답 URI 스키마의 세 가지 주요 유형, 즉 private-use, loopback 및 https를 규정하고 있습니다.

  • 사용(private-use) URI: OS에서 애플리케이션이 사용자 정의 URI 스키마를 등록할 수 있도록 허용하는 경우 사용할 수 있습니다. 이러한 사용자 정의 스키마를 사용하는 URL을 열려고 하면 관련 네이티브 애플리케이션이 실행됩니다. QOAuthUriSchemeReplyHandler 를 참조하십시오.
  • HTTPS URI: OS가 애플리케이션이 사용자 정의 HTTPS URL을 등록하는 것을 허용하는 경우 사용할 수 있습니다. 이 URL을 열려고 하면 관련 네이티브 애플리케이션이 실행됩니다. OS가 이를 지원하는 경우 이 스키마를 사용하는 것이 권장됩니다. QOAuthUriSchemeReplyHandler 를 참조하십시오.
  • 루프백 인터페이스: 이는 주로 데스크톱 애플리케이션 및 개발 중인 애플리케이션에서 사용됩니다. QOAuthHttpServerReplyHandler 는 리디렉션을 처리하기 위해 로컬 서버를 설정함으로써 이러한 URI를 처리하도록 설계되었습니다.

선택은 다음과 같은 여러 요인에 따라 달라집니다:

  • 인증 서버 공급업체가 지원하는 리디렉션 URI. 지원 여부는 공급업체마다 다르며, 종종 특정 클라이언트 유형 및 운영 체제에 따라 달라집니다. 또한, 애플리케이션이 공개되었는지 여부에 따라 지원 여부가 달라질 수 있습니다.
  • 대상 플랫폼에서 지원하는 리디렉션 URI 스키마.
  • 애플리케이션별 사용 편의성, 보안 및 기타 요구 사항.

RFC 8252는 다른 방식에 비해 보안 및 사용 편의성 측면에서 이점이 있는 https 스키마를 사용할 것을 권장합니다.

OAuth 2.0 기기 인증 그랜트

RFC 8628 OAuth 2.0 기기 인증 그랜트는 입력 기능이 제한적이거나 사용자 에이전트(브라우저) 사용이 실용적이지 않은 연결된 기기를 대상으로 합니다. 이 흐름을 사용하는 기기의 예로는 인증을 위해 외부 기기가 필요한 스마트 가전제품이 있습니다.

다음과 같은 예시 설정을 고려해 보겠습니다:

m_deviceFlow.setAuthorizationUrl(QUrl(authorizationUrl));
m_deviceFlow.setTokenUrl(QUrl(accessTokenUrl));
m_deviceFlow.setRequestedScopeTokens({scope});
m_deviceFlow.setClientIdentifier(clientIdentifier);
// 클라이언트 시크릿의 필요 여부는 인증 서버에 따라 다릅니다.
m_deviceFlow.setClientIdentifierSharedKey(clientSecret);

connect(&m_deviceFlow, &QOAuth2DeviceAuthorizationFlow::authorizeWithUserCode, this,
    [](const QUrl&verificationUrl, const QString&userCode, const QUrl&completeVerificationUrl) {
        if (completeVerificationUrl.isValid()) {
            // 인증 서버가  URL 매개변수의 일부로
            // 필요한 데이터를 이미 포함하고 있는  완료 URL 을  제공한 경우 ,
            // 해당 URL을 사용할 수 있습니다
            qDebug() << "Complete verification uri:" << completeVerificationUrl;
        } else {
            // 인증 서버에서 확인용 URL만 제공했으므로, 해당 URL을 사용한다
            qDebug() << "Verification uri and usercode:" << verificationUrl << userCode;
        }
    }
);

connect(&m_deviceFlow, &QAbstractOAuth::granted, this, [this](){
    // 여기서는 QNetworkRequestFactory를 사용하여 액세스 토큰을 저장합니다.
    m_api.setBearerToken(m_deviceFlow.token().toLatin1());
});
m_deviceFlow.grant();

기기 인증 부여 단계

기기 인증 그랜트 흐름은 인증 초기화, 토큰 폴링, 인증 완료의 세 가지 주요 단계로 구성됩니다. 이 단계들 이후에는 선택적으로 토큰 사용 및 토큰 갱신 단계가 이어집니다. 다음 그림은 이러한 단계들을 보여줍니다:

별도의 기기에서 브라우저를 사용하는 Qt 애플리케이션의 간소화된 권한 부여 흐름

  • 인가 초기화는 인가 서버에 HTTP 요청을 전송하여 수행됩니다. 인가 서버는 응답으로 사용자 코드, 확인 URL 및 기기 코드를 제공합니다.
  • 인가 초기화가 완료되면, 사용자에게 인가를 완료하기 위한 사용자 코드와 확인 URL이 제공됩니다. 최종 사용자에게 제공되는 방식은 화면에 표시되는 URL, QR 코드, 이메일 등 다양합니다. 자세한 내용은 RFC 8628 - 사용자 상호작용을 참조하십시오.
  • 최종 사용자가 인증을 완료할 때까지 기다리는 동안, 디바이스 플로우는 인증 서버에 토큰을 요청합니다. 이전 단계에서 수신한 디바이스 코드는 인증 세션을 매칭하는 데 사용됩니다. 폴링 간격은 인증 서버에 의해 결정되며, 일반적으로 5초입니다.
  • 최종 사용자가 인증을 수락하거나 거부하면, 인증 서버는 폴링 요청에 대해 요청된 토큰을 반환하거나, 거부된 경우 오류 코드를 반환하며, 이로써 인증이 완료됩니다.

세부 사항 및 사용자 지정

다음 그림은 기기 인증 그랜트 흐름을 보다 상세하게 보여줍니다. 이 그림은 때때로 필요한 주요 사용자 정의 사항을 나타냅니다. 예를 들어, 독점 매개변수나 추가 인증 자격 증명 등이 있습니다.

특정 이벤트 호출을 보여주는 OAuth 2.0 디바이스 그랜트 흐름의 세부 정보

리프레시 토큰

토큰 갱신을 위해서는 인증 서버가 인증 과정에서 갱신 토큰을 제공해야 합니다. 갱신 토큰 제공 여부는 인증 서버의 재량에 달려 있습니다. 일부 서버는 항상 갱신 토큰을 제공하기도 하고, 일부는 절대 제공하지 않기도 하며, 또 다른 일부는 인증 요청에 특정 scope 이 포함된 경우에만 갱신 토큰을 제공하기도 합니다.

다음 그림은 토큰 갱신 과정을 보다 상세하게 보여줍니다:

특정 이벤트 호출을 보여주는 토큰 갱신 내역

위 그림에서 볼 수 있듯이, 토큰을 갱신할 때도 일반적인 사용자 정의 지점을 활용할 수 있습니다.

애플리케이션 시작 후 토큰을 갱신하려면, 애플리케이션이 갱신 토큰을 안전하게 저장하고 ` QAbstractOAuth2::setRefreshToken`를 통해 이를 설정해야 합니다. 그런 다음 ` QAbstractOAuth2::refreshTokens `를 호출하여 새로운 토큰을 요청할 수 있습니다.

Qt 6.9의 새로운 기능으로, 애플리케이션이 토큰을 자동으로 갱신할 수 있게 되었습니다. 자세한 내용은 QAbstractOAuth2::accessTokenAboutToExpire, QAbstractOAuth2::autoRefresh 및 QAbstractOAuth2::refreshLeadTime 을 참조하십시오.

리프레시 토큰의 만료 시간은 일반적으로 인증 서버에서 명시하지 않습니다(서버 문서를 제외하고). 유효 기간은 며칠, 몇 달 또는 그 이상일 수 있습니다. 또한 다른 토큰과 마찬가지로, 리프레시 토큰은 사용자가 언제든지 취소하여 무효화할 수 있습니다. 따라서 QAbstractOAuth::requestFailed 또는 QAbstractOAuth2::serverReportedErrorOccurred 을 통해 리프레시 시도 실패를 적절히 감지하는 것이 중요합니다.

OAuth 2.0 흐름은 많은 사용자 상호작용을 필요로 하며, 이는 사용자 경험에 방해가 될 수 있습니다. 이러한 상호작용을 최소화하기 위해, 토큰을 사용자에게 알리지 않고 자동으로 갱신할 수 있습니다. 자세한 내용은 RFC 6749 - 액세스 토큰 갱신(Refreshing Access Tokens )을 참조하십시오.

Qt OpenID Connect 지원

OpenID Connect(OIDC) 는 OAuth 2.0 위에 구축된 간단한 신원 확인 계층입니다. OIDC는 인증 서버를 사용하여 사용자의 신원을 인증할 수 있습니다. 또한 OIDC를 통해 간단한 사용자 프로필 정보에 접근하는 것도 가능합니다.

현재 Qt의 OIDC 지원은 ID 토큰 획득으로 제한되어 있습니다. ID 토큰은 인증 이벤트에 대한 클레임을 포함하는 JSON 웹 토큰(JWT) 입니다.

참고: ID 토큰 유효성 검사 또는 ID 토큰 복호화 기능은 현재 구현되어 있지 않습니다. JWT 토큰 서명 또는 검증을 위해서는 타사 JWT 라이브러리를 사용해야 합니다.

애플리케이션이 수신된 토큰을 유효성 검사할 수 있다고 가정할 때, 해당 토큰을 사용하여 사용자의 신원을 확실하게 확인할 수 있습니다(단, OIDC 제공자 자체가 신뢰할 수 있는 경우에만 해당).

ID 토큰은 민감한 정보이므로 비밀로 유지해야 하며, 액세스 토큰과는 다릅니다. ID 토큰은 API 호출 시 전송하기 위한 용도가 아니며, 해당 용도로는 액세스 토큰이 사용됩니다. 일부 공급업체는 액세스 토큰에 동일한 JWT 형식을 사용할 수 있지만, 이는 동일한 형식을 사용하는 실제 ID 토큰과 혼동해서는 안 됩니다. ID 토큰의 경우, 토큰을 수신하는 클라이언트가 토큰을 검증할 책임이 있는 반면, 액세스 토큰의 경우 토큰을 수락하는 리소스 서버가 검증할 책임이 있습니다.

ID 토큰 획득하기

ID 토큰을 획득하는 방법은 액세스 토큰을 획득하는 방법과 유사합니다. 먼저, 적절한 범위를 설정해야 합니다. 인증 서버 공급업체는 profile 및 email 와 같은 추가 범위 지정자를 지원할 수 있지만, 모든 OIDC 요청에는 openid 범위가 반드시 포함되어야 합니다:

m_oauth.setRequestedScopeTokens({"openid"});

OIDC의 경우, nonce 매개변수를 사용하는 것이 강력히 권장됩니다. 이를 위해서는 적절한 NonceMode 이 설정되어 있는지 확인해야 합니다.

// This is for illustrative purposes, 'Automatic' is the default mode
m_oauth.setNonceMode(QAbstractOAuth2::NonceMode::Automatic);

마지막 단계로, QAbstractOAuth2::granted 신호나 QAbstractOAuth2::idTokenChanged 를 직접 수신할 수 있습니다:

connect(&m_oauth, &QAbstractOAuth2::idTokenChanged, this, [this](const QString &token) {
    Q_UNUSED(token); // Handle token
});

ID 토큰 검증

수신된 ID 토큰을 검증하는 것은 인증 흐름에서 매우 중요한 부분이며, 완전히 구현할 경우 다소 복잡한 작업이 됩니다. 자세한 내용은 OpenID Connect ID 검증 문서를 참조하십시오.

간단히 요약하자면, 유효성 검사는 다음과 같은 단계로 이루어집니다:

  • 필요한 경우 토큰을 복호화합니다(JWE 참조).
  • 토큰 헤더, 페이로드 및 서명 추출
  • 서명 유효성 검사
  • 페이로드의 필드(예: aud, iss, exp, nonce, iat) 유효성 검사

현재 Qt는 ID 토큰 유효성 검사를 지원하지 않지만, jwt-cpp와 같은 타사 JWT 라이브러리가 있습니다.

ID 토큰 검증 예시

이 섹션에서는 간단한 검증 예제를 설명합니다. 사전 준비 사항으로, 개발 환경에 OpenSSL 라이브러리가 설치되어 있어야 하며, 애플리케이션 프로젝트의 소스 디렉터리 내 include 폴더에 jwt-cpp가 위치해 있어야 합니다.

애플리케이션 프로젝트의 ` CMakeLists.txt ` 파일에서 먼저 필수 조건이 충족되었는지 확인합니다:

find_package(OpenSSL 1.0.0 QUIET)
set(JWT_CPP_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
if(OPENSSL_FOUND AND EXISTS "${JWT_CPP_INCLUDE_DIR}/jwt-cpp/jwt.h")

그런 다음 필요한 헤더 파일과 라이브러리를 추가합니다:

    target_include_directories(networkauth_oauth_snippets PRIVATE "${JWT_CPP_INCLUDE_DIR}")
    target_link_libraries(networkauth_oauth_snippets PRIVATE OpenSSL::SSL OpenSSL::Crypto)
    target_compile_definitions(networkauth_oauth_snippets PRIVATE JWT_CPP_AVAILABLE)

애플리케이션 소스 파일에서 검증 라이브러리를 포함시킵니다:

#ifdef JWT_CPP_AVAILABLE
#include "jwt-cpp/jwt.h"
#endif

애플리케이션이 ID 토큰을 수신하면, 이를 검증해야 합니다. 먼저 JSON Web Key Sets(JWKS)에서 일치하는 키를 찾습니다( OpenID Connect 디스커버리 참조).

try {
    const auto jwt = jwt::decode(m_oauth.idToken().toStdString());
    const auto jwks = jwt::parse_jwks(m_jwks->toJson(QJsonDocument::Compact).toStdString());
    const auto jwk = jwks.get_jwk(jwt.get_key_id());

그런 다음 실제 인증 절차를 수행합니다:

   // 여기서는 모듈러스와 지수를 사용하여 키를 도출합니다.
    const auto n = jwk.get_jwk_claim("n").as_string(); // 모듈러스
   const auto e = jwk.get_jwk_claim("e").as_string(); // 지수
    if (n.empty()|| e.empty()) {
        qWarning() << "Modulus or exponent empty";
       return false;
    }
    if (jwt.get_algorithm() != "RS256") { // 이 예제는 RS256만 지원합니다
        qWarning() << "Unsupported algorithm:" << jwt.get_algorithm();
       return false;
    }
    if (jwk.get_jwk_claim("kty").as_string() != "RSA") {
        qWarning() << "Unsupported key type:" << jwk.get_jwk_claim("kty").as_string();
       return false;
    }
    if (jwk.has_jwk_claim("use")&& jwk.get_jwk_claim("use").as_string() != "sig") {
        qWarning() << "Key not for signature" << jwk.get_jwk_claim("use").as_string();
       return false;
    }
    // 간단한 최소 검증 (특수 사례 및 예: 'sub' 검증은 생략).
    // jwt-cpp는 'exp', 'iat', 'nbf'가 존재할 경우 이를 추가로 확인합니다.
   const auto keyPEM = jwt::helper::create_public_key_from_rsa_components(n, e);
    auto verifier = jwt::verify()
                        .allow_algorithm(jwt::algorithm::rs256(keyPEM))
                       .with_claim("nonce", jwt::claim(m_oauth.nonce().toStdString()))
                        .with_issuer(m_oidcConfig->value("issuer"_L1).toString().toStdString())
                       .with_audience(std::string(clientIdentifier.data()))
                        .leeway(60UL);
    verifier.verify(jwt);
    qDebug() << "ID Token verified successfully";
   return true;
} catch(const std::exception &e) {
    // 오류 처리. 또는 jwt-cpp 호출 시 오류 매개변수를 전달
    qWarning() << "ID Token verification failed" << e.what();
   return false;
}

ID 토큰 값 읽기

ID 토큰은 JSON Web Token(JWT) 형식이며, 점(.)으로 구분된 헤더, 페이로드, 서명 부분으로 구성됩니다. ..

ID 토큰의 값을 읽는 방법은 간단합니다. 예를 들어, 다음과 같은 구조체(struct)가 있다고 가정해 봅시다:

struct IDToken {
    QJsonObject header;
    QJsonObject payload;
    QByteArray signature;
};

그리고 다음과 같은 함수가 있다고 가정해 봅시다:

std::optional<IDToken> parseIDToken(const QString &token) const;

토큰은 다음과 같이 추출할 수 있습니다:

if (token.isEmpty())
    return std::nullopt;

QList<QByteArray> parts = token.toLatin1().split('.');
if (parts.size() != 3)
    return std::nullopt;

QJsonParseError parsing;

QJsonDocument header = QJsonDocument::fromJson(
    QByteArray::fromBase64(parts.at(0), QByteArray::Base64UrlEncoding), &parsing);
if (parsing.error != QJsonParseError::NoError || !header.isObject())
    return std::nullopt;

QJsonDocument payload = QJsonDocument::fromJson(
    QByteArray::fromBase64(parts.at(1), QByteArray::Base64UrlEncoding), &parsing);
if (parsing.error != QJsonParseError::NoError || !payload.isObject())
    return std::nullopt;

QByteArray signature = QByteArray::fromBase64(parts.at(2), QByteArray::Base64UrlEncoding);

return IDToken{header.object(), payload.object(), signature};

경우에 따라 토큰이 JSON Web Encryption(JWE) 으로 암호화되어 있을 수 있으며, 이 JWE 내부에는 JWT 토큰이 포함되어 있습니다. 이 경우, 토큰을 먼저 복호화해야 합니다.

OpenID Connect 디스커버리

OpenID Connect 디스커버리는 OpenID 제공자와 상호작용하기 위해 필요한 세부 정보를 파악하는 방법을 정의합니다. 여기에는 authorization_endpoint 및 token_endpoint URL과 같은 정보가 포함됩니다.

이러한 제공자 세부 정보를 애플리케이션에서 정적으로 구성할 수도 있지만, 런타임 시점에 세부 정보를 탐색하면 다양한 제공자와 상호작용할 때 더 큰 유연성과 안정성을 확보할 수 있습니다.

디스커버리 문서를 가져오는 것은 간단한 HTTP GET 요청으로 이루어집니다. 이 문서는 일반적으로 https://<domain name>/.well-known/openid_configuration 에 위치합니다.

m_network->get(request, this, [this](QRestReply &reply) {
    if (reply.isSuccess()) {
        if (auto doc = reply.readJson(); doc && doc->isObject())
            m_oidcConfig = doc->object(); // Store the configuration
    }
});

특히, 토큰 유효성 검증을 위해 jwks_uri 필드는 현재(공개) 보안 자격 증명에 액세스할 수 있는 링크를 제공합니다. 이를 활용하면 애플리케이션에 해당 인증 정보를 직접 하드코딩할 필요가 없습니다. 또한 이는 키 교체에도 도움이 됩니다. 공급업체는 사용되는 키를 수시로 변경할 수 있으므로, 최신 키를 유지하는 것이 중요합니다.

키를 가져오는 과정도 마찬가지로 간단한 HTTP GET 요청으로 이루어집니다:

m_network->get(request, this, [this](QRestReply &reply) {
    if (reply.isSuccess()) {
        if (auto doc = reply.readJson(); doc && doc->isObject())
            m_jwks = doc; // Use the keys later to verify tokens
    }
});

키 세트에는 일반적으로 여러 개의 키가 포함되어 있습니다. 올바른 키는 JWT 헤더에 명시되어 있습니다(키를 올바르게 매칭하는 데 주의해야 하며, 단순히 키 ID( kid) 필드만 확인하는 것만으로는 충분하지 않습니다).

OpenID UserInfo 엔드포인트

OIDC 제공자가 지원하는 경우, 사용자 정보에 접근하는 또 다른 방법은 OpenID Connect UserInfo 엔드포인트를 사용하는 것입니다. userinfo의 URL은 OpenID Connect Discovery 문서의 userinfo_endpoint 필드에 포함되어 있습니다.

UserInfo 엔드포인트는 ID 토큰을 사용하지 않으며, 액세스 토큰을 통해 접근합니다. UserInfo에 접근하는 방법은 액세스 토큰을 사용하여 다른 리소스에 접근하는 방법과 유사합니다.

예를 들어 다음과 같이 액세스 토큰을 수신하고 설정했다고 가정하면:

QNetworkRequestFactory userInfoApi(url);
userInfoApi.setBearerToken(m_oauth.token().toLatin1());

이 경우 UserInfo에 접근하려면 HTTP GET 요청을 보내면 됩니다:

m_network->get(userInfoApi.createRequest(), this, [this](QRestReply&reply) {
    if (reply.isSuccess()) {
        if (auto doc = reply.readJson(); doc&& doc->isObject())
            qDebug() << doc->object(); // Use the userinfo
    }
});

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.