This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 9a96139f78 [#11279] docs: Add built-in IDP operator how-to and align
design doc (#11281)
9a96139f78 is described below
commit 9a96139f78bf708c4c057fab892a8716baad728e
Author: MaSai <[email protected]>
AuthorDate: Fri May 29 10:02:08 2026 +0800
[#11279] docs: Add built-in IDP operator how-to and align design doc
(#11281)
### What changes were proposed in this pull request?
- Add `docs/how-to-use-built-in-idp.md`, an operator guide for the
built-in IDP (`idp-basic` plugin): prerequisites (REST extension
package, schema upgrade, service admin password init), configuration,
management REST APIs with `curl` examples, password rules, and
access-control integration.
- Update `design-docs/gravitino-local-authentication.md` to align with
the how-to (service admin initialization, configuration, administrative
APIs; remove outdated or inaccurate prose).
- Link the guide from `docs/index.md` and `docs/security/security.md`.
### Why are the changes needed?
Operators need a single how-to and an aligned design document as
built-in IdP capabilities land across multiple PRs under epic #10959.
Fix: #11279
### Does this PR introduce _any_ user-facing change?
Documentation only.
### How was this patch tested?
- Manual review of markdown content and cross-links.
- N/A (documentation-only change; no OpenAPI spec updates in this PR).
Made with [Cursor](https://cursor.com)
---
design-docs/gravitino-local-authentication.md | 351 +++++---------------------
docs/index.md | 1 +
docs/security/how-to-use-built-in-idp.md | 220 ++++++++++++++++
docs/security/security.md | 2 +
4 files changed, 287 insertions(+), 287 deletions(-)
diff --git a/design-docs/gravitino-local-authentication.md
b/design-docs/gravitino-local-authentication.md
index 42f0dc2e29..bc90a39ea9 100644
--- a/design-docs/gravitino-local-authentication.md
+++ b/design-docs/gravitino-local-authentication.md
@@ -62,16 +62,15 @@ username/password authentication flow.
### 3.1 Authentication Model
-The local authentication is introduced as a new Gravitino authenticator mode:
**basic**.
-
-When enabled, Gravitino authenticates incoming requests through HTTP Basic
authentication:
+Built-in IDP provides local authentication for Gravitino. Gravitino
authenticates incoming requests
+through Basic authentication:
```text
Authorization: Basic <base64(username:password)>
```
-This mode is intended for quick-start deployments and isolated environments.
It should work out of
-the box with a minimal configuration and without any dependency on an external
identity system.
+Built-in IDP targets quick-start and isolated deployments with minimal
configuration and no
+external identity system.
### 3.2 Why Basic Authentication
@@ -111,21 +110,13 @@ The recommended module name is:
- `plugins:idp-basic`
-This naming keeps the capability grouping explicit while aligning the module
name with the
-configured authenticator type. Although the module also includes the broader
built-in
-authentication capability set, the entry point exposed to Gravitino is still
the `basic`
-authenticator, including:
+The `plugins:idp-basic` module provides:
- local user and local group management,
- password hashing and verification,
- service admin initialization support,
- and the local authentication management API wiring.
-The local authentication-specific logic should be owned by
-`plugins:idp-basic`, including storage access, authenticator logic, service
admin
-initialization logic, password hashing, and management API exposure, so that
the feature has a
-clear packaging boundary and can evolve independently.
-
---
## 4. Password Hashing
@@ -279,8 +270,8 @@ preserving the requirement that local identity tables
remain global and metalake
## 6. Service Admin Initialization
To keep local authentication usable immediately after installation without
introducing a hard-coded
-default password, Gravitino should initialize service admin accounts from an
environment variable
-during startup when the `basic` authenticator is enabled.
+default password, Gravitino initializes service admin accounts from an
environment variable during
+startup.
### 6.1 Initialization Inputs
@@ -288,66 +279,57 @@ After Gravitino is installed, the operator should
configure:
- `gravitino.authorization.serviceAdmins`, which remains the source of truth
for service admin
usernames
-- `GRAVITINO_INITIAL_ADMIN_PASSWORD`, a JSON array of `username:password`
strings used only when a
- configured service admin does not already have a password configured in the
gravitino
-
-Each username in `GRAVITINO_INITIAL_ADMIN_PASSWORD` should match a user
configured in
-`gravitino.authorization.serviceAdmins`.
+- `GRAVITINO_INITIAL_ADMIN_PASSWORD`, the initial password for service admins
that do not yet have a
+ row in `idp_user_meta`. Usernames come from
`gravitino.authorization.serviceAdmins`.
### 6.2 Initialization Process
The initialization process should be:
-1. Install Gravitino and configure the `basic` authenticator together with
- `gravitino.authorization.serviceAdmins`.
-2. If `basic` authentication is enabled for the first startup, set the
- `GRAVITINO_INITIAL_ADMIN_PASSWORD` environment variable when any configured
service admin does
- not yet have a password configured in the gravitino.
-3. Parse `GRAVITINO_INITIAL_ADMIN_PASSWORD` as a JSON array of
`username:password` strings.
-4. Validate the input before writing anything to the database:
- - the value must be valid JSON
- - every entry must use the format `username:password`
- - the service admin name must not contain a colon (`:`)
- - the password must satisfy the local authentication password policy
-5. Connect to the configured JDBC backend during Gravitino startup.
-6. For each user configured in `gravitino.authorization.serviceAdmins`, check
whether that service
- admin already has a password configured in the gravitino.
-7. If the service admin already has a password configured, continue startup
without modifying the
- stored password.
-8. If `GRAVITINO_INITIAL_ADMIN_PASSWORD` is configured and the service admin
does not yet have a
- password configured in the gravitino, hash the supplied password with
Argon2id and initialize that
- service admin account.
-9. If `GRAVITINO_INITIAL_ADMIN_PASSWORD` is not configured and any configured
service admin does not
- yet have a password configured in the gravitino, fail startup immediately
and prompt the user to
+1. Install Gravitino and configure `gravitino.authorization.serviceAdmins` and
+ `gravitino.server.rest.extensionPackages`.
+2. Before the first startup, set the `GRAVITINO_INITIAL_ADMIN_PASSWORD`
environment variable when
+ any configured service admin does not yet have a password stored in
`idp_user_meta`.
+3. Validate `GRAVITINO_INITIAL_ADMIN_PASSWORD` before writing anything to the
database. The password
+ must satisfy the local authentication password policy.
+4. For each user configured in `gravitino.authorization.serviceAdmins`, check
whether that service
+ admin already has a password stored in `idp_user_meta`.
+5. If the service admin already has a password in `idp_user_meta`, continue
startup without
+ modifying the stored password.
+6. If `GRAVITINO_INITIAL_ADMIN_PASSWORD` is configured and the service admin
does not yet have a
+ password stored in `idp_user_meta`, hash the supplied password with
Argon2id and create that
+ service admin account in `idp_user_meta`.
+7. If `GRAVITINO_INITIAL_ADMIN_PASSWORD` is not configured and any configured
service admin does not
+ yet have a password stored in `idp_user_meta`, fail startup immediately and
prompt the user to
declare `GRAVITINO_INITIAL_ADMIN_PASSWORD`.
This design keeps the first-use flow explicit while avoiding any built-in
default credential. The
-service admin exists before the first authenticated request is served, and the
database stores only
-the password hash rather than plaintext input.
+service admin exists before the first authenticated request is served, and
only password hashes are
+written to the database.
### 6.3 Example Initialization Flow
The following end-to-end flow shows how an operator can provision the initial
service admins for a
fresh Gravitino deployment.
-1. Deploy Gravitino with the `basic` authenticator enabled:
+1. Deploy Gravitino with built-in IDP enabled:
```properties
- gravitino.authenticators=basic
+
gravitino.server.rest.extensionPackages=org.apache.gravitino.idp.web.rest.feature
gravitino.authorization.serviceAdmins=admin1,admin2
```
-2. Export the initial service admin passwords before starting Gravitino:
+2. Export the initial service admin password before starting Gravitino:
```bash
- export
GRAVITINO_INITIAL_ADMIN_PASSWORD='["admin1:passwordForAdmin1","admin2:passwordForAdmin2"]'
+ export GRAVITINO_INITIAL_ADMIN_PASSWORD='passwordForServiceAdmins'
```
3. Start Gravitino.
-4. During startup, Gravitino validates the JSON payload, initializes passwords
only for service
- admins that do not yet have one configured in the gravitino, and leaves
existing service admin
- passwords unchanged.
+4. During startup, Gravitino validates the password, applies it to each
username in
+ `gravitino.authorization.serviceAdmins` that does not yet have a stored
password, and leaves
+ existing service admin passwords unchanged.
---
@@ -399,13 +381,9 @@ The proposed behavior is:
7. If authentication is challenged, include a `WWW-Authenticate: Basic`
response header.
8. If all checks pass, continue request processing as the authenticated
principal.
-> The exact HTTP status mapping should be aligned with Gravitino's existing
authentication and
-> exception handling conventions during implementation. A better default is:
-> malformed Basic header → `400`; missing user or invalid password → `401`.
-
### 7.5 Transport Security
-HTTP Basic authentication should be used over **HTTPS**.
+Basic authentication should be used over **HTTPS**.
If Basic authentication is enabled on plain HTTP, Gravitino should emit a
warning-level log because
credentials are otherwise exposed on the wire.
@@ -414,116 +392,44 @@ credentials are otherwise exposed on the wire.
## 8. Configuration
-### 8.1 Authenticator Selection
-
-Add `basic` to the configurable values of `gravitino.authenticators`.
-
-| Key | New Value | Default | Optional Values |
-|---|---|---|---|
-| `gravitino.authenticators` | `basic` | `simple` | `simple`, `oauth`,
`kerberos`, `basic` |
+### 8.1 Built-in IDP settings
-This should follow Gravitino's existing multi-authenticator behavior: multiple
authenticators are
-comma-separated, and if a request is supported by multiple authenticators
simultaneously, the first
-matching authenticator wins.
+| Key | Value
| Required when using built-in IDP |
+|---------------------------------------------|-------------------------------------------------|----------------------------------|
+| `gravitino.server.rest.extensionPackages` |
`org.apache.gravitino.idp.web.rest.feature` | Yes
|
+| `gravitino.authorization.serviceAdmins` | Comma-separated service admin
| Yes |
-The local authentication management capability is enabled only when `basic` is
included in
-`gravitino.authenticators`. If `basic` is not enabled, Gravitino should not
allow local authentication
-management APIs to be used.
+List `org.apache.gravitino.idp.web.rest.feature` in
`gravitino.server.rest.extensionPackages` so
+Jersey registers `/api/idp/*` management APIs. Callers must use Basic
authentication with a username
+in `gravitino.authorization.serviceAdmins` and a password stored in
`idp_user_meta`.
### 8.2 Password Algorithm
The initial implementation uses Argon2id as the fixed password hashing
algorithm.
-### 8.3 How Trino Accesses IRC with Basic Authentication
-
-For Trino to access IRC with Basic authentication, Trino must act as an HTTP
client for IRC requests
-and attach the required authentication information to the outbound REST
catalog requests.
-
-The current Trino version used here does not support this path out of the box,
so users cannot
-directly configure Trino to access IRC through Basic authentication in the
current baseline.
-
-If a user needs this capability, they can merge
-[`trinodb/trino#29132`](https://github.com/trinodb/trino/pull/29132) first,
and then use the
-extended REST catalog header passing capability introduced by that change as
the foundation for the
-IRC access path.
-
---
## 9. Administrative Operations
-Local authentication must support the following administrator-managed
operations:
-
-- get user
-- add user
-- remove user
-- change user password
-- get group
-- add group
-- remove group
-- add user to group
-- remove user from group
-
-These operations are expected to be performed by the service admin. After
membership changes,
-administrators should be able to inspect group information and user
information to verify the
-result.
-
-At a high level:
-
-1. **Get user**: read the local user information and its current group
memberships.
-2. **Add user**: create a new local user with a hashed password.
-3. **Remove user**: soft-delete the user record.
-4. **Change user password**: reset the password hash for an existing local
user through an
- administrator-managed operation.
-5. **Get group**: read the local group information and its current user
memberships.
-6. **Add group**: create a new local group.
-7. **Remove group**: soft-delete the group record.
-8. **Add user to group**: create a row in `idp_user_group_rel`.
-9. **Remove user from group**: soft-delete the corresponding relation row.
+Service admins manage local users and groups under `/api/idp` (global paths,
no `{metalake}`).
+Operations are available when `org.apache.gravitino.idp.web.rest.feature` is
listed in
+`gravitino.server.rest.extensionPackages`.
### 9.1 HTTP Interface Design
-The following APIs are intended for local user, local group, and
group-membership management.
-
-Because local authentication identities are global rather than
metalake-scoped, these management interfaces do
-not include `{metalake}` in their paths and use the `/api/idp` prefix.
-
-These APIs are available only when the `basic` authenticator is enabled. If
`basic` is not enabled,
-requests to these local authentication management endpoints should be rejected
rather than treated as available
-server APIs.
-
-All of the following operations are administrator-managed operations. They are
intended to be called
-by the configured service admin rather than by end users.
-
#### 9.1.1 Get a local user
-You can get a local user by its name. The response should include the user
name and current group
-memberships.
-
-The request path for REST API is `/api/idp/users/{user}`.
+`GET /api/idp/users/{user}`
```shell
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/idp/users/alice
```
-**Response:**
-
-```json
-{
- "code": 0,
- "user": {
- "name": "alice",
- "groups": ["engineering", "devops"]
- }
-}
-```
#### 9.1.2 Add a local user
-You can add a local user by providing a user name and password. The password
must be stored as a
-hash rather than plaintext.
-
-The request path for REST API is `/api/idp/users`.
+`POST /api/idp/users`
```shell
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
@@ -533,45 +439,21 @@ curl -X POST -H "Accept:
application/vnd.gravitino.v1+json" \
}' http://localhost:8090/api/idp/users
```
-**Response:**
-
-```json
-{
- "code": 0,
- "user": {
- "name": "alice",
- "groups": []
- }
-}
-```
#### 9.1.3 Remove a local user
-You can remove a local user by its name. This operation should soft-delete the
user record rather
-than physically remove it immediately.
-
-The request path for REST API is `/api/idp/users/{user}`.
+`DELETE /api/idp/users/{user}` (soft-delete)
```shell
curl -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/idp/users/alice
```
-**Response:**
-
-```json
-{
- "code": 0,
- "removed": true
-}
-```
#### 9.1.4 Reset a local user password
-You can reset the password of an existing local user by providing a new
password. This API is an
-administrator-managed password reset API rather than an end-user self-service
password change API.
-
-The request path for REST API is `/api/idp/users/{user}`.
+`PUT /api/idp/users/{user}` — administrator-only reset (`password` only; no
`oldPassword`). Usernames
+must not contain `:`. Password length is 12–64 characters.
```shell
curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
@@ -580,62 +462,19 @@ curl -X PUT -H "Accept:
application/vnd.gravitino.v1+json" \
}' http://localhost:8090/api/idp/users/alice
```
-**Response:**
-
-```json
-{
- "code": 0,
- "user": {
- "name": "alice",
- "groups": ["engineering", "devops"]
- }
-}
-```
-
-The password reset API follows these rules:
-
-- it does not support end-user self-service password changes
-- it does not accept `oldPassword`
-- only the service admin can reset account passwords
-- the user name cannot contain a colon (`:`); the Basic credential should be
parsed by splitting on
- the first colon, so the password may contain additional colons (RFC 7617)
-- the password must be at least 12 characters long and at most 64 characters
long
-
-The password reset API should return:
-
-| Error case | HTTP status |
-|---|---|
-| Account doesn't exist | `404` |
-
#### 9.1.5 Get a local group
-You can get a local group by its name. The response should include the group
name and current user
-memberships.
-
-The request path for REST API is `/api/idp/groups/{group}`.
+`GET /api/idp/groups/{group}`
```shell
curl -X GET -H "Accept: application/vnd.gravitino.v1+json" \
http://localhost:8090/api/idp/groups/engineering
```
-**Response:**
-
-```json
-{
- "code": 0,
- "group": {
- "name": "engineering",
- "users": ["alice", "bob"]
- }
-}
-```
#### 9.1.6 Add a local group
-You can add a local group by providing a group name.
-
-The request path for REST API is `/api/idp/groups`.
+`POST /api/idp/groups`
```shell
curl -X POST -H "Accept: application/vnd.gravitino.v1+json" \
@@ -644,93 +483,31 @@ curl -X POST -H "Accept:
application/vnd.gravitino.v1+json" \
}' http://localhost:8090/api/idp/groups
```
-**Response:**
-
-```json
-{
- "code": 0,
- "group": {
- "name": "engineering",
- "users": []
- }
-}
-```
#### 9.1.7 Remove a local group
-You can remove a local group by its name. This operation should soft-delete
the group record rather
-than physically remove it immediately.
-
-Note that removing a local group will also remove all relationships between
that group and its
-users. If the group still has users, the caller must explicitly set
`force=true`.
-
-The request path for REST API is `/api/idp/groups/{group}`.
+`DELETE /api/idp/groups/{group}?force={true|false}` — soft-delete. Use
`force=true` when the group
+still has members.
```shell
curl -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
'http://localhost:8090/api/idp/groups/engineering?force=true'
```
-**Response:**
-
-```json
-{
- "code": 0,
- "removed": true
-}
-```
-
-#### 9.1.8 Add users to a local group
-
-You can add users to a local group by providing the group name in the path and
the target user
-names in the request body.
-
-The request path for REST API is `/api/idp/groups/{group}/add`.
-
-```shell
-curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
--H "Content-Type: application/json" -d '{
- "users": ["alice", "bob"]
-}' http://localhost:8090/api/idp/groups/engineering/add
-```
-
-**Response:**
-```json
-{
- "code": 0,
- "group": {
- "name": "engineering",
- "users": ["alice", "bob"]
- }
-}
-```
+#### 9.1.8 Change local group membership
-#### 9.1.9 Remove users from a local group
-
-You can remove users from a local group by providing the group name in the
path and the target
-user names in the request body.
-
-The request path for REST API is `/api/idp/groups/{group}/remove`.
+`PUT /api/idp/groups/{group}/users` — at least one of `usersToAdd` or
`usersToRemove` is required.
```shell
curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
-H "Content-Type: application/json" -d '{
- "users": ["alice"]
-}' http://localhost:8090/api/idp/groups/engineering/remove
+ "usersToAdd": ["alice", "bob"],
+ "usersToRemove": ["carol"]
+}' http://localhost:8090/api/idp/groups/engineering/users
```
-**Response:**
-```json
-{
- "code": 0,
- "group": {
- "name": "engineering",
- "users": ["bob"]
- }
-}
-```
---
## 10. Work Plan and Checklist
@@ -739,7 +516,7 @@ curl -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
| Phase | Work Item | Module / Files | Notes |
|---|---|---|---|
-| 1 | Authenticator module wiring | `settings.gradle.kts`,
`server/build.gradle.kts`, `plugins:idp-basic` | Add the new module and make
the server load it when `gravitino.authenticators=basic`. |
+| 1 | Built-in IDP module wiring | `settings.gradle.kts`,
`server/build.gradle.kts`, `plugins:idp-basic` | Add the new module and
register `IdpRESTFeature` through `gravitino.server.rest.extensionPackages`. |
| 2 | Password hashing support | `PasswordHasher`, `Argon2idPasswordHasher`,
related tests | Use Argon2id as the only supported password hashing algorithm
and store PHC-style hash strings. |
| 3 | IdP metadata schema | JDBC schema files, mapper definitions, store layer
| Create `idp_user_meta`, `idp_group_meta`, and `idp_user_group_rel` with
soft-delete support. |
| 4 | Service admin initialization | startup initialization logic, validation
logic | Validate `GRAVITINO_INITIAL_ADMIN_PASSWORD`, initialize missing
configured service admins during startup, and fail startup when required
credentials are absent. |
@@ -752,13 +529,13 @@ curl -X PUT -H "Accept:
application/vnd.gravitino.v1+json" \
| Area | Checklist |
|---|---|
-| Module wiring | The design, module name, and server wiring all consistently
use `plugins:idp-basic`, while the authenticator mode remains `basic`. |
-| Configuration | All examples use `gravitino.authenticators=basic`, and no
obsolete configuration keys remain in the document. |
+| Module wiring | The design, module name, and server wiring all consistently
use `plugins:idp-basic`. |
+| Configuration | All examples use `gravitino.server.rest.extensionPackages`
and service-admin settings, and no obsolete configuration keys remain in the
document. |
| Schema design | The document consistently uses `idp_user_meta`,
`idp_group_meta`, and `idp_user_group_rel`, and the soft-delete lifecycle is
clearly described. |
| Security constraints | The document states that passwords are never stored
in plaintext, Basic authentication should be used only over HTTPS, and
initialization must enforce password policy. |
| Initialization flow | The document explains how configured service admins
are initialized during startup, when `GRAVITINO_INITIAL_ADMIN_PASSWORD` is
required, and that only hashed passwords are written. |
| Authentication flow | The Basic authentication flow is described end-to-end,
including username/password parsing, user lookup, hash verification, and group
resolution. |
-| API contract | The request paths, request bodies, and response bodies for
all `/api/idp` APIs are defined consistently across the document. |
+| API contract | Request paths and bodies for `/api/idp` APIs match the
[Built-in IDP OpenAPI](../docs/open-api/idp/openapi.yaml). |
| Implementation alignment | The proposed work items are specific enough that
reviewers and AI tools can map each part of the design to code changes and
tests. |
---
@@ -781,7 +558,7 @@ The local authentication capability is intentionally
lightweight, but the follow
This design adds local authentication support to Gravitino for environments
where an external IdP is
either unavailable or unnecessarily heavy. The key design choices are:
-- **HTTP Basic authentication**
+- **Basic authentication**
- **username/password credentials**
- **database-backed storage**
- **Argon2id password hashing**
diff --git a/docs/index.md b/docs/index.md
index f30fd380ab..2f9508a406 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -211,6 +211,7 @@ Gravitino provides security configurations for Gravitino,
including HTTPS, authe
* [HTTPS](./security/how-to-use-https.md): provides HTTPS configurations.
* [Authentication](./security/how-to-authenticate.md): provides authentication
configurations including simple, basic, OAuth, and Kerberos.
+* [Built-in IDP](./security/how-to-use-built-in-idp.md): operator guide for
the built-in identity provider (`idp-basic` plugin), including service admin
setup and `/api/idp` management APIs.
* [Access Control](./security/access-control.md): provides access control
configurations.
* [CORS](./security/how-to-use-cors.md): provides CORS configurations.
diff --git a/docs/security/how-to-use-built-in-idp.md
b/docs/security/how-to-use-built-in-idp.md
new file mode 100644
index 0000000000..ef18b44eaf
--- /dev/null
+++ b/docs/security/how-to-use-built-in-idp.md
@@ -0,0 +1,220 @@
+---
+title: How to use built-in IDP
+slug: /security/how-to-use-built-in-idp
+keyword: security authentication idp
+license: "This software is licensed under the Apache License version 2."
+---
+
+## Introduction
+
+Apache Gravitino can store **built-in IDP** (identity provider) users and
groups in the relational
+metadata store through the `idp-basic` plugin. This gives you a self-contained
way to manage
+**global** login identities (usernames, password hashes, and group membership)
without an external server.
+
+Built-in IDP is aimed at POC, offline, and isolated deployments. It is **not**
a replacement for
+enterprise IDPs such as Okta, Azure AD, or Keycloak. Use it only where a
lightweight local identity
+store is acceptable; restrict management APIs to **service admins**, store
password hashes only,
+and prefer [HTTPS](how-to-use-https.md) when credentials travel over the
network.
+
+This guide describes how to enable and operate the management APIs in
`plugins:idp-basic`. For
+request and response schemas, see the [Built-in IDP
OpenAPI](../open-api/idp/openapi.yaml).
+
+---
+
+## Prerequisites
+
+Before you call `/api/idp/*`, ensure the following:
+
+1. **IDP REST API registration** — In `gravitino.conf`, set:
+
+ ```properties
+ gravitino.server.rest.extensionPackages =
org.apache.gravitino.idp.web.rest.feature
+ ```
+
+2. **Service admin passwords** — Built-in IDP requires every username in
+ `gravitino.authorization.serviceAdmins` to have a password stored in
`idp_user_meta` before you
+ can call management APIs.
+
+ 1. Set service admin usernames in `gravitino.conf` (see [Access
control](access-control.md)):
+
+ ```properties
+ gravitino.authorization.serviceAdmins = admin
+ ```
+
+ 2. **Initialize service admin passwords at startup** — Before the first
start, set
+ `GRAVITINO_INITIAL_ADMIN_PASSWORD` to the initial password. Usernames
come from
+ `gravitino.authorization.serviceAdmins`. The value must satisfy the
+ [password rules](#password-and-username-rules) below.
+
+ ```shell
+ export GRAVITINO_INITIAL_ADMIN_PASSWORD='Passw0rd-Admin12'
+ ```
+
+ 3. **Start Gravitino**.
+
+ 4. **Call management APIs** — Use Basic authentication with a service admin
username and
+ password (for example `admin` / `Passw0rd-Admin12`).
+
+---
+
+## Configuration
+
+Set service admins in `gravitino.conf` (see also
[Prerequisites](#prerequisites)):
+
+| Configuration item | Description
| Example |
+|-----------------------------------------|-------------------------------------------------------------------------------------|---------|
+| `gravitino.authorization.serviceAdmins` | Comma-separated service admin that
can call built-in IDP management APIs | `admin` |
+
+Example:
+
+```properties
+gravitino.server.rest.extensionPackages =
org.apache.gravitino.idp.web.rest.feature
+gravitino.authorization.serviceAdmins = admin
+```
+
+---
+
+## Operations
+
+The following sections show how to call built-in IDP management APIs with
`curl`. Replace
+`localhost:8090`, usernames, and passwords with values that match your
deployment. Examples use
+Basic authentication with `admin` / `Passw0rd-Admin12` (from
[Prerequisites](#prerequisites)).
+
+**Base URL** — `http://<host>:<port>/api/idp`
+
+**Common headers**
+
+| Header | Value |
+|----------------|----------------------------------------------|
+| `Accept` | `application/vnd.gravitino.v1+json` |
+| `Content-Type` | `application/json` (for POST and PUT bodies) |
+
+Example:
+
+```shell
+curl -s -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ http://localhost:8090/api/idp/users/alice
+```
+
+### Password and username rules
+
+Password rules apply to add-user, change-password, and
`GRAVITINO_INITIAL_ADMIN_PASSWORD`:
+
+| Rule | Value |
+|-----------------|------------------------------------|
+| Username | Required; must **not** contain `:` |
+| Password length | 12–64 characters (inclusive) |
+
+Password reset is **admin-only** (request body has `password` only; no
`oldPassword`).
+
+### User operations
+
+#### Get a user
+
+`GET /api/idp/users/{user}`
+
+```shell
+curl -s -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ http://localhost:8090/api/idp/users/alice
+```
+
+#### Add a user
+
+`POST /api/idp/users`
+
+The request body uses field `user` (not `name`):
+
+```shell
+curl -s -X POST -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ -d '{"user":"alice","password":"Passw0rd-Alice"}' \
+ http://localhost:8090/api/idp/users
+```
+
+#### Change a user password
+
+`PUT /api/idp/users/{user}`
+
+Administrator reset only:
+
+```shell
+curl -s -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ -d '{"password":"Passw0rd-Alice-V2"}' \
+ http://localhost:8090/api/idp/users/alice
+```
+
+#### Remove a user
+
+`DELETE /api/idp/users/{user}`
+
+```shell
+curl -s -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ http://localhost:8090/api/idp/users/alice
+```
+
+### Group operations
+
+#### Get a group
+
+`GET /api/idp/groups/{group}`
+
+```shell
+curl -s -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ http://localhost:8090/api/idp/groups/engineering
+```
+
+#### Add a group
+
+`POST /api/idp/groups`
+
+The request body uses field `group` (not `name`):
+
+```shell
+curl -s -X POST -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ -d '{"group":"engineering"}' \
+ http://localhost:8090/api/idp/groups
+```
+
+#### Remove a group
+
+`DELETE /api/idp/groups/{group}?force={true|false}`
+
+If the group still has members, deletion fails unless `force=true`.
+
+```shell
+curl -s -X DELETE -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ 'http://localhost:8090/api/idp/groups/engineering?force=true'
+```
+
+#### Change group membership
+
+`PUT /api/idp/groups/{group}/users`
+
+Add and/or remove members in one request. At least one of `usersToAdd` or
`usersToRemove` is required.
+
+```shell
+curl -s -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
+ -d '{"usersToAdd":["alice","bob"],"usersToRemove":["carol"]}' \
+ http://localhost:8090/api/idp/groups/engineering/users
+```
+
+For full request and response definitions, see the [Built-in IDP
OpenAPI](../open-api/idp/openapi.yaml).
+
+---
+
+## Further reading
+
+- [Built-in IDP OpenAPI](../open-api/idp/openapi.yaml) — API paths, bodies,
and schemas
+- [How to use HTTPS](how-to-use-https.md) — transport security for credentials
diff --git a/docs/security/security.md b/docs/security/security.md
index 7fccd21807..5f6b9edc7b 100644
--- a/docs/security/security.md
+++ b/docs/security/security.md
@@ -17,6 +17,8 @@ Gravitino has supported the following security features:
### [Authentication](how-to-authenticate.md)
+### [Built-in IDP](how-to-use-built-in-idp.md)
+
### [HTTPS](how-to-use-https.md)
### [Access Control](access-control.md)