Nodaro 문서
문서노드 레퍼런스모델템플릿AI 에이전트(MCP)개발자셀프 호스팅리서치비교

싱글 사인온

신뢰할 수 있는 ID 공급자가 셀프 호스팅 Nodaro에 사용자를 로그인시키게 하세요. EXTERNAL_SSO_PROVIDERS 구성, 계정 연결 규칙, SSO 전용 로그인을 다룹니다.

싱글 사인온(SSO)을 사용하면 신뢰할 수 있는 외부 ID 공급자가 셀프 호스팅 Nodaro에 사용자를 로그인시킬 수 있습니다. 각 공급자를 EXTERNAL_SSO_PROVIDERS 변수에 기술하면 로그인 페이지에 SSO 버튼이 표시됩니다. 사용자가 최종적으로 들어가는 계정은 내 설치의 일반 사용자입니다. SSO는 세션을 시작하는 방법일 뿐입니다. 이 페이지는 운영자 쪽 설정을 다루며, 프로토콜은 개발자용 외부 로그인(SSO)에 있습니다.

사용할 공급자 종류

종류사용 시기상태
assertion발급자가 OpenID Connect나 SAML을 지원하지 않지만 수명이 짧은 JWT에 서명할 수 있을 때입니다. 예를 들어 Nodaro를 임베딩하는 앱입니다. Nodaro가 토큰을 검증하고 일반 세션으로 교환합니다.처음부터 끝까지 작동합니다.
oidcID 공급자가 OpenID Connect를 지원하고 내 설치의 Supabase Auth에 구성되어 있을 때입니다. 브라우저가 일반적인 Supabase 로그인 리디렉션을 거치고, Supabase가 결과를 검증합니다.공급자의 id는 Supabase Auth에 있는 OAuth 공급자의 이름이어야 합니다. 예: keycloak, azure.
saml—항목은 검증을 통과하지만, 로그인 페이지가 SAML 도메인을 보낼 수 없으므로 아무도 로그인시키지 않습니다. assertion이나 oidc를 쓰세요.

SSO는 기본적으로 꺼져 있습니다. EXTERNAL_SSO_PROVIDERS가 설정되지 않으면 로그인 페이지에 SSO 버튼이 표시되지 않고, GET /v1/sso/providers는 빈 목록을 반환하며, 모든 공급자 경로가 404 unknown_provider로 응답합니다.

공급자 구성

EXTERNAL_SSO_PROVIDERS를 JSON 배열로 설정하세요. 값을 직접 쓰거나, 파일을 읽으려면 @/path/to/providers.json 형식으로 지정합니다.

[
  {
    "id": "librechat",
    "label": "LibreChat",
    "kind": "assertion",
    "secret": "a-dedicated-32+char-hmac-key-not-the-idp-session-secret",
    "audience": "nodaro",
    "claimMap": { "email": "email", "emailVerified": "email_verified", "subject": "sub" },
    "initiateUrl": "https://chat.example.com/oauth/nodaro",
    "maxLifetimeSeconds": 300
  },
  {
    "id": "keycloak",
    "label": "Acme (Keycloak)",
    "kind": "oidc",
    "supabaseProvider": "keycloak"
  }
]

형식이 잘못된 값은 부팅을 멈춥니다. 시크릿이 들어 있는 항목의 오타 때문에 공급자가 조용히 빠져서는 안 됩니다. 그러면 SSO가 404로 실패하는 것처럼 보이거나 로그인이 절반만 구성되기 때문입니다.

Compose 스택에서는 Compose 파일이 .env에서 EXTERNAL_SSO_PROVIDERS와 EXTERNAL_SSO_LINK_EXISTING을 전달하지 않으므로, nodaro 서비스의 environment: 아래에 추가하세요. 파일을 쓰려면 컨테이너에 파일을 마운트하고 컨테이너 안의 경로를 지정하세요.

공급자 필드

