디스커버리

디스커버리 (OpenID Connect Discovery)

서비스가 많아지면 "이 OpenID Provider(OP)의 인가 엔드포인트는 어디지? 토큰 엔드포인트는? 공개 키는 어디서 가져오지?"를 하나하나 하드코딩하기가 싫어져요. OpenID Connect Discovery는 이런 OP의 설정을 한 번에 알아내는 표준 방법을 제공해요. OP가 광고하는 메타데이터를 읽으면 필요한 엔드포인트와 지원 기능을 알 수 있지요. 이 글에서는 Discovery가 메타데이터를 어떻게 노출하고, 어떤 필드로 구성되는지 살펴볼게요.

출처: OpenID Connect Discovery 1.0

본문

OpenID Provider 메타데이터

Discovery를 지원하는 OP는 자신의 설정(metadata)을 문서로 제공해요. RP(신뢰 당사자)는 이 메타데이터를 읽어 필요한 엔드포인트와 지원 사항을 파악하지요. 주요 구성 필드는 이래요.

  • issuer (REQUIRED) — OP가 자신의 발급자 식별자(Issuer Identifier)로 주장하는 https URL. 쿼리나 프래그먼트 성분은 없어요. WebFinger로 발급자를 알아낸다면, 그 값과 ID 토큰의 iss 클레임 값 모두 이 값과 같아야 해요.
  • authorization_endpoint (REQUIRED) — OP의 OAuth 2.0 인가 엔드포인트 URL. https 스킴이어야 하고 포트·경로·쿼리 파라미터 성분은 가질 수 있어요.
  • token_endpoint — OP의 OAuth 2.0 토큰 엔드포인트 URL. Implicit Flow만 쓰지 않는다면 REQUIRED예요.
  • userinfo_endpoint (RECOMMENDED) — OP의 UserInfo 엔드포인트 URL.
  • jwks_uri (REQUIRED) — OP의 JWK Set 문서 URL. RP가 OP의 서명을 검증하는 데 쓰는 서명 키(그리고 선택적으로 암호화 키)를 담고, 반드시 https 스킴이어야 해요. 개인 키나 대칭 키는 절대 담으면 안 돼요.
  • registration_endpoint (RECOMMENDED) — OP의 동적 클라이언트 등록(Dynamic Client Registration) 엔드포인트 URL.
  • scopes_supported (RECOMMENDED) — 서버가 지원하는 OAuth 2.0 스코프 값 배열. openid 스코프는 반드시 지원해야 해요.
  • response_types_supported (REQUIRED) — OP가 지원하는 response_type 값 배열. 동적 OP는 code, id_token, id_token token 값을 지원해야 해요.
  • grant_types_supported (OPTIONAL) — 지원하는 그랜트 타입 배열. 동적 OP는 authorization_codeimplicit을 지원해야 해요.
  • subject_types_supported (REQUIRED) — 지원하는 주체 식별자 타입 배열. 유효한 타입은 pairwisepublic이에요.
  • id_token_signing_alg_values_supported (REQUIRED) — ID 토큰 서명에 지원하는 JWS 알고리즘(alg 값) 배열. RS256은 반드시 포함해야 해요.
  • token_endpoint_auth_methods_supported (OPTIONAL) — 토큰 엔드포인트가 지원하는 클라이언트 인증 방식. client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt가 옵션이고, 생략하면 기본값은 client_secret_basic(HTTP Basic)이에요.

이 밖에도 response_modes_supported, acr_values_supported, request_object_signing_alg_values_supported, claims_supported 등 많은 선택 필드가 더 정의돼 있어요.

설정 정보 얻기: /.well-known/openid-configuration

Discovery를 지원하는 OP는 /.well-known/openid-configuration 문자열을 발급자(issuer)에 이어 붙인 경로에 JSON 문서를 두어야 해요. 문법은 RFC 5785의 well-known 규칙을 따르고, 응답은 application/json 콘텐츠 타입이어야 해요.

예를 들어 발급자가 https://example.com이면 다음과 같이 요청해요.

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

발급자가 경로 성분을 가질 땐(예: https://example.com/issuer1) 끝의 /를 제거한 뒤 이어 붙여요.

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

경로 성분을 쓰면 호스트 하나로 여러 발급자를 지원할 수 있어요. 멀티테넌트 호스팅 구성에서 필요한 방식이에요.

응답은 성공 시 200 OK 상태 코드와 application/json 콘텐츠 타입으로, 3장에서 정의한 메타데이터 값들의 부분집합을 멤버로 가진 JSON 객체를 돌려줘요. 여러 값을 가지는 클레임은 JSON 배열로 표현해요. 실제 예시는 다음과 같아요.

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

{
  "issuer": "https://server.example.com",
  "authorization_endpoint": "https://server.example.com/connect/authorize",
  "token_endpoint": "https://server.example.com/connect/token",
  "token_endpoint_auth_methods_supported": ["client_secret_basic", "private_key_jwt"],
  "userinfo_endpoint": "https://server.example.com/connect/userinfo",
  "check_session_iframe": "https://server.example.com/connect/check_session",
  "end_session_endpoint": "https://server.example.com/connect/end_session",
  "jwks_uri": "https://server.example.com/jwks.json",
  "registration_endpoint": "https://server.example.com/connect/register"
}

브라우저 기반(JavaScript) 클라이언트가 이 설정을 직접 읽을 수 있도록, openid-configuration 엔드포인트와 여러 엔드포인트는 CORS를 지원하는 것이 좋아요. 다만 인가 엔드포인트는 클라이언트가 직접 접근하지 않고 리다이렉트되는 곳이라 CORS 사용을 권장하지 않아요.

더 알아보기

  • 발급자를 WebFinger로 알아내는 방법은 Discovery 스펙의 2절 Issuer Discovery를 참고해요.
  • end_session_endpoint가 실제로 쓰이는 로그아웃 흐름은 RP-Initiated Logout 문서를 참고해요.
  • JWKS가 무엇인지 더 알고 싶다면 JSON Web Key(JWK) 스펙을 참고해요.