OpenID Connect Discovery 1.0 Deep Dive: OP의 ‘Self-Introduction’ 및 동적 구성 검색

발행: (2026년 3월 7일 PM 05:50 GMT+9)
13 분 소요
원문: Dev.to

Source: Dev.to

번역할 텍스트가 제공되지 않았습니다. 번역이 필요한 본문을 알려주시면 한국어로 번역해 드리겠습니다.

Source:

소개

여러분은 아마 OIDC (OpenID Connect) 를 매일 사용해 Google 로그인이나 기타 인증 흐름을 애플리케이션에 통합하고 있을 것입니다.
그 과정에서 라이브러리 초기화 코드에

issuer: "https://accounts.google.com"

와 같이 Issuer URL만 지정하면 Authorization Endpoint, Token Endpoint, 그리고 공개 키(JWKS) 위치까지 자동으로 찾아주는 것을 경험해 본 적이 있나요?

  • “Issuer URL만 제공하면 왜 모든 엔드포인트를 알 수 있는 걸까?”
  • “공개 키(JWKS) 회전이 발생해도 다운타임 없이 어떻게 따라갈 수 있을까?”
  • “처음부터 alice@example.com 같은 이메일 형태의 ID로부터 인증할 제공자를 어떻게 식별하나요?”

이 질문들에 대한 답은 OpenID Connect Discovery 1.0 입니다.

과거 OAuth 2.0 시절에는 개발자들이 문서를 읽고 각 엔드포인트(URL)(예: /authorize, /token)를 클라이언트에 하드코딩하는 경우가 많았습니다.
이 방식은 제공자가 URL을 바꾸거나 공개 키를 회전할 때마다 클라이언트 측 코드를 수정해야 하므로 확장성이 떨어졌습니다.

OIDC Discovery 1.0클라이언트가 OpenID Provider(OP)의 구성 정보(메타데이터)를 동적으로 발견하고 가져올 수 있게 하는 표준화된 메커니즘입니다.

이 글에서는 사양을 기반으로 OIDC Discovery의 두 단계에 대해 깊이 살펴보겠습니다.

단계목표
Issuer Discovery (Phase 1)사용자가 제공한 식별자(예: 이메일 주소)를 기반으로 누가 인증을 담당할 OpenID Provider(Issuer)인지 찾아냅니다. 이 단계는 Issuer가 이미 알려져 있는 경우(예: “Google로 로그인” 버튼 클릭) 생략될 수 있습니다.
Provider Configuration (Phase 2)식별된 Issuer에 대해 구성 메타데이터를 조회합니다 – “Authorization Endpoint는 어디인가?” “공개 키(JWKS)는 어디에 있나?”

Issuer Discovery가 필요한 경우

앱이 Google 로그인 전용이라면 Issuer는 당연히 https://accounts.google.com 입니다.
하지만 사용자의 이메일 도메인(@company.com)에 따라 동적으로 IdP를 전환해야 하는 엔터프라이즈 SaaS를 생각해 보세요.
이때 RFC 7033 WebFinger 가 역할을 합니다.

사용자 제공 식별자 정규화

사용자가 입력한 값은 다음과 같을 수 있습니다:

  • 이메일 주소 예: alice@example.com
  • URL 예: https://example.com/alice

OIDC Discovery는 고유하게 결정하기 위한 엄격한 정규화 단계를 정의합니다:

  • Host – 연결할 서버
  • Resource – WebFinger를 통해 조회할 식별자

정규화 규칙