필드적용 대상역할
id전체URL에 안전한 이름입니다. 첫 글자는 소문자나 숫자이고, 그 뒤로 소문자, 숫자, _, -를 쓸 수 있으며, 전체 1~63자이고 점은 쓸 수 없습니다. /v1/sso/<id> 경로에 나타나며 사용자에게 저장됩니다. 고유해야 합니다.
label전체공급자의 이름입니다. 공급자가 두 개 이상이고 서피스 프로필에 auth.ssoLabel이 없을 때만 로그인 버튼에 나타납니다.
kind전체assertion, oidc, saml 중 하나입니다.
secretassertion, 필수어서션의 서명을 검증하는 HS256 키이며, 16자 이상이어야 합니다. ID 공급자 자체의 세션 시크릿이 아닌 전용 키를 쓰세요. 그래야 Nodaro 쪽에서 유출되어도 공급자의 세션을 위조할 수 없습니다.
audienceassertion, 필수어서션의 aud 클레임과 같아야 하는 값입니다.
claimMapassertion이메일, 인증 여부 플래그, 주체를 담은 클레임입니다. 기본값은 { "email": "email", "emailVerified": "email_verified", "subject": "sub" }입니다.
initiateUrlassertion절대 URL입니다. 사용자가 로그인 버튼을 클릭하면 Nodaro가 이곳으로 리디렉션합니다. 공급자가 사용자를 로그인시킨 뒤 어서션과 함께 돌아옵니다. 이 값이 없으면 클릭했을 때 400 no_assertion으로 응답합니다.
initiateUrlByHostassertion포트 없는 소문자 호스트 이름을 절대 URL에 대응시키는 맵입니다. 해당 호스트로 들어온 클릭은 initiateUrl 대신 그 URL로 갑니다. 여러 호스트 이름으로 응답하는 설치용입니다. 잘못된 키는 부팅을 멈추며, 이 맵은 절대 공개되지 않습니다.
maxLifetimeSecondsassertion어서션이 가질 수 있는 최대 수명(exp 빼기 iat)입니다. 기본값은 300(5분)이고 최댓값은 3600입니다.
supabaseProvideroidc검증은 하지만 쓰이지 않습니다. 로그인 페이지는 공급자의 id를 Supabase에 전달합니다.
domainoidc, saml검증은 하지만 쓰이지 않습니다. oidc나 saml 항목에는 domain 또는 supabaseProvider가 필요합니다.

공개되는 것은 id, label, kind뿐이며, GET /v1/sso/providers로만 공개됩니다. 시크릿은 서버를 벗어나지 않습니다.

Nodaro가 어서션에서 확인하는 것

assertion 공급자의 ID 서버는 JWT에 서명하고 브라우저를 GET /v1/sso/<id>?assertion=<jwt>로 보냅니다. Nodaro는 다음 조건을 모두 충족할 때만 이를 받습니다.

  • HS256과 공급자의 secret으로 서명되어 있어야 합니다.
  • aud 값이 공급자의 audience와 같아야 합니다.
  • exp 값이 있고 5초의 허용 오차 안에서 지나지 않았어야 하며, 수명이 maxLifetimeSeconds 이내여야 합니다.
  • jti 값이 있고 한 번도 쓰인 적이 없어야 합니다. 각 어서션은 한 번만 작동합니다.
  • 이메일 클레임이 있어야 합니다. 계정을 만들거나 연결하려면 인증 여부 클레임이 true여야 합니다.

확인에 실패하면 401로 응답합니다. 이 경로는 IP 주소 하나당 60초에 20회까지 요청을 허용하고, 그다음에는 429로 응답합니다. 전체 계약, 오류 코드, 리디렉션 흐름은 개발자용 외부 로그인(SSO)에 있습니다.

계정 연결 방식

어서션이 통과하면 Nodaro는 일치하는 사용자를 찾거나 만듭니다. 이 규칙은 이메일 주소가 같다는 이유만으로 어서션이 기존 계정을 가로채는 일이 절대 없도록 합니다.

상황결과
해당 이메일의 계정이 없고, 이메일이 인증됨새 사용자가 만들어지고 공급자에 연결됩니다.
해당 이메일의 계정이 없고, 이메일이 인증되지 않음거부: 403 email_unverified.
계정이 이미 이 공급자에 연결됨로그인됩니다.
계정이 다른 공급자에 연결됨거부: 403 account_linked_other_provider. EXTERNAL_SSO_LINK_EXISTING=true여도 마찬가지입니다.
SSO 없는 로컬 계정이 있고, EXTERNAL_SSO_LINK_EXISTING=true이며, 이메일이 인증됨계정이 공급자에 연결되고 로그인됩니다.
SSO 없는 로컬 계정이 있고, 플래그가 false이거나 이메일이 인증되지 않음거부: 403 account_exists.

EXTERNAL_SSO_LINK_EXISTING의 기본값은 false이며, 계정 탈취에 안전한 설정입니다. 대소문자와 관계없이 true 또는 1만 이 기능을 활성화합니다. 공급자의 인증된 이메일 클레임을 기존 계정에 붙여도 될 만큼 신뢰할 때만 활성화하세요.

