OAuth2·OIDC 인증 서버 설계 가이드: 자체 구축과 외부 IdP 선택 기준부터 PKCE·토큰 운영까지

결론: 인증 서버는 로그인 화면이 아니라 운영 인프라다
결론부터 말하면, 단일 웹사이트의 이메일 로그인만 필요하다면 OAuth2·OIDC 인증 서버를 직접 만드는 것은 과한 선택일 수 있습니다. 반대로 SaaS, 관리자 시스템, 모바일 앱, B2B 포털, 외부 API, 고객사별 SSO가 함께 움직인다면 단순 JWT 로그인으로 버티기 어렵습니다. 이때는 ‘자체 구축이냐 외부 IdP냐’보다 먼저 클라이언트 유형, 계정 데이터, 권한 정책, 토큰 수명, 회수 방식, 운영 문서를 분리해서 봐야 합니다.
실무 판단은 이렇게 정리할 수 있습니다. 출시 속도와 보안 업데이트 책임을 줄이는 것이 중요하면 Auth0 같은 호스팅 IdP나 클라우드 IdP를 검토합니다. 회사 내부 정책, 오픈소스 기반 운영, SAML/OIDC 연동, 커스터마이징이 중요하면 Keycloak 같은 자체 운영 IdP가 후보가 됩니다. 제품 도메인에 계정·조직·권한·결제·관리자 정책이 깊게 묶여 있다면 Laravel·Node 백엔드에 인증 구조를 직접 설계하되, 프로토콜은 검증된 구현체와 표준에 맡겨야 합니다.
PM과 대표가 먼저 정해야 할 질문은 ‘로그인 기능을 무엇으로 만들까’가 아닙니다. ‘앞으로 어떤 앱과 고객사가 이 인증 체계를 신뢰하게 될까’입니다.
OAuth2와 OIDC를 혼동하면 설계가 어긋난다
OAuth2는 기본적으로 ‘누가 어떤 API에 접근할 수 있는가’를 다루는 권한 위임 프레임워크입니다. OIDC는 OAuth2 위에 로그인한 최종 사용자의 신원을 확인하기 위한 ID Token과 표준 클레임을 더한 인증 계층입니다. OpenID Connect Core는 OIDC를 OAuth 2.0 위의 identity layer로 정의하고, ID Token을 사용자의 인증 정보를 담는 JWT 기반 보안 토큰으로 설명합니다. ([openid.net](https://openid.net/specs/openid-connect-core-1_0-18.html))
| 구분 | OAuth2 중심 사고 | OIDC까지 포함한 사고 |
|---|---|---|
| 주요 질문 | 이 클라이언트가 API를 호출해도 되는가 | 이 사용자가 누구이며 어떤 세션으로 로그인했는가 |
| 주요 토큰 | Access Token, Refresh Token | ID Token, Access Token, Refresh Token |
| 검증 대상 | scope, audience, 만료, 발급자 | iss, sub, aud, nonce, auth_time, 서명 |
| 대표 사용처 | 외부 API 권한, 앱 간 연동, 백그라운드 작업 | 소셜 로그인, SSO, 고객사 IdP 연동, 계정 식별 |
따라서 ‘구글 로그인 붙였다’와 ‘우리 서비스가 OIDC Provider가 됐다’는 완전히 다른 이야기입니다. 전자는 외부 IdP의 클라이언트가 되는 것이고, 후자는 우리 서버가 다른 앱과 API에 토큰을 발급하는 인증 서버가 되는 것입니다. 이 차이를 문서화하지 않으면 외주 인수인계 때 가장 먼저 꼬이는 지점이 redirect URI, token audience, 사용자 식별자 매핑입니다.
왜 단순 JWT 구조가 나중에 문제가 되는가
MVP 단계에서는 로그인 성공 시 JWT를 발급하고, 프론트엔드가 이를 Authorization 헤더에 넣어 API를 호출하는 구조가 빠릅니다. 문제는 서비스가 커진 뒤에 발생합니다. 모바일 앱은 Refresh Token 저장과 분실 대응이 필요하고, 관리자 시스템은 역할 변경 즉시 반영이 필요하며, B2B 포털은 고객사별 SSO와 조직 단위 권한을 요구합니다. 회원 탈퇴 후 토큰 무효화, 비밀번호 변경 후 모든 기기 로그아웃, 고객사 계약 종료 후 직원 일괄 차단 같은 요구는 ‘JWT 서명 검증’만으로 해결되지 않습니다.
특히 Access Token을 너무 오래 살려 두면 회수 부담이 커지고, 너무 짧게 두면 모바일 앱과 SPA의 사용자 경험이 나빠집니다. 이 균형은 Refresh Token 설계 가이드에서 다룬 것처럼 토큰 수명, 재발급 조건, 회수 정책, 위험 이벤트 대응을 함께 봐야 합니다.
기본 흐름: Authorization Code Flow + PKCE

2026년 기준 새로 설계하는 웹·모바일·관리자 인증은 Authorization Code Flow에 PKCE를 붙이는 방향을 기본값으로 보는 것이 안전합니다. OAuth 2.0 Security BCP인 RFC 9700은 authorization server가 PKCE를 지원해야 하며, S256처럼 verifier를 노출하지 않는 challenge method를 사용하라고 권고합니다. 또한 PKCE 조언이 네이티브 앱뿐 아니라 웹 애플리케이션에도 적용된다고 명시합니다. ([rfc-editor.org](https://www.rfc-editor.org/info/rfc9700/))
- 클라이언트가 code verifier를 만들고, 이를 해시한 code challenge를 준비합니다.
- 브라우저를 인증 서버의 authorization endpoint로 리다이렉트합니다.
- 사용자가 로그인하고 동의하면 인증 서버가 authorization code를 redirect URI로 돌려줍니다.
- 클라이언트는 token endpoint에 authorization code와 code verifier를 보내 Access Token, ID Token, Refresh Token을 교환합니다.
- API 서버는 Access Token의 서명, issuer, audience, 만료, scope를 검증합니다.
Implicit Flow처럼 authorization response에서 access token이 바로 노출되는 방식은 최신 보안 기준에서 피하는 쪽으로 정리되고 있습니다. RFC 9700은 implicit grant와 access token을 authorization response에 싣는 응답 유형이 token leakage와 replay 위험을 가진다고 설명하며, 클라이언트가 이를 사용하지 않아야 한다고 권고합니다. Password Grant 역시 사용자 비밀번호를 클라이언트에 노출시키므로 사용해서는 안 된다고 명시합니다. ([rfc-editor.org](https://www.rfc-editor.org/info/rfc9700/))
인증 서버 설계의 7개 결정 항목
OAuth2·OIDC 인증 서버 설계는 엔드포인트 몇 개를 만드는 일이 아닙니다. 다음 7개 항목이 문서에 있어야 운영 중 판단이 가능합니다.
| 결정 항목 | 반드시 정할 것 | 운영 리스크 |
|---|---|---|
| Issuer | 토큰 발급 주체 URL, 환경별 분리 | 개발·스테이징·운영 토큰 혼용 |
| Client Registry | 웹, 모바일, 관리자, 외부 API, 파트너별 client_id와 redirect URI | 리다이렉트 오용, 비공식 앱 난립 |
| Scope와 Role | API 권한 scope와 서비스 내부 역할의 경계 | 토큰에 과도한 권한 내장 |
| Token Lifetime | Access Token, Refresh Token, ID Token 수명 | 탈취 대응 지연 또는 잦은 재로그인 |
| Revocation | 로그아웃, 탈퇴, 관리자 차단, 비밀번호 변경 시 회수 범위 | 탈퇴 사용자의 잔여 접근 |
| Discovery와 JWKS | well-known 설정, jwks_uri, 키 교체 절차 | 키 회전 실패, 클라이언트 장애 |
| Audit Log | 로그인, 토큰 발급, refresh, 실패, 관리자 권한 변경 기록 | 침해 사고 원인 추적 불가 |
OIDC Discovery나 OAuth Authorization Server Metadata를 지원하면 클라이언트가 authorization endpoint, token endpoint, jwks_uri, revocation endpoint, introspection endpoint, PKCE 지원 메서드 같은 정보를 표준 위치에서 읽을 수 있습니다. RFC 8414는 이런 metadata 항목들을 정의하고, OIDC Discovery는 well-known 설정 문서로 Provider 구성을 노출하는 방식을 제공합니다. ([datatracker.ietf.org](https://datatracker.ietf.org/doc/html/rfc8414))
자체 구축 vs 외부 IdP: 기능보다 책임 범위로 비교하라

많은 팀이 Auth0, Keycloak, 클라우드 IdP, 자체 개발을 가격표로만 비교합니다. 그러나 인증은 장애가 나면 모든 서비스가 멈추고, 보안 사고가 나면 고객 신뢰가 훼손되는 영역입니다. 월 과금보다 중요한 것은 누가 보안 업데이트, 키 회전, MFA, SSO, 로그, 장애 대응, 고객사 요구를 책임지는지입니다.
| 선택지 | 적합한 경우 | 주의할 점 |
|---|---|---|
| 호스팅 IdP | 빠른 출시, 소셜 로그인, MFA, 표준 SSO, 보안 업데이트 위임이 중요할 때 | MAU, 조직 수, 커스텀 도메인, 엔터프라이즈 SSO, 로그 보관, API 제한을 최신 가격표로 확인해야 함 |
| Keycloak 자체 운영 | 오픈소스 기반 SSO, SAML/OIDC, 조직 내부 정책, 커스텀 로그인 플로우가 필요할 때 | 운영, 업그레이드, 백업, HA, 테마 관리, 취약점 대응을 내부 또는 운영 파트너가 맡아야 함 |
| Laravel·Node 커스텀 | 계정·조직·권한·과금·관리자 정책이 제품 도메인과 강하게 결합될 때 | 암호·토큰·프로토콜을 직접 발명하지 말고 검증된 라이브러리와 표준 테스트를 우선해야 함 |
| 기존 JWT 개선 | 운영 중 서비스라 전면 교체가 어렵고 점진적 전환이 필요할 때 | issuer, audience, token version, jti, refresh rotation, revoke table을 단계적으로 추가해야 함 |
Keycloak은 OIDC Provider로 well-known configuration, authorization endpoint, token endpoint, userinfo, logout, certificate, introspection, revocation endpoint 등을 제공합니다. 동시에 공식 문서도 OAuth 2.1 draft는 변경될 수 있다고 설명하므로, ‘OAuth 2.1 지원’이라는 표현만 보고 설계를 고정하기보다 현재 운영 기준은 OAuth 2.0 + RFC 9700 BCP 조합으로 잡는 편이 현실적입니다. ([keycloak.org](https://www.keycloak.org/securing-apps/oidc-layers))
Auth0 같은 호스팅 IdP는 Refresh Token Rotation과 재사용 탐지 같은 기능을 제품 기능으로 제공할 수 있습니다. 다만 이런 기능이 어떤 플랜과 클라이언트 유형에서 제공되는지, 로그를 얼마나 보관하는지, 조직별 SSO가 별도 과금인지 등은 계약 시점의 공식 문서와 견적서로 확인해야 합니다. ([dev.auth0.com](https://dev.auth0.com/docs/secure/tokens/refresh-tokens/refresh-token-rotation))
토큰 회수와 재사용 탐지: stateless JWT의 한계
RFC 7009는 클라이언트가 더 이상 필요하지 않은 access token 또는 refresh token을 인증 서버에 알려 무효화하는 revocation endpoint를 정의합니다. RFC 7662는 자원 서버가 토큰의 active 상태와 메타데이터를 인증 서버에 질의하는 introspection 방식을 정의합니다. 즉, 즉시 회수가 필요한 서비스는 결국 어느 정도의 서버 측 상태를 가져야 합니다. ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc7009.html?utm_source=openai))
Refresh Token은 더 민감합니다. RFC 9700은 public client의 refresh token에 대해 sender-constrained 방식 또는 refresh token rotation을 사용해 replay를 탐지해야 한다고 설명합니다. Rotation은 매 refresh마다 새 refresh token을 발급하고 이전 토큰을 무효화하며, 이미 사용된 토큰이 다시 들어오면 token family를 위험 상태로 보고 재인증을 요구하는 방식입니다. ([rfc-editor.org](https://www.rfc-editor.org/info/rfc9700/))
실무에서는 다음 원칙이 유효합니다. Access Token은 짧게, Refresh Token은 저장 위치와 회수 정책을 강하게, 관리자 권한 변경은 token version이나 introspection으로 빠르게 반영합니다. 관리자 화면과 프론트엔드 보호 구조는 Next.js App Router 인증 권한 설계 가이드처럼 라우트 보호와 API 권한 검증을 분리해서 봐야 합니다.
Laravel·Node 구현 시 현실적인 선택
Laravel에서 OAuth2 서버가 필요하다면 Passport가 공식 선택지입니다. Laravel 13 문서는 OAuth2 지원이 반드시 필요하면 Passport를, 단순 SPA·모바일·API 토큰 발급 목적이면 Sanctum을 검토하라고 설명합니다. Passport는 Authorization Code Grant with PKCE, token lifetime, revoke, purge, scopes 등을 제공합니다. ([laravel.com](https://laravel.com/docs/13.x/passport))
다만 Passport를 쓴다고 해서 OIDC Provider 전체가 자동으로 완성된다고 보면 안 됩니다. ID Token 발급, OIDC Discovery, JWKS, claim mapping, nonce 검증, RP-Initiated Logout, 고객사 SSO 요구까지 포함하면 추가 설계가 필요합니다. Laravel 백엔드가 이미 강한 팀이라면 OAuth2 서버는 Passport로 구성하고, OIDC Provider 역할은 Keycloak 같은 별도 IdP로 분리하는 구조도 검토할 수 있습니다.
Node.js에서는 oidc-provider 같은 구현체가 후보가 될 수 있습니다. OpenID Foundation 인증 구현 목록은 node oidc-provider를 Node.js용 OpenID Provider 구현체로 소개하며, PKCE와 Dynamic Registration 등 여러 기능을 제공하지만 데이터 모델과 프론트엔드 뷰는 팀이 가져가야 한다고 설명합니다. ([openid.net](https://openid.net/certification/certified-openid-connect-implementations/))
다중 테넌트 SaaS에서는 인증과 권한을 분리해야 한다
B2B SaaS에서 자주 나오는 실수는 ID Token의 email 도메인만 보고 고객사를 판단하는 것입니다. 고객사 합병, 도메인 변경, 외부 협력자 계정, 개인 이메일 초대, 여러 조직에 속한 사용자 같은 상황이 생기면 바로 깨집니다. 인증은 사용자의 identity를 확인하고, 권한은 우리 서비스의 tenant membership과 role policy에서 판단해야 합니다. 테넌트별 데이터 분리는 다중 테넌시 데이터 모델링 가이드와 함께 검토하는 편이 안전합니다.
| 데이터 | 인증 서버에 가까운 정보 | 서비스 DB에 가까운 정보 |
|---|---|---|
| 사용자 식별 | sub, issuer, provider account id | internal user id, status |
| 조직 소속 | 일부 IdP group claim 가능 | tenant_id, membership, invite status |
| 권한 | scope, high-level role claim | resource-level permission, admin policy |
| 감사 | login, token issue, failed auth | 주문 수정, 정산 승인, 관리자 변경 이력 |
운영 체크리스트: 출시 전 확인할 것

- 클라이언트 매트릭스: 웹, 모바일, 관리자, 파트너 API, 배치 작업별 client_id, redirect URI, 허용 grant type을 표로 정리했는가.
- PKCE 정책: public client뿐 아니라 웹 클라이언트에도 PKCE S256을 기본값으로 둘 것인가.
- 토큰 수명: Access Token, ID Token, Refresh Token, 관리자 세션의 수명을 각각 다르게 정의했는가.
- 회수 시나리오: 로그아웃, 탈퇴, 비밀번호 변경, 관리자 차단, 고객사 계약 종료, 기기 분실 시 어떤 토큰을 회수하는가.
- 키 관리: signing key 생성, 보관, 회전, JWKS 캐시 만료, 이전 키 유지 기간을 문서화했는가.
- 권한 변경 반영: role 변경이 즉시 반영되어야 하는 API와 다음 로그인 때 반영되어도 되는 API를 나눴는가.
- 감사 로그: 로그인 성공·실패, refresh, revoke, 관리자 권한 변경, SSO 매핑 실패를 추적할 수 있는가.
- 장애 대응: IdP 장애 시 로그인 불가, 기존 세션 유지, 관리자 긴급 접근 정책을 정했는가.
- 인수인계 문서: flow diagram, endpoint list, claim schema, scope list, token sample, 만료·회수 정책, 테스트 계정이 남아 있는가.
기존 JWT 구조에서 전환하는 순서
운영 중 서비스라면 한 번에 모든 로그인을 OIDC로 바꾸기보다 다음 순서가 안전합니다. 첫째, 현재 발급 중인 JWT의 issuer, audience, subject, exp, role claim을 조사합니다. 둘째, 사용자와 조직 식별자를 내부 DB 기준으로 고정합니다. 셋째, 신규 토큰에는 token version 또는 jti를 넣어 회수 가능성을 확보합니다. 넷째, Refresh Token 저장소와 revoke table을 추가합니다. 다섯째, API 서버가 기존 토큰과 신규 토큰을 일정 기간 함께 검증하도록 합니다. 마지막으로 관리자·모바일·외부 API 순서로 클라이언트를 이전합니다.
이 과정을 문서 없이 진행하면 프론트엔드, 모바일, 백엔드, 운영자가 서로 다른 만료 시간을 기억하게 됩니다. 외주 인수인계 프로젝트에서 가장 중요한 산출물은 소스코드보다 인증 명세서일 때가 많습니다.
AgentMit이 보는 구현 범위
AgentMit은 인증을 로그인 화면이 아니라 계정 데이터 모델, 클라이언트 등록, 토큰 수명, 권한 정책, 관리자 운영 화면, 감사 로그가 맞물리는 백엔드 구조로 봅니다. 특정 솔루션을 먼저 권하기보다, 기존 JWT 구조를 계속 개선할지, Keycloak이나 호스팅 IdP를 붙일지, Laravel·Node 기반으로 인증 서버와 SaaS 관리자 기능을 설계할지부터 판단합니다.
정부지원 MVP, B2B 포털, BizMit 같은 SaaS 운영 화면, 관리자 대시보드, 업무 자동화 프로젝트에서는 ‘나중에 SSO 붙이면 된다’가 비용을 키우는 경우가 많습니다. 요구사항 명세 단계에서 client matrix, scope matrix, token lifecycle, 관리자 권한표를 먼저 정리하면 개발 범위와 견적도 훨씬 선명해집니다.
FAQ
1. OAuth2와 OIDC는 무엇이 다른가요?
OAuth2는 API 접근 권한 위임을 위한 프레임워크이고, OIDC는 사용자 인증을 위한 ID Token과 표준 클레임을 더한 계층입니다. 로그인과 SSO가 목적이면 OIDC까지 봐야 합니다.
2. SaaS MVP도 자체 인증 서버가 필요한가요?
초기에는 외부 IdP나 프레임워크 기본 인증으로 충분한 경우가 많습니다. 다만 모바일, 관리자, 고객사별 SSO, 외부 API가 로드맵에 있으면 클라이언트·토큰·권한 정책은 초기에 문서화해야 합니다.
3. PKCE를 웹 백엔드가 있는 서비스에도 써야 하나요?
네. 최신 보안 권고에서는 PKCE를 네이티브 앱만의 기능으로 보지 않습니다. Authorization Code 탈취와 주입을 줄이기 위해 웹 애플리케이션에도 기본값으로 두는 편이 안전합니다.
4. JWT 로그아웃이나 회원 탈퇴 후 토큰 무효화는 어떻게 해야 하나요?
Refresh Token은 즉시 회수하고, Access Token은 짧게 운영하거나 introspection, denylist, token version 같은 서버 측 상태로 즉시 차단할 수 있어야 합니다.
5. Laravel이나 Node.js에서 OIDC 인증 서버를 직접 만들 수 있나요?
가능하지만 프로토콜을 직접 발명하면 안 됩니다. Laravel Passport, Keycloak, node oidc-provider 같은 검증된 선택지를 놓고 계정 모델, 동의 화면, claim, 토큰 정책을 별도로 설계해야 합니다.
참고 문서
- OAuth 2.0 Security BCP, RFC 9700: PKCE, implicit flow, password grant, refresh token rotation 기준을 확인했습니다. ([rfc-editor.org](https://www.rfc-editor.org/info/rfc9700/))
- OpenID Connect Core와 Discovery: OIDC의 identity layer, ID Token, Discovery 문서 설명에 참고했습니다. ([openid.net](https://openid.net/specs/openid-connect-core-1_0-18.html))
- RFC 7009, RFC 7662, RFC 8414: revocation, introspection, authorization server metadata 설계 기준에 참고했습니다. ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc7009.html?utm_source=openai))
- Keycloak, Laravel Passport, OpenID Foundation 인증 구현 목록, Auth0 문서: 실제 구현 선택지 비교와 운영 기능 설명에 참고했습니다. ([keycloak.org](https://www.keycloak.org/securing-apps/oidc-layers))