ConditionNormalized Form
스킴 없음이며 @를 포함하는 경우 (예: joe@example.com)acct: 스킴으로 해석 → acct:joe@example.com
스킴 없음이며 @포함하지 않는 경우 (예: example.com 또는 example.com:8080)https:// 로 해석 → https://example.com
명시적 스킴 (https://, acct:, …)그대로 사용, 추가 변경 없음
프래그먼트 존재 (#…)프래그먼트를 완전히 제거

Source:

Phase 1 – WebFinger를 이용한 Issuer Discovery

사용자가 joe@example.com을 입력했다고 가정하면, 이는 acct:joe@example.com으로 정규화됩니다.

  1. 호스트 식별 – 권한 부분(example.com)을 추출합니다.
  2. 리소스 지정 – 전체 정규화된 URI(acct:joe@example.com)를 resource 쿼리 매개변수로 사용합니다.
  3. WebFinger 엔드포인트 호출 – 추출한 호스트의 /.well-known/webfinger에 대해 HTTP GET 요청을 수행합니다.
GET /.well-known/webfinger?resource=acct%3Ajoe%40example.com&rel=http%3A%2F%2Fopenid.net%2Fspecs%2Fconnect%2F1.0%2Fissuer HTTP/1.1
Host: example.com
  • resource – 조회할 식별자를 URL‑인코딩한 값.
  • rel – 고정 값 http://openid.net/specs/connect/1.0/issuer이며, “OIDC Issuer 정보를 요청한다”는 의미입니다.

예시 응답 (JRD – JSON Resource Descriptor)

HTTP/1.1 200 OK
Content-Type: application/jrd+json

{
  "subject": "acct:joe@example.com",
  "links": [
    {
      "rel": "http://openid.net/specs/connect/1.0/issuer",
      "href": "https://server.example.com"
    }
  ]
}

href 값(https://server.example.com)이 Issuer URL이며, 클라이언트는 다음 단계에서 이 URL을 사용합니다.

Phase 2 – 제공자 구성

Issuer가 알려지면, 클라이언트는 OP의 메타데이터를 가져옵니다.
OIDC Discovery는 OP가 JSON 메타데이터를 Issuer URL에 /.well-known/openid-configuration을 추가한 경로에 노출하도록 요구합니다.

GET /.well-known/openid-configuration HTTP/1.1
Host: server.example.com

⚠️ 일반적인 함정 – Issuer에 경로가 포함된 경우

Issuer에 경로가 포함되어 있는 경우(예: https://example.com/tenant-1):

  1. 끝에 있는 슬래시를 제거합니다.
  2. 경로 바로 뒤에 /.well-known/openid-configuration 을 추가합니다.

결과 URL:

https://example.com/tenant-1/.well-known/openid-configuration

많은 구현에서 발생하듯이 구성 파일을 도메인 루트(https://example.com/.well-known/openid-configuration)에 잘못 배치하지 않도록 주의하세요.

OP 메타데이터 예시 응답

HTTP/1.1 200 OK
Content-Type: application/json

{
  "issuer": "https://server.example.com",
  "authorization_endpoint": "https://server.example.com/oauth2/v1/authorize",
  "token_endpoint": "https://server.example.com/oauth2/v1/token",
  "userinfo_endpoint": "https://server.example.com/oauth2/v1/userinfo",
  "jwks_uri": "https://server.example.com/oauth2/v1/keys",
  "registration_endpoint": "https://server.example.com/oauth2/v1/register",
  "scopes_supported": ["openid", "profile", "email"],
  "response_types_supported": ["code", "id_token", "token id_token"],
  "grant_types_supported": ["authorization_code", "refresh_token", "client_credentials"],
  "subject_types_supported": ["public", "pairwise"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "claims_supported": ["sub", "iss", "aud", "exp", "iat", "email", "email_verified", "name"]
}

JSON 객체(OP 메타데이터)는 다음을 나열합니다:

  • 엔드포인트 (authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri, …)
  • 지원되는 기능 (scopes_supported, grant_types_supported, claims_supported, 등)

이 메타데이터를 사용하면 클라이언트 라이브러리가 자동으로 다음을 수행할 수 있습니다:

  • 사용자를 올바른 Authorization Endpoint(인증 엔드포인트)로 안내합니다
  • 코드를 적절한 Token Endpoint(토큰 엔드포인트)에서 교환합니다
  • JWKS URI에서 공개 키를 가져옵니다(키 회전을 투명하게 처리)

요약

  1. Issuer Discovery (optional) – WebFinger를 사용하여 사용자 제공 식별자(이메일, URL 등)를 Issuer URL로 변환합니다.
  2. Provider Configuration – Issuer의 /.well-known/openid-configuration 문서를 가져와 모든 필수 엔드포인트와 기능을 확인합니다.

이 두 단계 디스커버리 프로세스는 하드코딩을 없애고, 멀티‑테넌트 시나리오를 지원하며, 다운타임 없이 원활한 키 회전을 보장합니다.

OpenID Connect Provider Configuration (Discovery)

{
  "authorization_endpoint": "https://server.example.com/connect/authorize",
  "token_endpoint": "https://server.example.com/connect/token",
  "userinfo_endpoint": "https://server.example.com/connect/userinfo",
  "jwks_uri": "https://server.example.com/jwks.json",
  "response_types_supported": [
    "code",
    "id_token",
    "id_token token"
  ],
  "subject_types_supported": [
    "public",
    "pairwise"
  ],
  "id_token_signing_alg_values_supported": [
    "RS256",
    "ES256"
  ],
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic",
    "private_key_jwt"
  ],
  "scopes_supported": [
    "openid",
    "profile",
    "email"
  ],
  "claims_supported": [
    "sub",
    "iss",
    "name",
    "email"
  ],
  "registration_endpoint": "https://server.example.com/connect/register"
}

매개변수 개요

매개변수 이름필수 / 선택설명
issuerREQUIREDOP의 Issuer Identifier. TLS 검사와 ID 토큰의 iss 클레임 검증에 사용됩니다.
authorization_endpointREQUIRED사용자가 인증을 위해 리디렉션되는 엔드포인트입니다.
token_endpointREQUIRED*인증 코드를 토큰으로 교환하는 데 사용되는 엔드포인트입니다. Implicit Flow만 지원하는 OP를 제외합니다.
jwks_uriREQUIREDID 토큰 서명을 검증하기 위한 공개 키(JWK Set)가 위치한 URL입니다.
response_types_supportedREQUIREDOP가 지원하는 OIDC 인증 흐름(예: code, id_token, id_token token)입니다.
subject_types_supportedREQUIRED지원되는 sub 식별자 유형 – public(모든 RP에 동일) 또는 pairwise(RP마다 고유)입니다.
id_token_signing_alg_values_supportedREQUIREDOP가 ID 토큰에 사용할 수 있는 서명 알고리즘입니다. RS256은 반드시 포함되어야 합니다.
token_endpoint_auth_methods_supportedOPTIONAL토큰 엔드포인트에서 허용되는 클라이언트 인증 방법(예: client_secret_basic, private_key_jwt)입니다.
scopes_supportedRECOMMENDEDOP가 지원하는 스코프 목록입니다. openid 스코프는 SHOULD 포함되어야 합니다.
claims_supportedRECOMMENDEDOP가 반환할 수 있는 클레임 목록(예: name, email, sub, iss)입니다.
registration_endpointRECOMMENDED동적 클라이언트 등록을 위한 엔드포인트입니다.

Source:

Deep‑Dive: jwks_uri

  • 왜 중요한가jwks_uri는 OP(인증 제공자)의 공개 키에 대한 단일 진실 소스입니다. 이 URL을 가져옴으로써 신뢰당사자(RP)는 수신하는 모든 ID Token의 서명을 검증할 수 있습니다.
  • 키 회전 – RP는 들어오는 ID Token의 kid(Key ID) 헤더를 확인해야 합니다. 해당 키가 로컬 캐시에서 누락된 경우, RP는 jwks_uri에서 JWKS를 다시 가져와야 합니다. 이를 통해 다운타임 없이 원활한 키 회전이 가능합니다.
  • 보안 고려 사항
    • 위장 공격: 공격자가 RP가 악성 JWKS를 다운로드하도록 만들면 위조된 ID Token이 가능해집니다.

    • Discovery 사양은 엄격한 검사를 강제합니다:

      “반환된 issuer 값은 /.well-known/openid-configuration 앞에 사용된 Issuer URL과 동일해야 합니다.” (OIDC Discovery §4.3)

      “구현은 TLS를 지원해야 합니다.” (OIDC Discovery §7.1)

    • 모든 통신(WebFinger 및 Provider Configuration)은 HTTPS를 통해 이루어져야 하며, RP는 서버의 TLS 인증서(RFC 6125)를 검증해야 합니다. 평문 전송은 MITM이 jwks_uri를 공격자가 제어하는 서버로 바꾸는 것을 허용하게 됩니다.

OIDC Discovery 1.0의 핵심 정리

  1. 하드코딩된 URL 금지/.well-known/openid-configuration을 사용하면 RP가 런타임에 OP의 엔드포인트와 기능을 발견할 수 있습니다.
  2. WebFinger 통합 – 사용자의 이메일과 같은 식별자를 올바른 Issuer로 해석할 수 있습니다(Discovery Phase 1).
  3. 자동 키 회전 – JWKS를 동적으로 가져오면 견고하고 원활한 보안 운영이 가능합니다.

다음에 OIDC 라이브러리를 사용할 때, 위 모든 기능을 구동하는 숨은 요청 /.well-known/openid-configuration을 떠올려 보세요.

References

  • OpenID Connect Discovery 1.0 – 공급자 메타데이터에 대한 핵심 사양.
  • RFC 7033 – WebFinger – 리소스(예: 이메일 주소)에 대한 정보를 발견하는 메커니즘.
  • RFC 5785 – Well‑Known URIs – 발견 엔드포인트에 사용되는 /.well-known/ 네임스페이스를 정의합니다.
0 조회
Back to Blog

관련 글

더 보기 »