account_exists와 email_unverified는 모든 계정에 같은 문구를 쓰므로, 로그인 양식으로 어떤 주소가 있는지 알아낼 수 없습니다. 서버 로그에는 어떤 계정이 대상이었는지 기록됩니다.

로그인 버튼

공급자가 하나 이상 구성되면 로그인 페이지에 SSO 버튼이 표시됩니다.

  • 공급자가 하나일 때: 버튼에 싱글 사인온 또는 서피스 프로필의 auth.ssoLabel이 표시됩니다.
  • 공급자가 여러 개일 때: 서피스 프로필에 auth.ssoLabel이 없으면 각 버튼에 해당 공급자의 label이 표시됩니다.

로그인한 뒤 사용자는 /projects로 이동합니다. 공급자는 다시 리디렉션할 때 &next=<path>를 추가해 다른 페이지로 보낼 수 있습니다. Nodaro는 이 값이 같은 출처의 상대 경로이고, /로 시작하되 //로 시작하지 않을 때만 따릅니다. 그 밖의 값은 /projects로 갑니다. CORS_ORIGIN에 나열된 호스트에서는 도착 리디렉션이 상대 경로로 유지되어, 사용자가 들어온 호스트에 머뭅니다.

SSO로만 로그인 허용

Business 에디션에서는 서피스 프로필로 다른 로그인 방법을 없앨 수 있습니다.

NODARO_SURFACE_PROFILE={"auth":{"methods":["sso"],"ssoLabel":"Sign in with Acme"}}

sso만 나열한 목록은 서버 쪽 규칙도 활성화합니다. 로그인한 모든 계정이 SSO를 통해 만들어졌거나 연결되어 있어야 합니다. 다른 세션은 첫 API 호출에서 403 sso_required로 거부되므로, 로그인 서비스에 직접 등록한 계정은 설치를 쓸 수 없습니다. 목록에 email을 추가하면 이 규칙이 꺼집니다. 에디션과 서피스 프로필을 참조하세요.

보안 요약

  • 공급자마다 공급자 자체의 세션 시크릿과 다른 전용 시크릿.
  • 서버가 강제하는 짧은 수명의 일회용 어서션.
  • 로그인 후에는 같은 출처로만 리디렉션.
  • 시크릿은 서버를 벗어나지 않습니다. 어서션과 일회용 토큰은 요청 로그에서 제거됩니다.
  • IP 주소별로 교환 경로에 요청 한도 적용.
  • 일반 세션. 교환 후 사용자는 특별한 자격 증명이 없는 일반 사용자입니다.

자주 묻는 질문

싱글 사인온은 기본적으로 켜져 있나요?

아니요. EXTERNAL_SSO_PROVIDERS가 설정되지 않으면 로그인 페이지에 SSO 버튼이 표시되지 않고, 모든 SSO 경로가 404로 응답합니다. 활성화하려면 공급자를 하나 이상 구성하세요.

어떤 종류의 공급자를 구성해야 하나요?

Nodaro를 임베딩하는 앱처럼 수명이 짧은 서명 토큰을 발급할 수 있는 발급자에는 assertion을 쓰세요. 내 설치의 Supabase Auth에 구성한 ID 공급자에는 oidc를 쓰세요. saml 종류의 항목은 검증은 통과하지만 아무도 로그인시키지 않습니다.

SSO가 이메일이 같은 기존 계정을 가로챌 수 있나요?

기본적으로는 불가능합니다. EXTERNAL_SSO_LINK_EXISTING이 false이므로, 이미 로컬 계정이 있는 이메일의 어서션은 account_exists로 거부됩니다. 공급자의 인증된 이메일 클레임을 신뢰할 때만 true로 설정하세요.

SSO로만 로그인하도록 하려면 어떻게 하나요?

Business 에디션에서 서피스 프로필의 auth.methods를 ["sso"]로 설정하고 auth.ssoLabel을 지정하세요. 그러면 모든 계정이 SSO를 거쳐야 하며, 다른 세션은 403 sso_required로 거부됩니다.

EXTERNAL_SSO_PROVIDERS에 실수가 있으면 어떻게 되나요?

설치가 부팅을 거부합니다. 형식이 잘못된 값 때문에 공급자가 조용히 빠지거나 로그인이 절반만 구성되는 일은 없습니다.

최종 업데이트

목차