JWT 로그인
JWT 로그인 (머신 투 머신)
lakeFS Team과 lakeFS Enterprise에서 사용할 수 있어요. 무료 체험을 시작하거나 문의하세요.
대화형(브라우저) 로그인에는 Single Sign On (SSO)을 사용하세요. AWS에 상주하는 워크로드에는 AWS IAM Roles를 사용하세요.
JWT 로그인은 외부 아이덴티티 프로바이더에서 이미 JWT를 보유하고 있는 워크로드 - 서비스 계정, CI 러너, 애플리케이션 백엔드 - 가 그 JWT를 lakeFS 세션 bearer 토큰으로 교환할 수 있게 해줘요. 브라우저 플로우도 없고, 워크로드마다 lakeFS access key를 프로비저닝할 필요도 없어요.
본문
동작 방식
┌──────────┐ client_credentials ┌──────────┐
│ Caller │ ────────────────────► │ IdP │
└──────────┘ │ (Entra/ │
│ external JWT │ Auth0) │
│ ◄────────────────────────── └──────────┘
│
│ POST /api/v1/auth/jwt/login
│ { "token": "<external JWT>" }
▼
┌──────────┐
│ lakeFS │
└──────────┘
│ { "token": "<lakeFS bearer>", "token_expiration": ... }
│
▼
Authorization: Bearer *** bearer>
→ any subsequent lakeFS API call
-
호출자는 자신의 IdP에서 JWT를 얻어요. 서비스 계정 워크로드의 경우 일반적으로 OAuth 2.0
client_credentials그랜트예요. -
호출자는 그 JWT를
POST /api/v1/auth/jwt/login으로 보내요. lakeFS는 IdP의 JWKS로 서명을 검증하고, 표준iss/aud/exp/iat/nbf클레임을 확인하고, JWT의 그룹/역할 클레임을 lakeFS 그룹으로 해석해요. -
lakeFS는 해석된 정책 ID를 담은 단기 세션(Session) 을 저장하고, subject가 세션 id인 bearer 토큰을 반환해요.
-
호출자는 이후 lakeFS API 호출에서 `Authorization: Bearer *** 헤더로 bearer를 사용해요.
S3 클라이언트는 외부 JWT나 lakeFS bearer 어느 쪽이든 같은 권한을 지닌 임시 S3 게이트웨이 자격 증명으로 교환할 수 있어요.
JWT 로그인에서 lakeFS 사용자 row는 만들어지지 않아요: 세션이 곧 인가 기록이에요. 서버에서 그 세션을 삭제하면 그 세션에서 발급된 모든 bearer가 즉시 무효화돼요.
언제 사용하나요?
| 시나리오 | 사용할 방식 |
|---|---|
| 대화형(사람) 로그인 | SSO |
| IAM 역할이 있는 AWS 상주 워크로드 | AWS IAM Roles |
| IdP가 발행한 JWT를 이미 보유한 워크로드 | JWT login |
| IdP가 없는 워크로드 — 정적 자격 증명이 필요한 경우 | lakeFS access key |
설정
JWT 로그인은 옵트인(opt-in)이에요. auth.providers.jwt.jwks_url이 설정되지 않으면 엔드포인트는 501 Not Implemented를 반환해요.
auth:
providers:
jwt:
# Required.
jwks_url: https://<idp>/.well-known/jwks.json
issuer: https://<idp>/
# At least one configured audience must match the token's `aud`
# claim. Empty list skips the audience check.
audiences:
- https://lakefs/api
# RFC 6901 JSON Pointer to the principal identity claim.
# Defaults to "/oid" (Entra). For Auth0 use "/sub".
identity_claim_ref: /oid
# JSON Pointer to the group / role claim. Each value must match a
# lakeFS group identifier (group name on Enterprise; generated
# group ID on Cloud — see "Mapping IdP groups to lakeFS
# permissions"). Default "/roles".
groups_claim_ref: /roles
# Caps the lakeFS session's lifetime. Effective expiry is
# min(now + session_max_ttl, jwt.exp). Default 1h.
session_max_ttl: 1h
# Clock-skew tolerance for exp / iat / nbf. Default 60s.
leeway: 60s
# How often the background sweep deletes expired sessions.
# Default 5m.
cleanup_interval: 5m
# Pin additional claims to exact string values. See
# "Per-tenant isolation" below.
required_claims:
# https://lakefscloud.io/org_id: acme
필드 레퍼런스
| Key | Required | Default | Description |
|---|---|---|---|
| jwks_url | yes | — | IdP의 JWKS 문서 URL. |
| issuer | yes | — | 토큰의 iss 클레임과 정확히 같아야 하는 값. |
| audiences | no | [] | 받아들이는 aud 값 목록. 비워 두면 이 검사를 건너뛰어요. |
| identity_claim_ref | no | /oid | principal 식별자를 가리키는 JSON Pointer. |
| groups_claim_ref | no | /roles | 그룹/역할 목록을 가리키는 JSON Pointer. |
| session_max_ttl | no | 1h | 만들어지는 세션 수명의 상한. |
| leeway | no | 60s | exp / iat / nbf에 허용되는 시계 오차. |
| cleanup_interval | no | 5m | 만료 세션 정리(sweep) 주기. |
| required_claims | no | none | 클레임 이름 → 정확한 문자열 값 맵. |
엔드포인트
POST /api/v1/auth/jwt/login
Content-Type: application/json
{ "token": "<external JWT>" }
| Status | Meaning |
|---|---|
| 200 | 성공. 본문: { "token": "", "token_expiration": }. |
| 401 | 검증 실패: 서명, 만료, audience, issuer, identity 클레임 누락 등. |
| 501 | JWT 로그인이 설정되지 않음(jwks_url 없음). |
IdP 그룹을 lakeFS 권한으로 매핑하기
JWT의 groups 클레임에 있는 모든 값에 대해 lakeFS는 일치하는 그룹을 찾아요. 그 그룹들에 연결된 정책의 합집합이 세션에 기록되고, 모든 인가 검사에서 참조돼요.
클레임 값은 lakeFS 그룹 식별자와 일치해야 해요. 그 식별자가 무엇인지는 RBAC 백엔드에 따라 달라져요:
| 배포 형태 | 클레임이 담아야 하는 그룹 식별자 |
|---|---|
| lakeFS Enterprise (built-in RBAC) | 그룹 이름, 예: data-engineers |
| lakeFS Cloud (external RBAC) | 그룹의 생성된 ID, 예: LGIDAfIbmpkx-711slxX-BGKt — display name이 아니에요 |
lakeFS Cloud에서 그룹은 생성된 ID(LGID…)를 가지고, 사람이 읽기 좋은 문자열은 display name일 뿐이에요. OIDC 설정이 그룹을 ID로 참조하는 이유(default_initial_groups: [LGID…])와 같아요. display name을 담은 클레임은 어떤 정책으로도 해석되지 않아요. 그룹의 ID는 lakectl auth groups list나 UI에서 찾을 수 있어요.
Warning
일치하지 않는 그룹 값은 조용히 건너뛰어져요. groups 클레임이 어떤 그룹 식별자와도 일치하지 않는 토큰은 권한 없는 세션을 만들고, 모든 authorized 호출이 401 insufficient permissions를 반환해요(토큰은 여전히 인증되고 - 세션은 만들어지고 - 다만 아무 것도 할 수 없어요). JWT 기반 호출을 내보내기 전에 그룹을 만들고, 정책을 연결하고, IdP가 올바른 식별자를 내보내는지 확인하세요.
Microsoft Entra ID
Entra 설정 (한 번만)
-
App Registration — "lakeFS" (리소스)
-
Expose an API → Application ID URI: 예.
api://lakefs. 이 값이audience예요. -
App roles → Create app role:
-
허용 멤버 유형: Applications
-
값:
lakefs-data-engineers(이 값이roles클레임으로 내보내지고 lakeFS 그룹 ID와 일치해야 해요). -
App Registration — "lakeFS-client" (호출자)
-
Certificates & secrets → New client secret - 값을 기록해 두세요.
-
API permissions → Add a permission → My APIs → lakeFS →
lakefs-data-engineers애플리케이션 권한에 체크 → Grant admin consent. -
(선택 사항, 멀티 테넌트 배포에서 권장) 서비스 principal의 앱 메타데이터에서
org_id(또는 유사한) 클레임을 주입하는 claims-mapping 정책을 추가하고, lakeFS 설정의required_claims로 고정하세요. 아래 Per-tenant isolation을 참고하세요.
lakeFS 설정
auth:
providers:
jwt:
jwks_url: https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys
issuer: https://login.microsoftonline.com/<tenant-id>/v2.0
audiences: ["api://lakefs"]
identity_claim_ref: /oid
groups_claim_ref: /roles
session_max_ttl: 1h
일치하는 lakeFS 그룹을 미리 만들고 정책을 연결해요:
lakectl auth groups create --id lakefs-data-engineers
lakectl auth policies create --id ReadAll --statement-document - <<'EOF'
{ "statement": [
{ "effect": "allow", "action": ["fs:*"], "resource": "*" }
]}
EOF
lakectl auth groups policies attach --id lakefs-data-engineers --policy ReadAll
교환 플로우
# 1. Acquire an Entra access token via client_credentials.
TOKEN=$(curl -s -X POST \
"https://login.microsoftonline.com/$TENANT_ID/oauth2/v2.0/token" \
-H 'content-type: application/x-www-form-urlencoded' \
-d "client_id=$ENTRA_CLIENT_ID\
&client_secret=$ENTRA_CLIENT_SECRET\
&scope=api%3A%2F%2Flakefs%2F.default\
&grant_type=client_credentials" | jq -r .access_token)
# 2. Exchange for a lakeFS bearer.
BEARER=$(curl -s -X POST "https://lakefs.example.com/api/v1/auth/jwt/login" \
-H 'content-type: application/json' \
-d "{\"token\": \"$TOKEN\"}" | jq -r .token)
# 3. Drive authenticated API calls.
curl -H "Authorization: Bearer ***" \
"https://lakefs.example.com/api/v1/repositories"
토큰 형태 검증
올바른 클레임이 있는지 페이로드를 디코딩해서 확인해요:
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
기대되는 필드:
{
"iss": "https://login.microsoftonline.com/<tenant-id>/v2.0",
"oid": "<service-principal-object-id>",
"aud": "api://lakefs",
"roles": ["lakefs-data-engineers"],
"iat": 1700000000,
"exp": 1700003600
}
roles가 없다면 App Role이 Allowed member types = Applications로 만들어졌는지, 그리고 그 역할에 대해 클라이언트 애플리케이션에 admin consent가 부여됐는지 확인하세요.
Auth0
Auth0 설정 (한 번만)
-
APIs → Create API
-
Identifier (
audience가 됨): 예.https://lakefs/api -
Signing Algorithm:
RS256(기본) -
Enable RBAC: 켬
-
Add Permissions in the Access Token: 켬
-
Permissions → Add a Permission: 사용하려는 lakeFS 그룹마다 하나씩 권한을 정의해요. 권한의 이름은 lakeFS 그룹 식별자와 같아야 해요 - lakeFS Cloud에서는 display name이 아니라 생성된 그룹 ID(예.
LGIDAfIbmpkx-711slxX-BGKt)예요. ID를 붙여넣을 때 어중간한 마지막 문자가 따라붙지 않도록 주세요. -
Applications → Create Application → Machine to Machine
-
위 API에 대해 새 애플리케이션을 승인해요.
-
애플리케이션의 행을 펼쳐 1단계에서 정의한 권한(들)에 체크해요. 그러면 M2M 클라이언트는 발급되는 모든 access token의
permissions클레임으로 이것들을 받아요. -
client_id와client_secret을 기록해 두세요.
lakeFS 설정
auth:
providers:
jwt:
jwks_url: https://YOUR_TENANT.us.auth0.com/.well-known/jwks.json
issuer: https://YOUR_TENANT.us.auth0.com/
audiences: ["https://lakefs/api"]
identity_claim_ref: /sub
groups_claim_ref: /permissions
session_max_ttl: 1h
Auth0 권한 이름과 ID가 일치하는 lakeFS 그룹을 만들고 정책을 연결해요(Entra 예제와 동일한 lakectl auth … 명령).
교환 플로우
# 1. Acquire an Auth0 access token via client_credentials.
TOKEN=$(curl -s -X POST "https://YOUR_TENANT.us.auth0.com/oauth/token" \
-H 'content-type: application/x-www-form-urlencoded' \
-d "grant_type=client_credentials\
&client_id=$AUTH0_CLIENT_ID\
&client_secret=$AUTH0_CLIENT_SECRET\
&audience=https://lakefs/api" | jq -r .access_token)
# 2. Exchange for a lakeFS bearer.
BEARER=$(curl -s -X POST "https://lakefs.example.com/api/v1/auth/jwt/login" \
-H 'content-type: application/json' \
-d "{\"token\": \"$TOKEN\"}" | jq -r .token)
# 3. Drive authenticated API calls.
curl -H "Authorization: Bearer ***" \
"https://lakefs.example.com/api/v1/repositories"
토큰 형태 검증
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
기대되는 필드:
{
"iss": "https://YOUR_TENANT.us.auth0.com/",
"sub": "<m2m-client-id>@clients",
"aud": "https://lakefs/api",
"permissions": ["lakefs-data-engineers"],
"iat": 1700000000,
"exp": 1700003600
}
permissions가 없거나 비어 있다면, API에 Enable RBAC과 Add Permissions in the Access Token이 둘 다 켜져 있는지, 그리고 M2M 애플리케이션에 그 권한이 체크되어 있는지 다시 확인하세요.
보안
비대칭 서명만 지원
검증기는 RS256/384/512, ES256/384/512, PS256/384/512를 받아들여요. HMAC 변형(HS*)과 none은 시작 시 거부돼요 - 대칭 서명은 JWKS 신뢰 모델과 호환되지 않아요.
테넌트별 격리
하나의 IdP가 여러 lakeFS 인스턴스를 위한 토큰을 발급하는 멀티 테넌트 배포(예: 하나의 Entra 테넌트가 여러 lakeFS Cloud 조직을 제공)에서는, 테넌트 A를 위해 발급된 토큰이 테넌트 B의 lakeFS에서 깨끗하게 검증될 수 있어요. iss가 같고, 시그니처가 유효하고, 둘 다 같은 Application ID URI를 사용하면 audience도 일치하거든요.
required_claims로 테넌트별 클레임을 고정해 완화해요:
auth:
providers:
jwt:
...
required_claims:
https://lakefscloud.io/org_id: <this-tenant>
서비스 principal의 앱 메타데이터에서 같은 클레임을 주입하는 IdP 쪽 claims-mapping 정책과 결합하면, 한 테넌트를 위해 발급된 토큰이 다른 테넌트에 사용될 수 없음을 보장해요.
IdP가 M2M 토큰에 대해 테넌트별 클레임을 내보낼 수 없다면 - 예를 들어 Organizations for Client Credentials가 없는 Auth0 테넌트에서는 client_credentials 그랜트에 네이티브 org_id 클레임이 제공되지 않아요 - azp(authorized party = M2M client_id)를 매칭해 전용 클라이언트를 고정하세요:
auth:
providers:
jwt:
...
required_claims:
azp: <m2m-client-id>
그러면 각 테넌트는 자기만의 M2M 애플리케이션을 사용하고, 그 클라이언트의 토큰만 받아들여져요. IdP Organization 기능에 의존하지 않고도 실질적인 격리 보장을 유지할 수 있어요.
Warning
공유 IdP 배포에서 테넌트별 격리를 건너뛰는 것은 교차 테넌트 권한 상승 위험이에요. 둘 이상의 lakeFS 인스턴스가 같은 IdP를 신뢰할 때는 항상 required_claims(org/tenant 클레임, 최소한 azp)을 설정하세요.
로깅되는 데이터
요청별 로그와 감사 레코드는 세션 인증된 호출자를 principal_type=session, 세션의 subject(예. jwt:<iss>:<oid>), 고유한 session_id로 식별해요. 원본 외부 JWT는 절대 로그로 남지 않아요. 검증기 에러는 클레임 이름과 타임스탬프를 노출하지만, 원본 토큰 바이트는 절대 노출하지 않아요.
취소(Revocation)
세션은 서버 쪽에 저장되기 때문에 취소는 즉각적이에요: 세션을 삭제하면 - 명시적으로든 만료되게 하든 - 그 세션에서 발급된 모든 bearer가 다음 요청에서 401을 반환해요. bearer의 시그니처가 여전히 유효하더라도요. cleanup_interval 설정은 만료된 세션이 KV에서 얼마나 자주 제거되는지 제어하고, 만료된 세션은 읽기 시점에 느긋하게(lazily) 수집되기도 해요.
감사 로그
JWT 로그인 bearer가 구동하는 모든 액션은 감사 레코드에 세션 principal을 담아요:
| 컬럼 | 값 |
|---|---|
| principal_type | session |
| subject | jwt:: (세션의 subject) |
| session_id | 살아 있는 세션 엔티티의 id |
| user | subject와 동일(하위 호환 컬럼) |
운영자는 감사 행을 principal_type=session으로 필터링해 설치 환경 전반의 모든 M2M 호출을 볼 수 있고, session_id로 필터링해 단일 로그인이 구동하는 모든 액션을 상관(correlate)할 수 있어요.
더 알아보기 (Learn more)
공식 문서의 원문은 https://docs.lakefs.io/security/jwt-login/ 에서 확인할 수 있어요.