This is an automated email from the ASF dual-hosted git repository.

jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new 456e239a33 [Cherry-pick to branch-1.3] Docs: update the security 
section (#12391) (#12413)
456e239a33 is described below

commit 456e239a334a03498b4c9044baaa57d4e3baaa25
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Tue Aug 11 20:19:50 2026 +0800

    [Cherry-pick to branch-1.3] Docs: update the security section (#12391) 
(#12413)
    
    **Cherry-pick Information:**
    - Original commit: 36425469a1b50a6650b4506431ce054952793257
    - Target branch: `branch-1.3`
    - Status: ✅ Clean cherry-pick (no conflicts)
    
    Co-authored-by: Mark Hoerth <[email protected]>
---
 .../flink-authentication-with-gravitino.md         |   6 +-
 docs/gravitino-server-config.md                    |  10 +-
 docs/how-to-use-gvfs.md                            |  10 +-
 docs/iceberg-rest-engine/flink.md                  |   3 +
 docs/iceberg-rest-engine/ray.md                    |   3 +
 docs/iceberg-rest-engine/spark.md                  |   2 +-
 docs/iceberg-rest-engine/trino.md                  |   3 +-
 docs/index.md                                      |   2 +-
 docs/open-api/idp/idp.yaml                         | 102 ++--
 docs/open-api/idp/openapi.yaml                     |   8 +-
 docs/security/access-control.md                    |   3 +-
 docs/security/authorization-pushdown.md            | 140 +++---
 docs/security/how-to-authenticate.md               |  14 +-
 docs/security/how-to-use-built-in-idp.md           | 530 ---------------------
 docs/security/how-to-use-cors.md                   |  74 +--
 docs/security/how-to-use-https.md                  | 122 +++--
 docs/security/local-users-and-groups.md            | 197 ++++++++
 docs/security/security.md                          |  28 --
 .../spark-authentication-with-gravitino.md         |   6 +-
 docs/trino-connector/authentication.md             |  10 +-
 docs/webui-v2.md                                   |   8 +-
 docs/webui.md                                      |   6 +-
 22 files changed, 467 insertions(+), 820 deletions(-)

diff --git a/docs/flink-connector/flink-authentication-with-gravitino.md 
b/docs/flink-connector/flink-authentication-with-gravitino.md
index 9b5b6c7188..2e0bb5c557 100644
--- a/docs/flink-connector/flink-authentication-with-gravitino.md
+++ b/docs/flink-connector/flink-authentication-with-gravitino.md
@@ -22,14 +22,14 @@ In simple mode, the username originates from Flink. The 
resolution order is:
 ## Basic Mode
 
 In Basic mode, the Flink connector authenticates to the Gravitino server using 
HTTP Basic credentials
-against the built-in IDP. The Gravitino server must have Basic authentication 
enabled. See
+against the local user store. The Gravitino server must have Basic 
authentication enabled. See
 [How to authenticate](../security/how-to-authenticate.md#basic-mode) for 
server-side setup.
 
 | Property                                                      | Type   | 
Default Value | Description                                    | Required       
     |
 
|---------------------------------------------------------------|--------|---------------|------------------------------------------------|---------------------|
 | table.catalog-store.gravitino.gravitino.client.auth.type      | string | 
(none)        | Set to `basic` to enable Basic authentication. | Yes, for Basic 
mode |
-| table.catalog-store.gravitino.gravitino.client.basic.username | string | 
(none)        | The built-in IDP username.                     | Yes, for Basic 
mode |
-| table.catalog-store.gravitino.gravitino.client.basic.password | string | 
(none)        | The built-in IDP password.                     | Yes, for Basic 
mode |
+| table.catalog-store.gravitino.gravitino.client.basic.username | string | 
(none)        | Username in the local user store.                     | Yes, 
for Basic mode |
+| table.catalog-store.gravitino.gravitino.client.basic.password | string | 
(none)        | Password for that user.                     | Yes, for Basic 
mode |
 
 ### Basic Configuration Example
 
diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md
index 5c5afaa792..707791a1fd 100644
--- a/docs/gravitino-server-config.md
+++ b/docs/gravitino-server-config.md
@@ -16,7 +16,7 @@ authorization on.
 This page covers the server itself. Catalog properties, which configure an 
individual catalog
 rather than the server, are covered further down. Properties for the auxiliary 
services live with
 those services: see [Iceberg REST Catalog Service](iceberg-rest-service.md) and
-[Security](security/security.md).
+[Security](security/how-to-authenticate.md).
 
 ## Quick Start
 
@@ -104,7 +104,7 @@ plain HTTP, so clients and the Web UI must move to 
`httpsPort`, which defaults t
 JWKS endpoint, which is the common case for an external identity provider. 
Static sign keys,
 Kerberos, and the OIDC login flow for the Web UI each take a different set of 
properties. See
 [How to Authenticate](security/how-to-authenticate.md), or
-[How to Use the Built-in IdP](security/how-to-use-built-in-idp.md) to keep 
users and groups in
+[Local users and groups](security/local-users-and-groups.md) to keep users and 
groups in
 Gravitino's own metadata store instead of an external provider.
 
 **`gravitino.authorization.serviceAdmins` has no default.** It is the one 
property here that
@@ -354,7 +354,7 @@ Naming an authenticator is one line; configuring it is not. 
Each value reads its
 `gravitino.authenticator.*` properties, covered in
 [How to Authenticate](security/how-to-authenticate.md). To hold users, 
password hashes, and group
 membership in Gravitino's own relational store rather than an external 
provider, see
-[How to Use the Built-in IdP](security/how-to-use-built-in-idp.md).
+[Local users and groups](security/local-users-and-groups.md).
 
 `gravitino.authenticator`, in the singular, is a deprecated spelling that 
still works.
 
@@ -651,7 +651,7 @@ in `conf/gravitino-env.sh`.
   properties of the auxiliary service named by `gravitino.auxService.names`
 - [How to Authenticate](security/how-to-authenticate.md), for the 
`gravitino.authenticator.*`
   properties behind each value of `gravitino.authenticators`
-- [How to Use the Built-in IdP](security/how-to-use-built-in-idp.md), for 
holding users, password
+- [Local users and groups](security/local-users-and-groups.md), for holding 
users, password
   hashes, and group membership in Gravitino's own relational store
 - [Access Control](security/access-control.md), for the privilege model the 
authorizer enforces once
   `gravitino.authorization.enable` is set: roles, grants, ownership, and 
metalake administration
@@ -663,4 +663,4 @@ in `conf/gravitino-env.sh`.
   store, and client certificate properties
 - [CORS](security/how-to-use-cors.md), for letting browser clients served from 
another origin call
   the API
-- [Security](security/security.md)
+- [Security](security/how-to-authenticate.md)
diff --git a/docs/how-to-use-gvfs.md b/docs/how-to-use-gvfs.md
index f7eaa514d4..0f7e030a68 100644
--- a/docs/how-to-use-gvfs.md
+++ b/docs/how-to-use-gvfs.md
@@ -349,7 +349,7 @@ FileSystem fs = filesetPath.getFileSystem(conf);
 #### `OAuth` Authentication
 
 If you want to use `oauth2` authentication for the Gravitino client in the 
Gravitino Virtual File System,
-refer to this document to complete the configuration of the Gravitino server 
and the OAuth server: [Security](security/security.md).
+refer to this document to complete the configuration of the Gravitino server 
and the OAuth server: [Security](security/how-to-authenticate.md).
 
 Then, you can configure the Hadoop configuration like this:
 
@@ -373,7 +373,7 @@ FileSystem fs = filesetPath.getFileSystem(conf);
 #### `Kerberos` Authentication
 
 If you want to use `kerberos` authentication for the Gravitino client in the 
Gravitino Virtual File System,
-refer to this document to complete the configuration of the Gravitino server: 
[Security](security/security.md).
+refer to this document to complete the configuration of the Gravitino server: 
[Security](security/how-to-authenticate.md).
 
 Then, you can configure the Hadoop configuration like this:
 
@@ -413,8 +413,8 @@ to recompile the native libraries like `libhdfs` and 
others, and completely repl
 | `cache_size`                    | The cache capacity of the Gravitino 
Virtual File System.                                                            
                                                                                
                                                                                
                                                                                
   | `20`                                                                 | No  
                     [...]
 | `cache_expired_time`            | The value of time that the cache expires 
after accessing in the Gravitino Virtual File System. The value is in 
`seconds`.                                                                      
                                                                                
                                                                                
        | `3600`                                                               
| No                       [...]
 | `auth_type`                     | The auth type the Gravitino client uses 
with the Gravitino Virtual File System. Supports `simple`, `basic`, and 
`oauth2`.                                                                       
                                                                                
                                                                                
       | `simple`                                                             | 
No                       [...]
-| `basic_username`                | The username for the Gravitino client when 
using `basic` auth type with the built-in IDP.                                  
                                                                                
                                                                                
                                                                            | 
(none)                                                               | Yes if 
you use `basic` a [...]
-| `basic_password`                | The password for the Gravitino client when 
using `basic` auth type with the built-in IDP.                                  
                                                                                
                                                                                
                                                                            | 
(none)                                                               | Yes if 
you use `basic` a [...]
+| `basic_username`                | The username for the Gravitino client when 
using `basic` auth type with the local user store.                              
                                                                                
                                                                                
                                                                                
| (none)                                                               | Yes if 
you use `basi [...]
+| `basic_password`                | The password for the Gravitino client when 
using `basic` auth type with the local user store.                              
                                                                                
                                                                                
                                                                                
| (none)                                                               | Yes if 
you use `basi [...]
 | `oauth2_server_uri`             | The auth server URI for the Gravitino 
client when using `oauth2` auth type.                                           
                                                                                
                                                                                
                                                                                
 | (none)                                                               | Yes 
if you use `oauth2`  [...]
 | `oauth2_credential`             | The auth credential for the Gravitino 
client when using `oauth2` auth type.                                           
                                                                                
                                                                                
                                                                                
 | (none)                                                               | Yes 
if you use `oauth2`  [...]
 | `oauth2_path`                   | The auth server path for the Gravitino 
client when using `oauth2` auth type. Please remove the first slash `/` from 
the path, for example `oauth/token`.                                            
                                                                                
                                                                                
   | (none)                                                               | Yes 
if you use `oauth2`  [...]
@@ -734,7 +734,7 @@ 
print(fs.ls("gvfs://fileset/fileset_catalog/tmp/test_fileset"))
 #### `OAuth` Authentication
 
 First, make sure that your Gravitino server is also configured to use the 
`oauth2` authentication mode,
-and you have an OAuth server to fetch the token: 
[Security](security/security.md).
+and you have an OAuth server to fetch the token: 
[Security](security/how-to-authenticate.md).
 
 Then, you can configure the authentication like this:
 
diff --git a/docs/iceberg-rest-engine/flink.md 
b/docs/iceberg-rest-engine/flink.md
index 8a247bc4ab..70581975c9 100755
--- a/docs/iceberg-rest-engine/flink.md
+++ b/docs/iceberg-rest-engine/flink.md
@@ -78,6 +78,9 @@ CREATE CATALOG gravitino_irc WITH (
 
 ### Basic Authentication
 
+If Gravitino uses [local users and 
groups](../security/local-users-and-groups.md) for Basic
+authentication, set the auth properties on the catalog:
+
 ```sql
 CREATE CATALOG gravitino_irc WITH (
   'type'                     = 'iceberg',
diff --git a/docs/iceberg-rest-engine/ray.md b/docs/iceberg-rest-engine/ray.md
index fc74863a9a..4b1265d2ee 100644
--- a/docs/iceberg-rest-engine/ray.md
+++ b/docs/iceberg-rest-engine/ray.md
@@ -39,6 +39,9 @@ catalog_kwargs = {
 
 ### Credential Vending with Basic Authentication
 
+If Gravitino uses [local users and 
groups](../security/local-users-and-groups.md) for Basic
+authentication, pass the credentials in the catalog arguments:
+
 ```python
 catalog_kwargs = {
     "name": "default",
diff --git a/docs/iceberg-rest-engine/spark.md 
b/docs/iceberg-rest-engine/spark.md
index b69923c391..08e47a5666 100755
--- a/docs/iceberg-rest-engine/spark.md
+++ b/docs/iceberg-rest-engine/spark.md
@@ -80,7 +80,7 @@ instance profile, in which case the explicit credential lines 
can be omitted.
 
 ### Basic Authentication
 
-If Gravitino uses [built-in IDP](../security/how-to-use-built-in-idp.md) Basic 
authentication,
+If Gravitino uses [local users and 
groups](../security/local-users-and-groups.md) for Basic authentication,
 add the auth properties to `$SPARK_HOME/conf/spark-defaults.conf`:
 
 ```properties
diff --git a/docs/iceberg-rest-engine/trino.md 
b/docs/iceberg-rest-engine/trino.md
index aafb32def3..b2ecd19dcc 100755
--- a/docs/iceberg-rest-engine/trino.md
+++ b/docs/iceberg-rest-engine/trino.md
@@ -293,7 +293,8 @@ is reached, which surfaces as a 403 rather than as a 
storage error.
 Requires Trino 481 or later.
 
 `iceberg.rest-catalog.security` has no Basic value, so Trino cannot be 
configured to authenticate to
-a REST catalog with a username and password. Gravitino's IRC does accept HTTP 
Basic, so the way
+a REST catalog with a username and password. Gravitino's IRC does accept HTTP 
Basic against
+[local users and groups](../security/local-users-and-groups.md), so the way
 across is to construct the header yourself and have Trino attach it to every 
REST catalog request.
 Trino 481 added `iceberg.rest-catalog.http-headers`
 ([trinodb/trino#24236](https://github.com/trinodb/trino/issues/24236)) for 
sending arbitrary headers,
diff --git a/docs/index.md b/docs/index.md
index 1819d190cb..9b71df7e5b 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -212,7 +212,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.
+* [Local users and groups](./security/local-users-and-groups.md): operator 
guide for the local user store behind HTTP Basic authentication, 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/open-api/idp/idp.yaml b/docs/open-api/idp/idp.yaml
index 51b44480db..2437519f9d 100644
--- a/docs/open-api/idp/idp.yaml
+++ b/docs/open-api/idp/idp.yaml
@@ -23,10 +23,10 @@ paths:
     post:
       tags:
         - IDP
-      summary: Add built-in IDP user
+      summary: Add local user store user
       description: >
-        Creates a built-in IDP user with the given username and password.
-        Requires the `idp-basic` plugin and built-in IdP Basic authentication. 
Built-in IdP is
+        Creates a local user store user with the given username and password.
+        Requires the `idp-basic` plugin and local user store Basic 
authentication. Local user store is
         incompatible with the `simple` authenticator (the default), 
`gravitino.authenticators` must
         include `basic` and must not include `simple`.
       operationId: addIdpUser
@@ -41,7 +41,7 @@ paths:
                 $ref: "#/components/examples/AddUserRequest"
       responses:
         "200":
-          description: Returns the added built-in IDP user
+          description: Returns the added local user store user
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -59,7 +59,7 @@ paths:
                 IllegalArgumentException:
                   $ref: 
"#/components/examples/IdpAddUserIllegalArgumentException"
         "409":
-          description: Conflict - The built-in IDP user already exists
+          description: Conflict - The local user store user already exists
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -79,12 +79,12 @@ paths:
     get:
       tags:
         - IDP
-      summary: Get built-in IDP user
-      description: Returns the specified built-in IDP user, including group 
membership (empty if none; the example shows a user who already belongs to 
groups).
+      summary: Get local user store user
+      description: Returns the specified local user store user, including 
group membership (empty if none; the example shows a user who already belongs 
to groups).
       operationId: getIdpUser
       responses:
         "200":
-          description: Returns the built-in IDP user object
+          description: Returns the local user store user object
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -95,7 +95,7 @@ paths:
         "403":
           $ref: "#/components/responses/IdpForbiddenErrorResponse"
         "404":
-          description: Not Found - The specified built-in IDP user does not 
exist
+          description: Not Found - The specified local user store user does 
not exist
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -109,8 +109,8 @@ paths:
     put:
       tags:
         - IDP
-      summary: Change built-in IDP user password
-      description: Updates the password of the specified built-in IDP user and 
returns the user object with unchanged group membership.
+      summary: Change local user store user password
+      description: Updates the password of the specified local user store user 
and returns the user object with unchanged group membership.
       operationId: changeIdpUserPassword
       requestBody:
         required: true
@@ -123,7 +123,7 @@ paths:
                 $ref: "#/components/examples/ChangePasswordRequest"
       responses:
         "200":
-          description: Returns the built-in IDP user after the password change
+          description: Returns the local user store user after the password 
change
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -143,7 +143,7 @@ paths:
         "403":
           $ref: "#/components/responses/IdpForbiddenErrorResponse"
         "404":
-          description: Not Found - The specified built-in IDP user does not 
exist
+          description: Not Found - The specified local user store user does 
not exist
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -157,8 +157,8 @@ paths:
     delete:
       tags:
         - IDP
-      summary: Remove built-in IDP user
-      description: Removes the specified built-in IDP user.
+      summary: Remove local user store user
+      description: Removes the specified local user store user.
       operationId: removeIdpUser
       responses:
         "200":
@@ -172,8 +172,8 @@ paths:
     post:
       tags:
         - IDP
-      summary: Add built-in IDP group
-      description: Creates a built-in IDP group with the given name.
+      summary: Add local user store group
+      description: Creates a local user store group with the given name.
       operationId: addIdpGroup
       requestBody:
         required: true
@@ -186,7 +186,7 @@ paths:
                 $ref: "#/components/examples/AddGroupRequest"
       responses:
         "200":
-          description: Returns the added built-in IDP group
+          description: Returns the added local user store group
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -204,7 +204,7 @@ paths:
                 IllegalArgumentException:
                   $ref: 
"#/components/examples/IdpAddGroupIllegalArgumentException"
         "409":
-          description: Conflict - The built-in IDP group already exists
+          description: Conflict - The local user store group already exists
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -224,12 +224,12 @@ paths:
     get:
       tags:
         - IDP
-      summary: Get built-in IDP group
-      description: Returns the specified built-in IDP group, including member 
usernames (empty if none; the example shows a group that already has members).
+      summary: Get local user store group
+      description: Returns the specified local user store group, including 
member usernames (empty if none; the example shows a group that already has 
members).
       operationId: getIdpGroup
       responses:
         "200":
-          description: Returns the built-in IDP group object
+          description: Returns the local user store group object
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -240,7 +240,7 @@ paths:
         "403":
           $ref: "#/components/responses/IdpForbiddenErrorResponse"
         "404":
-          description: Not Found - The specified built-in IDP group does not 
exist
+          description: Not Found - The specified local user store group does 
not exist
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -254,8 +254,8 @@ paths:
     delete:
       tags:
         - IDP
-      summary: Remove built-in IDP group
-      description: Removes the specified built-in IDP group.
+      summary: Remove local user store group
+      description: Removes the specified local user store group.
       operationId: removeIdpGroup
       parameters:
         - $ref: "../openapi.yaml#/components/parameters/force"
@@ -283,8 +283,8 @@ paths:
     put:
       tags:
         - IDP
-      summary: Change built-in IDP group membership
-      description: Adds and/or removes users from the specified built-in IDP 
group.
+      summary: Change local user store group membership
+      description: Adds and/or removes users from the specified local user 
store group.
       operationId: changeIdpGroupMembership
       requestBody:
         required: true
@@ -297,7 +297,7 @@ paths:
                 $ref: "#/components/examples/GroupMembershipChangeRequest"
       responses:
         "200":
-          description: Returns the built-in IDP group after membership changes
+          description: Returns the local user store group after membership 
changes
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -317,7 +317,7 @@ paths:
         "403":
           $ref: "#/components/responses/IdpForbiddenErrorResponse"
         "404":
-          description: Not Found - The specified built-in IDP group or user 
does not exist
+          description: Not Found - The specified local user store group or 
user does not exist
           content:
             application/vnd.gravitino.v1+json:
               schema:
@@ -340,13 +340,13 @@ components:
         name:
           type: string
           maxLength: 128
-          description: The username of the built-in IDP user
+          description: The username of the local user store user
         groups:
           type: array
           items:
             type: string
             maxLength: 128
-          description: The built-in IDP groups the user belongs to
+          description: The local user store groups the user belongs to
 
     IdpGroup:
       type: object
@@ -356,13 +356,13 @@ components:
         name:
           type: string
           maxLength: 128
-          description: The name of the built-in IDP group
+          description: The name of the local user store group
         users:
           type: array
           items:
             type: string
             maxLength: 128
-          description: The usernames of members in the built-in IDP group
+          description: The usernames of members in the local user store group
 
     AddUserRequest:
       type: object
@@ -381,7 +381,7 @@ components:
           format: password
           minLength: 12
           maxLength: 64
-          description: The password of the built-in IDP user to add.
+          description: The password of the local user store user to add.
           writeOnly: true
 
     ChangePasswordRequest:
@@ -394,7 +394,7 @@ components:
           format: password
           minLength: 12
           maxLength: 64
-          description: The new password of the built-in IDP user.
+          description: The new password of the local user store user.
           writeOnly: true
 
     AddGroupRequest:
@@ -423,7 +423,7 @@ components:
             minLength: 1
             maxLength: 128
           minItems: 1
-          description: The usernames to add to the built-in IDP group
+          description: The usernames to add to the local user store group
         usersToRemove:
           type: array
           items:
@@ -431,7 +431,7 @@ components:
             minLength: 1
             maxLength: 128
           minItems: 1
-          description: The usernames to remove from the built-in IDP group
+          description: The usernames to remove from the local user store group
 
     IdpUserResponse:
       type: object
@@ -461,7 +461,7 @@ components:
     IdpForbiddenErrorResponse:
       description:
         Forbidden - Only callers listed in 
`gravitino.authorization.serviceAdmins` may
-        manage built-in IDP users and groups. Unauthenticated requests and 
authenticated
+        manage local user store users and groups. Unauthenticated requests and 
authenticated
         non-service-admin users receive this response.
       content:
         application/vnd.gravitino.v1+json:
@@ -552,89 +552,89 @@ components:
       value: {
         "code": 1001,
         "type": "IllegalArgumentException",
-        "message": "Failed to operate built-in IdP user [] operation [ADD], 
reason [\"user\" field is required and cannot be empty]"
+        "message": "Failed to operate local user store user [] operation 
[ADD], reason [\"user\" field is required and cannot be empty]"
       }
 
     IdpChangePasswordIllegalArgumentException:
       value: {
         "code": 1001,
         "type": "IllegalArgumentException",
-        "message": "Failed to operate built-in IdP user [alice] operation 
[UPDATE], reason [\"password\" field is required and cannot be empty]"
+        "message": "Failed to operate local user store user [alice] operation 
[UPDATE], reason [\"password\" field is required and cannot be empty]"
       }
 
     IdpAddGroupIllegalArgumentException:
       value: {
         "code": 1001,
         "type": "IllegalArgumentException",
-        "message": "Failed to operate built-in IdP group [] operation [ADD], 
reason [\"group\" field is required and cannot be empty]"
+        "message": "Failed to operate local user store group [] operation 
[ADD], reason [\"group\" field is required and cannot be empty]"
       }
 
     IdpGroupMembershipIllegalArgumentException:
       value: {
         "code": 1001,
         "type": "IllegalArgumentException",
-        "message": "Failed to operate built-in IdP group [engineers] operation 
[UPDATE], reason [usersToAdd and usersToRemove cannot both be null]"
+        "message": "Failed to operate local user store group [engineers] 
operation [UPDATE], reason [usersToAdd and usersToRemove cannot both be null]"
       }
 
     IdpNotFoundException:
       value: {
         "code": 1003,
         "type": "NotFoundException",
-        "message": "Failed to operate built-in IdP user [missing-user] 
operation [GET], reason [IdP user not found: missing-user]"
+        "message": "Failed to operate local user store user [missing-user] 
operation [GET], reason [IdP user not found: missing-user]"
       }
 
     IdpAlreadyExistsException:
       value: {
         "code": 1004,
         "type": "AlreadyExistsException",
-        "message": "Failed to operate built-in IdP user [alice] operation 
[ADD], reason [IdP user alice already exists]"
+        "message": "Failed to operate local user store user [alice] operation 
[ADD], reason [IdP user alice already exists]"
       }
 
     IdpUserUpdateNotFoundException:
       value: {
         "code": 1003,
         "type": "NotFoundException",
-        "message": "Failed to operate built-in IdP user [missing-user] 
operation [UPDATE], reason [IdP user not found: missing-user]"
+        "message": "Failed to operate local user store user [missing-user] 
operation [UPDATE], reason [IdP user not found: missing-user]"
       }
 
     IdpGroupNotFoundException:
       value: {
         "code": 1003,
         "type": "NotFoundException",
-        "message": "Failed to operate built-in IdP group [missing-group] 
operation [GET], reason [IdP group not found: missing-group]"
+        "message": "Failed to operate local user store group [missing-group] 
operation [GET], reason [IdP group not found: missing-group]"
       }
 
     IdpGroupUpdateNotFoundException:
       value: {
         "code": 1003,
         "type": "NotFoundException",
-        "message": "Failed to operate built-in IdP group [missing-group] 
operation [UPDATE], reason [IdP group not found: missing-group]"
+        "message": "Failed to operate local user store group [missing-group] 
operation [UPDATE], reason [IdP group not found: missing-group]"
       }
 
     IdpMembershipUserNotFoundException:
       value: {
         "code": 1003,
         "type": "NotFoundException",
-        "message": "Failed to operate built-in IdP group [engineers] operation 
[UPDATE], reason [IdP user not found: missing-user]"
+        "message": "Failed to operate local user store group [engineers] 
operation [UPDATE], reason [IdP user not found: missing-user]"
       }
 
     IdpGroupAlreadyExistsException:
       value: {
         "code": 1004,
         "type": "AlreadyExistsException",
-        "message": "Failed to operate built-in IdP group [engineers] operation 
[ADD], reason [IdP group engineers already exists]"
+        "message": "Failed to operate local user store group [engineers] 
operation [ADD], reason [IdP group engineers already exists]"
       }
 
     IdpGroupNotEmptyException:
       value: {
         "code": 1006,
         "type": "IllegalStateException",
-        "message": "Failed to operate built-in IdP group [engineers] operation 
[REMOVE], reason [IdP group engineers is not empty, use force=true to delete 
it]"
+        "message": "Failed to operate local user store group [engineers] 
operation [REMOVE], reason [IdP group engineers is not empty, use force=true to 
delete it]"
       }
 
     IdpForbiddenException:
       value: {
         "code": 1008,
         "type": "ForbiddenException",
-        "message": "Only service admins can manage built-in IdP users and 
groups."
+        "message": "Only service admins can manage local user store users and 
groups."
       }
diff --git a/docs/open-api/idp/openapi.yaml b/docs/open-api/idp/openapi.yaml
index d8271fab8d..263c50c7fa 100644
--- a/docs/open-api/idp/openapi.yaml
+++ b/docs/open-api/idp/openapi.yaml
@@ -18,17 +18,17 @@
 ---
 openapi: 3.0.3
 info:
-  title: Gravitino Built-in IDP REST API
+  title: Gravitino Local Users and Groups REST API
   license:
     name: Apache 2.0
     url: https://www.apache.org/licenses/LICENSE-2.0.html
   version: 1.3.0-SNAPSHOT
   description: |
-    OpenAPI specification for built-in IDP user and group management APIs 
exposed
+    OpenAPI specification for local user store user and group management APIs 
exposed
     by the `idp-basic` plugin. Clients authenticate with Basic credentials 
validated against
-    built-in IdP user metadata. Enable built-in IdP by registering
+    local user metadata. Enable local user store by registering
     `org.apache.gravitino.idp.web.rest.feature` in 
`gravitino.server.rest.extensionPackages`.
-    Built-in IdP is incompatible with the `simple` authenticator (the default),
+    Local user store is incompatible with the `simple` authenticator (the 
default),
     `gravitino.authenticators` must include `basic` and must not include 
`simple`.
 
 servers:
diff --git a/docs/security/access-control.md b/docs/security/access-control.md
index 538a80abba..bbc8c41d0c 100755
--- a/docs/security/access-control.md
+++ b/docs/security/access-control.md
@@ -587,5 +587,4 @@ client.setOwner(schema, "analyst", Owner.Type.USER);
 - [Authorization Pushdown](authorization-pushdown.md), for pushing enforcement 
down to the underlying
   data source or to an external system such as Apache Ranger
 - [How to Authenticate](how-to-authenticate.md), for establishing who the 
caller is
-- [How to Use the Built-in IdP](how-to-use-built-in-idp.md)
-- [Security](security.md)
+- [Local users and groups](local-users-and-groups.md)
diff --git a/docs/security/authorization-pushdown.md 
b/docs/security/authorization-pushdown.md
index 0bc406c1d0..dea56c5d03 100644
--- a/docs/security/authorization-pushdown.md
+++ b/docs/security/authorization-pushdown.md
@@ -1,99 +1,98 @@
 ---
 title: "Authorization Pushdown"
 slug: "/security/authorization-pushdown"
-keyword: "security"
+keywords:
+  - security
+  - authorization
+  - ranger
 license: "This software is licensed under the Apache License version 2."
 ---
 
-## Introduction
+## Overview
 
-![authorization pushdown](../assets/security/authorization-pushdown.png)
+Authorization pushdown applies a grant made in Gravitino to Apache Ranger, so 
the permission is enforced where the data is rather than only inside Gravitino. 
An engine that reads the table directly is still subject to it.
 
-Gravitino offers a set of authorization frameworks that integrate with various 
underlying data source permission systems, such as MySQL's native permission 
management and Apache Ranger for big data. These frameworks align with 
Gravitino's own authorization model and methodology.
-Gravitino manages different data sources through Catalogs, and when a user 
performs an authorization operation on data within a Catalog, Gravitino invokes 
the Authorization Plugin module for that Catalog.
-This module translates Gravitino's authorization model into the permission 
rules of the underlying data source. The permissions are then enforced by the 
underlying permission system via the respective client, such as JDBC or the 
Apache Ranger client.
+Pushdown is configured per catalog with the `authorization-provider` property. 
Set it to `ranger` when the catalog's permissions live in a single Ranger 
service, or to `chain` when one catalog needs its grants applied in more than 
one. A catalog without the property manages access in Gravitino alone.
 
-### Ranger Hadoop SQL Plugin
+Gravitino resolves which catalog holds the object being granted on, hands the 
operation to that catalog's plugin, and the plugin maps the Gravitino privilege 
onto Ranger's model and writes it through the Ranger admin REST API. The plugin 
interface is not specific to Ranger, so other permission systems can be added, 
and Ranger is what ships today.
 
-To use the Ranger Hadoop SQL Plugin, you need to configure the following 
properties:
+Once a catalog is configured, grants are made through the ordinary 
[authorization REST 
API](https://gravitino.apache.org/docs/latest/api/rest/grant-role-to-user). No 
separate pushdown call exists.
 
-| Property Name                                         | Description          
                                                                                
                                                | Default Value                 
   | Required |
-|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------|----------|
-| `authorization-provider`                              | Providers to use to 
implement authorization plugin such as `ranger`.                                
                                                 | (none)                       
    | No       |
-| `authorization.ranger.admin.url`                      | The Apache Ranger 
web URIs.                                                                       
                                                   | (none)                     
      | No       |
-| `authorization.ranger.service.type`                   | The Apache Ranger 
service type. Supports `HadoopSQL` or `HDFS`.                                   
                                                   | (none)                     
      | No       |
-| `authorization.ranger.auth.type`                      | The Apache Ranger 
authentication type `simple` or `kerberos`.                                     
                                                   | `simple`                   
      | No       |
-| `authorization.ranger.username`                       | The Apache Ranger 
admin web login username (auth type=simple), or kerberos principal(auth 
type=kerberos), Need have Ranger administrator permission. | (none)             
              | No       |
-| `authorization.ranger.password`                       | The Apache Ranger 
admin web login user password (auth type=simple), or path of the keytab 
file(auth type=kerberos)                                   | (none)             
              | No       |
-| `authorization.ranger.service.name`                   | The Apache Ranger 
service name.                                                                   
                                                   | (none)                     
      | No       |
-| `authorization.ranger.service.create-if-absent`       | If this property is 
true and the Ranger service doesn't exist, Gravitino will create a Ranger 
service                                                | false                  
          | No       |
-| `authorization.ranger.jdbc.driverClassName`           | The property is used 
to specify driver class name when creating Ranger HadoopSQL service             
                                                | 
`org.apache.hive.jdbc.HiveDrive` | No       |
-| `authorization.ranger.jdbc.url`                       | The property is used 
to specify jdbc url when creating Ranger HadoopSQL service                      
                                                | `jdbc:hive2://127.0.0.1:8081` 
   | No       |
-| `authorization.ranger.hadoop.security.authentication` | The property is used 
to specify Hadoop security authentication when creating Ranger HDFS service     
                                                | `simple`                      
   | No       |
-| `authorization.ranger.hadoop.rpc.protection`          | The property is used 
to specify Hadoop rpc protection when creating Ranger HDFS service              
                                                | `authentication`              
   | No       |
-| `authorization.ranger.fs.default.name`                | The property is used 
to specify default filesystem when creating Ranger HDFS service                 
                                                | `hdfs://127.0.0.1:8090`       
   | No       |
+## Ranger
 
-:::caution
-The Gravitino Ranger authorization plugin only supports the Apache Ranger 
HadoopSQL Plugin and Apache Ranger HDFS Plugin.
-:::
+The Ranger provider covers two Ranger service types. `HadoopSQL` governs 
schemas, tables, and columns for the Hive, Iceberg, and Paimon catalogs. `HDFS` 
governs paths, which is what a fileset catalog needs. A catalog selects one of 
them with `authorization.ranger.service.type`.
 
-Once you have used the correct configuration, you can perform authorization 
operations by calling Gravitino [authorization RESTful 
API](https://gravitino.apache.org/docs/latest/api/rest/grant-role-to-user).
+Spark reaches these catalogs through the Kyuubi authorization plugin. That 
plugin cannot push updates or deletes for a Paimon catalog.
 
-Gravitino will initially create three roles in Apache Ranger:
+### Configuration
 
-- GRAVITINO_METALAKE_OWNER_ROLE: Includes users and user groups designated as 
metalake owners, corresponding to the owner's privileges in Ranger policies.
-- GRAVITINO_CATALOG_OWNER_ROLE: Includes users and user groups designated as 
catalog owners, corresponding to the owner's privileges in Ranger policies.
-- GRAVITINO_OWNER_ROLE: Used to label Ranger policy items related to schema 
and table owner privileges. It does not include any users or user groups.
+| Property Name                                         | Description          
                                                                                
          | Default Value                     |
+|-------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------|
+| `authorization-provider`                              | Set to `ranger` to 
push grants into Apache Ranger                                                  
            | (none)                            |
+| `authorization.ranger.admin.url`                      | The Ranger admin web 
URI                                                                             
          | (none)                            |
+| `authorization.ranger.service.type`                   | `HadoopSQL` or 
`HDFS`                                                                          
                | (none)                            |
+| `authorization.ranger.service.name`                   | The Ranger service 
to write policies into                                                          
            | (none)                            |
+| `authorization.ranger.auth.type`                      | `simple` or 
`kerberos`                                                                      
                   | `simple`                          |
+| `authorization.ranger.username`                       | Ranger admin login 
username, or the Kerberos principal. Requires Ranger administrator permission   
            | (none)                            |
+| `authorization.ranger.password`                       | Ranger admin login 
password, or the path to the keytab file                                        
            | (none)                            |
+| `authorization.ranger.service.create-if-absent`       | Creates the Ranger 
service when it does not already exist                                          
            | `false`                           |
 
-#### Configure the Ranger Hadoop SQL Plugin
+The remaining properties apply only when `create-if-absent` is `true`, since 
they describe the service Gravitino creates.
 
-Suppose you have an Apache Hive service in your datacenter and have created a 
`hiveRepo` in Apache Ranger to manage its permissions.
-The Ranger service is accessible at `172.0.0.100:6080`, with the username 
`Jack` and the password `PWD123`.
-To add this Hive service to Gravitino using the Hive catalog, you'll need to 
configure the following parameters.
+| Property Name                                         | Description          
                                                    | Default Value             
        |
+|-------------------------------------------------------|--------------------------------------------------------------------------|-----------------------------------|
+| `authorization.ranger.jdbc.driverClassName`           | Driver class for a 
new HadoopSQL service                                 | 
`org.apache.hive.jdbc.HiveDriver` |
+| `authorization.ranger.jdbc.url`                       | JDBC URL for a new 
HadoopSQL service                                     | 
`jdbc:hive2://127.0.0.1:8081`     |
+| `authorization.ranger.hadoop.security.authentication` | Hadoop security 
authentication for a new HDFS service                    | `simple`             
             |
+| `authorization.ranger.hadoop.security.authorization`  | Hadoop security 
authorization for a new HDFS service                     | (none)               
             |
+| `authorization.ranger.hadoop.rpc.protection`          | Hadoop RPC 
protection for a new HDFS service                             | 
`authentication`                  |
+| `authorization.ranger.fs.default.name`                | Default filesystem 
for a new HDFS service                                | `hdfs://127.0.0.1:8090` 
          |
+
+### Example
+
+A Hive service is already managed by a Ranger service named `hiveRepo`, and 
Ranger is reachable at `172.0.0.100:6080`. Adding that Hive service to 
Gravitino as a Hive catalog with pushdown enabled takes the following catalog 
properties.
 
 ```properties
 authorization-provider=ranger
 authorization.ranger.admin.url=172.0.0.100:6080
 authorization.ranger.auth.type=simple
-authorization.ranger.username=Jack
-authorization.ranger.password=PWD123
+authorization.ranger.username={ranger_admin_user}
+authorization.ranger.password={ranger_admin_password}
 authorization.ranger.service.type=HadoopSQL
 authorization.ranger.service.name=hiveRepo
 ```
 
-:::caution
-Gravitino supports authorization pushdown to Apache Ranger for the Hive, 
Iceberg, and Paimon catalogs through the Ranger Hadoop SQL Plugin.
-Spark can use Kyuubi authorization plugin to access Gravitino's catalog. But 
the plugin can't support to update or delete data for Paimon catalog.
-More data source authorization is under development.
-:::
+### Roles Gravitino Creates
+
+Gravitino creates three roles in Ranger and manages their membership itself, 
so treat them as owned by Gravitino rather than editing them in the Ranger UI.
+
+| Role                            | Purpose                                    
                                                      |
+|---------------------------------|---------------------------------------------------------------------------------------------------|
+| `GRAVITINO_METALAKE_OWNER_ROLE` | Holds the users and groups that own the 
metalake, carrying owner privileges in Ranger policies    |
+| `GRAVITINO_CATALOG_OWNER_ROLE`  | Holds the users and groups that own the 
catalog, carrying owner privileges in Ranger policies     |
+| `GRAVITINO_OWNER_ROLE`          | Labels the policy items covering schema 
and table owner privileges, and holds no members          |
 
-### Chain Authorization Plugin
+## Chaining Plugins
 
-Gravitino supports chaining multiple authorization plugins to secure one 
catalog.
-The authorization plugin chain is defined in the `authorization.chain.plugins` 
property, with the plugin names separated by commas.
-When a user performs an authorization operation on data within a catalog, the 
chained plugin will apply the authorization rules for every plugin defined in 
the chain.
+One catalog often needs permissions applied in more than one place. A Hive 
catalog storing its data on HDFS needs both the table grant in the HadoopSQL 
service and the corresponding path grant in the HDFS service, or an engine 
reading the files directly bypasses the table permission.
 
-To use the chained authorization plugin, you need to configure the following 
properties:
+The `chain` provider handles this. Set `authorization.chain.plugins` to a 
comma-separated list of names you choose, then configure each named plugin with 
`authorization.chain.{plugin_name}` as its property prefix. Every plugin in the 
chain is applied on each authorization operation.
 
-| Property Name                                            | Description       
                                                                   | Default 
Value | Required                    |
-|----------------------------------------------------------|--------------------------------------------------------------------------------------|---------------|-----------------------------|
-| `authorization-provider`                                 | Providers to use 
to implement authorization plugin such as `chain`                   | (none)    
    | No                          |
-| `authorization.chain.plugins`                            | The 
comma-separated list of plugin names, like 
`${plugin-name1},${plugin-name2},...` | (none)        | Yes if you use chain 
plugin |
-| `authorization.chain.${plugin-name}.ranger.admin.url`    | The Ranger 
authorization plugin properties of the `${plugin-name}`                   | 
(none)        | Yes if you use chain plugin |
-| `authorization.chain.${plugin-name}.ranger.service.type` | The Ranger 
authorization plugin properties of the `${plugin-name}`                   | 
(none)        | Yes if you use chain plugin |
-| `authorization.chain.${plugin-name}.ranger.service.name` | The Ranger 
authorization plugin properties of the `${plugin-name}`                   | 
(none)        | Yes if you use chain plugin |
-| `authorization.chain.${plugin-name}.ranger.username`     | The Ranger 
authorization plugin properties of the `${plugin-name}`                   | 
(none)        | Yes if you use chain plugin |
-| `authorization.chain.${plugin-name}.ranger.password`     | The Ranger 
authorization plugin properties of the `${plugin-name}`                   | 
(none)        | Yes if you use chain plugin |
+| Property Name                                            | Description       
                                             |
+|----------------------------------------------------------|-----------------------------------------------------------------|
+| `authorization-provider`                                 | Set to `chain` to 
apply several plugins to this catalog         |
+| `authorization.chain.plugins`                            | Comma-separated 
plugin names, each naming a prefix below        |
+| `authorization.chain.{plugin_name}.ranger.admin.url`     | The admin URI for 
that plugin                                   |
+| `authorization.chain.{plugin_name}.ranger.service.type`  | `HadoopSQL` or 
`HDFS` for that plugin                           |
+| `authorization.chain.{plugin_name}.ranger.service.name`  | The Ranger 
service for that plugin                              |
+| `authorization.chain.{plugin_name}.ranger.username`      | The Ranger admin 
login username for that plugin                 |
+| `authorization.chain.{plugin_name}.ranger.password`      | The Ranger admin 
login password for that plugin                 |
 
-:::caution
-The Gravitino chain authorization plugin only supports the Apache Ranger 
HadoopSQL Plugin and Apache Ranger HDFS Plugin.
-The properties of every chained authorization plugin should use 
`authorization.chain.${plugin-name}` as the prefix.
-:::
+The names in `authorization.chain.plugins` are labels rather than plugin 
types, so any name works as long as it matches the prefix used by its 
properties. Every plugin in a chain is a Ranger plugin, since Ranger is the 
only provider available to chain.
 
-#### Configure the Chain Authorization Plugin
+### Example
 
-Suppose you have an Apache Hive service in your datacenter and have created a 
`hiveRepo` in Apache Ranger to manage its permissions.
-The Apache Hive service will use HDFS to store its data. You have created a 
`hdfsRepo` in Apache Ranger to manage HDFS's permissions.
+The Hive service is managed by the Ranger service `hiveRepo` and its 
underlying HDFS storage by `hdfsRepo`. Chaining the two keeps the table grant 
and the path grant in step.
 
 ```properties
 authorization-provider=chain
@@ -102,12 +101,17 @@ 
authorization.chain.hive.ranger.admin.url=http://ranger-service:6080
 authorization.chain.hive.ranger.service.type=HadoopSQL
 authorization.chain.hive.ranger.service.name=hiveRepo
 authorization.chain.hive.ranger.auth.type=simple
-authorization.chain.hive.ranger.username=Jack
-authorization.chain.hive.ranger.password=PWD123
+authorization.chain.hive.ranger.username={ranger_admin_user}
+authorization.chain.hive.ranger.password={ranger_admin_password}
 authorization.chain.hdfs.ranger.admin.url=http://ranger-service:6080
 authorization.chain.hdfs.ranger.service.type=HDFS
 authorization.chain.hdfs.ranger.service.name=hdfsRepo
 authorization.chain.hdfs.ranger.auth.type=simple
-authorization.chain.hdfs.ranger.username=Jack
-authorization.chain.hdfs.ranger.password=PWD123
-```
\ No newline at end of file
+authorization.chain.hdfs.ranger.username={ranger_admin_user}
+authorization.chain.hdfs.ranger.password={ranger_admin_password}
+```
+
+## Further Reading
+
+- [Access Control](access-control.md) for the Gravitino privilege model that 
pushdown translates from
+- [Authorization REST 
API](https://gravitino.apache.org/docs/latest/api/rest/grant-role-to-user) for 
making the grants
diff --git a/docs/security/how-to-authenticate.md 
b/docs/security/how-to-authenticate.md
index c3cbfccdff..28b273a875 100644
--- a/docs/security/how-to-authenticate.md
+++ b/docs/security/how-to-authenticate.md
@@ -45,7 +45,7 @@ curl -v -X GET \
 
 ### Basic Mode
 
-In Basic mode, Gravitino verifies HTTP Basic credentials against built-in IDP 
user metadata stored
+In Basic mode, Gravitino verifies HTTP Basic credentials against local user 
metadata stored
 in the relational entity store.
 
 To enable Basic mode:
@@ -53,14 +53,14 @@ To enable Basic mode:
 - Set `gravitino.authenticators` to `basic`.
 - Set `gravitino.server.rest.extensionPackages` to 
`org.apache.gravitino.idp.web.rest.feature`.
 - Set `gravitino.authorization.serviceAdmins` to the service admin usernames 
that should exist in
-  the built-in IDP.
+  the local user store.
 
-Built-in IdP is **incompatible** with the `simple` authenticator (the default),
+Local user store is **incompatible** with the `simple` authenticator (the 
default),
 `gravitino.authenticators` must include `basic` and must not include `simple`.
 - On the first startup, if any configured service admin does not yet have a 
password, set the
   `GRAVITINO_INITIAL_ADMIN_PASSWORD` environment variable to the initial 
password (12 to 64
   characters) before starting Gravitino. The same password is applied to every 
configured service
-  admin that does not yet exist in the built-in IDP.
+  admin that does not yet exist in the local user store.
 
 For the client side, enable Basic mode with the following code:
 
@@ -91,8 +91,8 @@ curl -v -X GET \
 ```
 
 The Web UI uses the first entry in `gravitino.authenticators` from `/configs`. 
When it is `basic`,
-the login page shows a username and password form backed by built-in IdP user 
metadata. See
-[built-in IDP Web UI](how-to-use-built-in-idp.md#web-ui).
+the login page shows a username and password form backed by local user 
metadata. See
+[Local users and groups](local-users-and-groups.md).
 
 ### OAuth Mode
 
@@ -383,7 +383,7 @@ This example shows how to enable built-in Basic 
authentication.
 
 - Gravitino distribution package (includes the idp-basic plugin on the server 
classpath)
 
-Built-in IdP is **incompatible** with the `simple` authenticator (the default),
+Local user store is **incompatible** with the `simple` authenticator (the 
default),
 `gravitino.authenticators` must include `basic` and must not include `simple`.
 
 **Configuration:**
diff --git a/docs/security/how-to-use-built-in-idp.md 
b/docs/security/how-to-use-built-in-idp.md
deleted file mode 100644
index 9eb2e64b6a..0000000000
--- a/docs/security/how-to-use-built-in-idp.md
+++ /dev/null
@@ -1,530 +0,0 @@
----
-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).
-
-## Web UI
-
-When built-in IdP Basic authentication is enabled, the Web UI exposes a 
username and password login
-form. Configure **both** of the following in `gravitino.conf`:
-
-```properties
-gravitino.authenticators = basic
-gravitino.server.rest.extensionPackages = 
org.apache.gravitino.idp.web.rest.feature
-```
-
-The Web UI reads `gravitino.authenticators` from `/configs` and uses the first 
entry as the active
-authentication type. List `basic` first when you want the built-in IdP login 
form. Built-in IdP is
-**incompatible** with the `simple` authenticator (the default). When IdP is 
enabled, do not include
-`simple` in `gravitino.authenticators`.
-
----
-
-## Prerequisites
-
-Before you call `/api/idp/*`, ensure the following:
-
-1. **IDP REST API registration** — In `gravitino.conf`, set:
-
-   ```properties
-   gravitino.authenticators = basic
-   gravitino.server.rest.extensionPackages = 
org.apache.gravitino.idp.web.rest.feature
-   ```
-
-2. **Server authenticator** — Built-in IdP is **incompatible** with the 
`simple` authenticator
-   (the default). Do not list `simple` together with `basic` in 
`gravitino.authenticators`.
-
-3. **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.authenticators`                | Must include `basic` when the 
built-in IdP plugin is enabled             | `basic`                            
         |
-| `gravitino.server.rest.extensionPackages` | Registers built-in IdP REST APIs 
                                        | 
`org.apache.gravitino.idp.web.rest.feature` |
-| `gravitino.authorization.serviceAdmins`   | Comma-separated service admin 
that can call built-in IDP management APIs | `admin`                            
         |
-
-Example:
-
-```properties
-gravitino.authenticators = basic
-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).
-
----
-
-## Engines using Basic authentication
-
-Configure Basic credentials on each engine connector. See
-[Spark 
authentication](../spark-connector/spark-authentication-with-gravitino.md),
-[Flink 
authentication](../flink-connector/flink-authentication-with-gravitino.md), and
-[Trino authentication](../trino-connector/authentication.md) for full 
connector setup.
-
-### Spark
-
-```properties
-spark.sql.gravitino.authType=basic
-spark.sql.gravitino.basic.username=${username}
-spark.sql.gravitino.basic.password=${password}
-```
-
-### Flink
-
-```yaml
-table.catalog-store.gravitino.gravitino.client.auth.type: basic
-table.catalog-store.gravitino.gravitino.client.basic.username: ${username}
-table.catalog-store.gravitino.gravitino.client.basic.password: ${password}
-```
-
-### Trino
-
-In `etc/catalog/gravitino.properties`:
-
-```properties
-gravitino.client.authType=basic
-gravitino.client.basic.username=${username}
-gravitino.client.basic.password=${password}
-```
-
----
-
-## Engines using Basic authentication for Iceberg REST catalog
-
-Connect Spark, Flink, and Trino directly to the Gravitino Iceberg REST (IRC) 
endpoint at
-`http://<gravitino-host>:9001/iceberg/`. No Gravitino engine connector plugin 
is required.
-Configure only the Basic auth properties below. See
-[Connect Spark via Iceberg REST](../iceberg-rest-engine/spark.md),
-[Connect Flink via Iceberg REST](../iceberg-rest-engine/flink.md), and
-[Connect Trino via Iceberg REST](../iceberg-rest-engine/trino.md) for full IRC 
setup.
-
-### Spark
-
-```properties
-spark.sql.catalog.<catalog-name>.rest.auth.type=basic
-spark.sql.catalog.<catalog-name>.rest.auth.basic.username=${username}
-spark.sql.catalog.<catalog-name>.rest.auth.basic.password=${password}
-```
-
-### Flink
-
-```sql
-'rest.auth.type' = 'basic',
-'rest.auth.basic.username' = '${username}',
-'rest.auth.basic.password' = '${password}'
-```
-
-### Trino
-
-Requires Trino **481+**. Trino has no native Basic mode for Iceberg REST; pass
-`Authorization` via HTTP headers.
-
-Generate Base64 once:
-
-```shell
-echo -n '${username}:${password}' | base64
-```
-
-In `etc/catalog/<catalog-name>.properties`:
-
-```properties
-iceberg.rest-catalog.http-headers=Authorization: Basic <base64-credentials>
-```
-
-Replace `<base64-credentials>` with the output of `echo -n 
'${username}:${password}' | base64`.
-
----
-
-## End-to-end setup
-
-The following steps provision Gravitino and engines so a named user can 
connect with Basic
-credentials. The examples use service admin `admin` / `Passw0rd-Admin12`, 
engine user
-`alice` / `Passw0rd-Alice12`, metalake `example`, and Gravitino at 
`http://localhost:8090`.
-Replace these with values that match your deployment.
-
-### 1. Admin initialization
-
-1. Append the following to `gravitino.conf` (see also 
[Prerequisites](#prerequisites) and
-   [Configuration](#configuration)):
-
-   ```properties
-   gravitino.authenticators = basic
-   gravitino.server.rest.extensionPackages = 
org.apache.gravitino.idp.web.rest.feature
-   gravitino.authorization.enable = true
-   gravitino.authorization.serviceAdmins = admin
-   ```
-
-   Built-in IdP is **incompatible** with the `simple` authenticator (the 
default),
-   `gravitino.authenticators` must include `basic` and must not include 
`simple`.
-
-2. Before the first start, set the initial service admin password (see
-   [password rules](#password-and-username-rules)):
-
-   ```shell
-   export GRAVITINO_INITIAL_ADMIN_PASSWORD='Passw0rd-Admin12'
-   ```
-
-3. Start Gravitino and verify the service admin can call the API:
-
-   ```shell
-   curl -s -H "Accept: application/vnd.gravitino.v1+json" \
-     -H "Authorization: Basic $(echo -n 'admin:Passw0rd-Admin12' | base64)" \
-     http://localhost:8090/api/version
-   ```
-
-### 2. Create users
-
-Create a built-in IdP user for each engine or operator account. Service admins 
call
-`/api/idp/users` (see [Add a user](#add-a-user)):
-
-```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-Alice12"}' \
-  http://localhost:8090/api/idp/users
-```
-
-### 3. Create a metalake
-
-Create a metalake with a service admin account. When authorization is enabled, 
only service admins
-can create metalakes (see [Access control](access-control.md)). See also
-[Manage metalakes](../manage-metalake-using-gravitino.md#create-a-metalake).
-
-```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 '{"name":"example","comment":"Basic auth example","properties":{}}' \
-  http://localhost:8090/api/metalakes
-```
-
-Create catalogs in this metalake before engines can query data. See
-[Manage relational metadata](../manage-relational-metadata-using-gravitino.md).
-
-### 4. Add users to the metalake
-
-Register the engine user in the metalake authorization namespace (see
-[Add a user](access-control.md#add-a-user)). The username must match a 
built-in IdP user created in
-step 2:
-
-```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 '{"name":"alice"}' \
-  http://localhost:8090/api/metalakes/example/users
-```
-
-Grant roles or ownership as needed so the user can access catalogs and 
metadata. See
-[Access control](access-control.md).
-
-### 5. Configure engines to use Basic authentication
-
-Configure each engine with `http://localhost:8090`, metalake `example`, and 
user `alice` /
-`Passw0rd-Alice12` from step 2. Deploy the Gravitino connector for each engine 
first; see
-[Spark 
authentication](../spark-connector/spark-authentication-with-gravitino.md),
-[Flink 
authentication](../flink-connector/flink-authentication-with-gravitino.md), and
-[Trino authentication](../trino-connector/authentication.md).
-
-#### Spark
-
-```shell
-$SPARK_HOME/bin/spark-sql \
-  --conf 
spark.plugins=org.apache.gravitino.spark.connector.plugin.GravitinoSparkPlugin \
-  --conf spark.sql.gravitino.uri=http://localhost:8090 \
-  --conf spark.sql.gravitino.metalake=example \
-  --conf spark.sql.gravitino.authType=basic \
-  --conf spark.sql.gravitino.basic.username=alice \
-  --conf spark.sql.gravitino.basic.password=Passw0rd-Alice12 \
-  -e "SHOW CATALOGS;"
-```
-
-#### Flink
-
-```shell
-$FLINK_HOME/bin/sql-client.sh \
-  -D table.catalog-store.kind=gravitino \
-  -D table.catalog-store.gravitino.gravitino.uri=http://localhost:8090 \
-  -D table.catalog-store.gravitino.gravitino.metalake=example \
-  -D table.catalog-store.gravitino.gravitino.client.auth.type=basic \
-  -D table.catalog-store.gravitino.gravitino.client.basic.username=alice \
-  -D 
table.catalog-store.gravitino.gravitino.client.basic.password=Passw0rd-Alice12
-```
-
-#### Trino
-
-Create `etc/catalog/gravitino.properties`:
-
-```properties
-connector.name=gravitino
-gravitino.uri=http://localhost:8090
-gravitino.metalake=example
-gravitino.client.authType=basic
-gravitino.client.basic.username=alice
-gravitino.client.basic.password=Passw0rd-Alice12
-```
-
-Set `catalog.management=dynamic` in `etc/config.properties`, restart Trino, 
then verify:
-
-```shell
-$TRINO_HOME/bin/launcher restart
-java -jar trino-cli.jar --server http://localhost:8080 --user alice \
-  --execute "SHOW CATALOGS"
-```
-
-### 6. Access Iceberg REST catalog with Basic authentication
-
-Connect engines directly to `http://localhost:9001/iceberg`. No Gravitino 
connector plugin is
-required. Use user `alice` / `Passw0rd-Alice12` from step 2. See
-[Connect Spark via Iceberg REST](../iceberg-rest-engine/spark.md),
-[Connect Flink via Iceberg REST](../iceberg-rest-engine/flink.md), and
-[Connect Trino via Iceberg REST](../iceberg-rest-engine/trino.md) for full IRC 
setup.
-
-#### Spark
-
-```shell
-$SPARK_HOME/bin/spark-sql \
-  --conf 
spark.sql.extensions=org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions
 \
-  --conf spark.sql.catalog.gravitino_irc=org.apache.iceberg.spark.SparkCatalog 
\
-  --conf spark.sql.catalog.gravitino_irc.type=rest \
-  --conf spark.sql.catalog.gravitino_irc.uri=http://localhost:9001/iceberg \
-  --conf spark.sql.catalog.gravitino_irc.rest.auth.type=basic \
-  --conf spark.sql.catalog.gravitino_irc.rest.auth.basic.username=alice \
-  --conf 
spark.sql.catalog.gravitino_irc.rest.auth.basic.password=Passw0rd-Alice12 \
-  -e "SHOW NAMESPACES IN gravitino_irc;"
-```
-
-#### Flink
-
-```shell
-cat > /tmp/flink-irc.sql <<'EOF'
-CREATE CATALOG gravitino_irc WITH (
-  'type'                     = 'iceberg',
-  'catalog-type'             = 'rest',
-  'uri'                      = 'http://localhost:9001/iceberg',
-  'rest.auth.type'           = 'basic',
-  'rest.auth.basic.username' = 'alice',
-  'rest.auth.basic.password' = 'Passw0rd-Alice12'
-);
-USE CATALOG gravitino_irc;
-SHOW DATABASES;
-EOF
-
-$FLINK_HOME/bin/sql-client.sh -f /tmp/flink-irc.sql
-```
-
-#### Trino
-
-Requires Trino **481+**. Trino has no native Basic mode for Iceberg REST; pass 
`Authorization`
-via HTTP headers.
-
-```shell
-echo -n 'alice:Passw0rd-Alice12' | base64
-```
-
-Create `etc/catalog/gravitino_irc.properties`:
-
-```properties
-connector.name=iceberg
-iceberg.catalog.type=rest
-iceberg.rest-catalog.uri=http://localhost:9001/iceberg
-iceberg.rest-catalog.http-headers=Authorization: Basic 
YWxpc2U6UGFzc3cwcmQtQWxpY2UxMg==
-```
-
-Restart Trino and verify:
-
-```shell
-$TRINO_HOME/bin/launcher restart
-java -jar trino-cli.jar --server http://localhost:8080 --user alice \
-  --execute "SHOW SCHEMAS FROM gravitino_irc"
-```
-
----
-
-## 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/how-to-use-cors.md b/docs/security/how-to-use-cors.md
index 724228989c..7436affba1 100644
--- a/docs/security/how-to-use-cors.md
+++ b/docs/security/how-to-use-cors.md
@@ -1,38 +1,48 @@
 ---
 title: "CORS"
 slug: "/security/how-to-use-cors"
-keyword: "security cors"
+keywords:
+  - security
+  - cors
 license: "This software is licensed under the Apache License version 2."
 ---
 
-## Introduction
-
-Cross-Origin Resource Sharing (CORS) is a browser security mechanism that 
controls which web origins can call the Gravitino HTTP API. Without CORS 
configuration, browsers block requests from a web UI hosted on a different 
origin than the Gravitino server. Configure CORS when the Gravitino web UI or 
any other browser-based client runs on a different host, port, or protocol than 
the server.
-
-## Server Configuration
-
-| Configuration item                                 | Description             
                                                                                
                                                                                
                     | Default value                                 | Required 
|
-|----------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|----------|
-| `gravitino.server.webserver.enableCorsFilter`      | Enable cross-origin 
resource share filter.                                                          
                                                                                
                         | false                                         | No   
    |
-| `gravitino.server.webserver.allowedOrigins`        | A comma separated list 
of allowed origins to access the resources. The default value is *, which means 
all origins.                                                                    
                      | `*`                                           | No      
 |
-| `gravitino.server.webserver.allowedTimingOrigins`  | A comma separated list 
of allowed origins to time the resource. The default value is the empty string, 
which means no origins.                                                         
                      | ''(empty string)                              | No      
 |
-| `gravitino.server.webserver.allowedMethods`        | A comma separated list 
of allowed HTTP methods used when accessing the resources. The default values 
are GET, POST, HEAD, and DELETE.                                                
                        | `GET,POST,HEAD,DELETE,PUT`                    | No    
   |
-| `gravitino.server.webserver.allowedHeaders`        | A comma separated list 
of allowed HTTP headers specified when accessing the resources. The default 
value is X-Requested-With,Content-Type,Accept,Origin. If the value is a single 
*, it accepts all headers. | `X-Requested-With,Content-Type,Accept,Origin` | No 
      |
-| `gravitino.server.webserver.preflightMaxAgeInSecs` | The number of seconds 
to cache preflight requests by the client. The default value is 1800 seconds or 
30 minutes.                                                                     
                       | `1800`                                        | No     
  |
-| `gravitino.server.webserver.allowCredentials`      | A boolean indicating if 
the resource allows requests with credentials. The default value is true.       
                                                                                
                     | `true`                                        | No       
|
-| `gravitino.server.webserver.exposedHeaders`        | A comma separated list 
of allowed HTTP headers exposed on the client. The default value is the empty 
list.                                                                           
                        | ''(empty string)                              | No    
   |
-| `gravitino.server.webserver.chainPreflight`        | If true chained 
preflight requests for normal handling (as an OPTION request). Otherwise, the 
filter responds to the preflight. The default is true.                          
                               | `true`                                        
| No       |
-
-## Apache Iceberg REST Service Configuration
-
-| Configuration item                             | Description                 
                                                                                
                                                                                
                 | Default value                                 | Required |
-|------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------|----------|
-| `gravitino.iceberg-rest.enableCorsFilter`      | Enable cross-origin 
resource share filter.                                                          
                                                                                
                         | false                                         | No   
    |
-| `gravitino.iceberg-rest.allowedOrigins`        | A comma separated list of 
allowed origins that access the resources. The default value is *, which means 
all origins.                                                                    
                    | `*`                                           | No       |
-| `gravitino.iceberg-rest.allowedTimingOrigins`  | A comma separated list of 
allowed origins that time the resource. The default value is the empty string, 
which means no origins.                                                         
                    | ''(empty string)                              | No       |
-| `gravitino.iceberg-rest.allowedMethods`        | A comma separated list of 
allowed HTTP methods used when accessing the resources. The default values are 
GET, POST, HEAD, and DELETE.                                                    
                    | `GET,POST,HEAD,DELETE,PUT`                    | No       |
-| `gravitino.iceberg-rest.allowedHeaders`        | A comma separated list of 
HTTP allowed headers specified when accessing the resources. The default value 
is X-Requested-With,Content-Type,Accept,Origin. If the value is a single *, it 
accepts all headers. | `X-Requested-With,Content-Type,Accept,Origin` | No       
|
-| `gravitino.iceberg-rest.preflightMaxAgeInSecs` | The number of seconds to 
cache preflight requests by the client. The default value is 1800 seconds or 30 
minutes.                                                                        
                    | `1800`                                        | No       |
-| `gravitino.iceberg-rest.allowCredentials`      | A boolean indicating if the 
resource allows requests with credentials. The default value is true.           
                                                                                
                 | `true`                                        | No       |
-| `gravitino.iceberg-rest.exposedHeaders`        | A comma separated list of 
allowed HTTP headers exposed on the client. The default value is the empty 
list.                                                                           
                        | ''(empty string)                              | No    
   |
-| `gravitino.iceberg-rest.chainPreflight`        | If true chained preflight 
requests for normal handling (as an OPTION request). Otherwise, the filter 
responds to the preflight. The default is true.                                 
                        | `true`                                        | No    
   |
+## Overview
+
+Cross-Origin Resource Sharing is a browser mechanism that controls which web 
origins may call an HTTP API. A browser blocks a request from a page whose 
origin differs from the server's in host, port, or protocol unless the server 
says otherwise, so any browser-based client hosted separately from Gravitino 
needs the CORS filter enabled.
+
+The filter is off by default and applies only to browsers. Requests from 
engines, the CLI, and other server-side clients are unaffected either way.
+
+## Configuration
+
+The Gravitino server and the Iceberg REST service each have their own CORS 
filter, configured with the same property names under different prefixes. Use 
`gravitino.server.webserver.` for the Gravitino server and 
`gravitino.iceberg-rest.` for the Iceberg REST service, and set each one only 
for the service that needs it.
+
+| Property Name           | Description                                        
                                         | Default Value                        
         |
+|-------------------------|---------------------------------------------------------------------------------------------|-----------------------------------------------|
+| `enableCorsFilter`      | Enables the CORS filter                            
                                         | `false`                              
         |
+| `allowedOrigins`        | Comma-separated origins allowed to access the 
resources, or `*` for all                     | `*`                             
              |
+| `allowedTimingOrigins`  | Comma-separated origins allowed to time the 
resources. Empty means none                     | (empty)                       
                |
+| `allowedMethods`        | Comma-separated HTTP methods allowed when 
accessing the resources                           | `GET,POST,HEAD,DELETE,PUT`  
                  |
+| `allowedHeaders`        | Comma-separated request headers allowed, or a 
single `*` to accept any header               | 
`X-Requested-With,Content-Type,Accept,Origin` |
+| `exposedHeaders`        | Comma-separated response headers made readable to 
the client. Empty means none              | (empty)                             
          |
+| `preflightMaxAgeInSecs` | How long a client may cache a preflight response   
                                         | `1800`                               
         |
+| `allowCredentials`      | Whether requests carrying credentials are allowed  
                                         | `true`                               
         |
+| `chainPreflight`        | Passes preflight requests to the target resource 
as an `OPTIONS` request instead of answering them in the filter | `true`        
            |
+
+### Origins and Credentials
+
+The two defaults do not work together. Browsers reject a response that allows 
credentials while allowing every origin, so leaving `allowedOrigins` at `*` 
with `allowCredentials` at `true` means any authenticated browser request fails 
even though the filter is enabled.
+
+List the origins your clients actually use instead. A wildcard origin is only 
usable when `allowCredentials` is `false`, which rules out any request carrying 
a token or a cookie.
+
+### Example
+
+A web UI served from `https://console.example.com` calling a Gravitino server 
elsewhere needs the following in `gravitino.conf`.
+
+```properties
+gravitino.server.webserver.enableCorsFilter = true
+gravitino.server.webserver.allowedOrigins = https://console.example.com
+gravitino.server.webserver.allowedHeaders = 
X-Requested-With,Content-Type,Accept,Origin,Authorization
+```
+
+`Authorization` is added because the default header list omits it, and a 
browser sending a bearer token names that header in its preflight request.
diff --git a/docs/security/how-to-use-https.md 
b/docs/security/how-to-use-https.md
index b5e36e541d..3be70815fa 100644
--- a/docs/security/how-to-use-https.md
+++ b/docs/security/how-to-use-https.md
@@ -1,102 +1,83 @@
 ---
 title: "HTTPS"
 slug: "/security/how-to-use-https"
-keyword: "security HTTPS protocol"
+keywords:
+  - security
+  - https
+  - tls
 license: "This software is licensed under the Apache License version 2."
 ---
 
-## Introduction
+## Overview
 
-For users choosing OAuth 2.0 as the authentication method, it is recommended 
to use HTTPS instead of HTTP. HTTPS encrypts the request headers, offering 
better protection against smuggling attacks.
+HTTPS encrypts request headers, which matters most when those headers carry 
credentials. Any deployment using OAuth 2.0 or [local users and 
groups](local-users-and-groups.md) should enable it, since both put a token or 
a password in a header on every request.
 
-Note that Gravitino cannot simultaneously support both HTTP and HTTPS within a 
single server instance. If HTTPS is enabled, Gravitino will no longer provide 
HTTP service.
+A server instance serves one protocol. Enabling HTTPS stops the plain HTTP 
listener rather than adding to it, so clients configured against the HTTP port 
need updating at the same time.
 
-Both the Gravitino server and Iceberg REST service can configure and support 
HTTPS.
+## Configuration
 
-### Apache Gravitino Server Configuration
+The Gravitino server and the Iceberg REST service are configured separately 
with the same property names under different prefixes. Use 
`gravitino.server.webserver.` for the Gravitino server and 
`gravitino.iceberg-rest.` for the Iceberg REST service.
 
-| Configuration item                                  | Description            
                                            | Default value     | Required      
                                    |
-|-----------------------------------------------------|--------------------------------------------------------------------|-------------------|---------------------------------------------------|
-| `gravitino.server.webserver.enableHttps`            | Enables HTTPS.         
                                            | `false`           | No            
                                    |
-| `gravitino.server.webserver.httpsPort`              | The HTTPS port number 
of the Jetty web server.                     | `8433`            | No           
                                     |
-| `gravitino.server.webserver.keyStorePath`           | Path to the key store 
file.                                        | (none)            | Yes if use 
HTTPS                                  |
-| `gravitino.server.webserver.keyStorePassword`       | Password to the key 
store.                                         | (none)            | Yes if use 
HTTPS                                  |
-| `gravitino.server.webserver.keyStoreType`           | The type to the key 
store.                                         | `JKS`             | No         
                                       |
-| `gravitino.server.webserver.managerPassword`        | Manager password to 
the key store.                                 | (none)            | Yes if use 
HTTPS                                  |
-| `gravitino.server.webserver.tlsProtocol`            | TLS protocol to use. 
The JVM must support the TLS protocol to use. | (none)            | No          
                                      |
-| `gravitino.server.webserver.enableCipherAlgorithms` | The collection of 
enabled cipher algorithms.                       | '' (empty string) | No       
                                         |
-| `gravitino.server.webserver.enableClientAuth`       | Enables the 
authentication of the client.                          | `false`           | No 
                                               |
-| `gravitino.server.webserver.trustStorePath`         | Path to the trust 
store file.                                      | (none)            | Yes if 
use HTTPS and the authentication of client |
-| `gravitino.server.webserver.trustStorePassword`     | Password to the trust 
store.                                       | (none)            | Yes if use 
HTTPS and the authentication of client |
-| `gravitino.server.webserver.trustStoreType`         | The type to the trust 
store.                                       | `JKS`             | No           
                                     |
+| Property Name             | Description                                    | 
Default Value | Required                       |
+|---------------------------|------------------------------------------------|---------------|--------------------------------|
+| `enableHttps`             | Enables HTTPS                                  | 
`false`       | No                             |
+| `httpsPort`               | HTTPS port for the Jetty web server            | 
`8433` and `9433` | No                         |
+| `keyStorePath`            | Path to the key store file                     | 
(none)        | Yes                            |
+| `keyStorePassword`        | Password for the key store                     | 
(none)        | Yes                            |
+| `managerPassword`         | Manager password for the key store             | 
(none)        | Yes                            |
+| `keyStoreType`            | Key store type                                 | 
`JKS`         | No                             |
+| `tlsProtocol`             | TLS protocol to use, which the JVM must support| 
(none)        | No                             |
+| `enableCipherAlgorithms`  | Cipher algorithms to enable                    | 
(empty)       | No                             |
+| `enableClientAuth`        | Requires clients to authenticate with a 
certificate | `false`  | No                             |
+| `trustStorePath`          | Path to the trust store file                   | 
(none)        | Yes with client authentication |
+| `trustStorePassword`      | Password for the trust store                   | 
(none)        | Yes with client authentication |
+| `trustStoreType`          | Trust store type                               | 
`JKS`         | No                             |
 
-### Apache Iceberg REST Service Configuration
+The default HTTPS port is `8433` for the Gravitino server and `9433` for the 
Iceberg REST service. Everything in the Required column applies once 
`enableHttps` is `true`.
 
-| Configuration item                              | Description                
                                        | Default value     | Required          
                                |
-|-------------------------------------------------|--------------------------------------------------------------------|-------------------|---------------------------------------------------|
-| `gravitino.iceberg-rest.enableHttps`            | Enables HTTPS.             
                                        | `false`           | No                
                                |
-| `gravitino.iceberg-rest.httpsPort`              | The HTTPS port number of 
the Jetty web server.                     | `9433`            | No              
                                  |
-| `gravitino.iceberg-rest.keyStorePath`           | Path to the key store 
file.                                        | (none)            | Yes if use 
HTTPS                                  |
-| `gravitino.iceberg-rest.keyStorePassword`       | Password to the key store. 
                                        | (none)            | Yes if use HTTPS  
                                |
-| `gravitino.iceberg-rest.keyStoreType`           | The type to the key store. 
                                        | `JKS`             | No                
                                |
-| `gravitino.iceberg-rest.managerPassword`        | Manager password to the 
key store.                                 | (none)            | Yes if use 
HTTPS                                  |
-| `gravitino.iceberg-rest.tlsProtocol`            | TLS protocol to use. The 
JVM must support the TLS protocol to use. | (none)            | No              
                                  |
-| `gravitino.iceberg-rest.enableCipherAlgorithms` | The collection of enabled 
cipher algorithms.                       | '' (empty string) | No               
                                 |
-| `gravitino.iceberg-rest.enableClientAuth`       | Enables the authentication 
of the client.                          | `false`           | No                
                                |
-| `gravitino.iceberg-rest.trustStorePath`         | Path to the trust store 
file.                                      | (none)            | Yes if use 
HTTPS and the authentication of client |
-| `gravitino.iceberg-rest.trustStorePassword`     | Password to the trust 
store.                                       | (none)            | Yes if use 
HTTPS and the authentication of client |
-| `gravitino.iceberg-rest.trustStoreType`         | The type to the trust 
store.                                       | `JKS`             | No           
                                     |
+For the values `tlsProtocol` and `enableCipherAlgorithms` accept, see the 
"Additional JSSE Standard Names" section of the Java security guide, under 
[protocols](https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#jssenames)
 and [cipher 
suites](https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites)
 respectively.
 
-Refer to the "Additional JSSE Standard Names" section of the [Java security 
guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#jssenames)
 for the list of protocols related to tlsProtocol. You can find the list of 
`tlsProtocol` values for Java 8 in this document.
+## Local Development Example
 
-Refer to the "Additional JSSE Standard Names" section of the [Java security 
guide](https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites)
 for the list of protocols related to tlsProtocol. You can find the list of 
`enableCipherAlgorithms` values for Java 8 in this document.
+The following produces a self-signed certificate so you can exercise an HTTPS 
endpoint on one machine. It is not a production setup, since a self-signed 
certificate trusted by editing a JVM trust store is not how certificates are 
managed in a real deployment.
 
-### Example
-
-Follow these steps to set up an HTTPS server:
-
-1. Prerequisite
-   - You need to install the JDK8, wget, and set the environment JAVA_HOME.
-   - If you want to use the command `curl` to request the Gravitino server, 
you should install openSSL.
-2. Generate the key store
+**1. Generate a key store.**
 
 ```shell
 cd $JAVA_HOME
-bin/keytool -genkeypair  -alias localhost \
--keyalg RSA -keysize 4096 -keypass localhost \
--sigalg SHA256withRSA \
--keystore localhost.jks -storetype JKS -storepass localhost \
--dname "cn=localhost,ou=localhost,o=localhost,l=beijing,st=beijing,c=cn" \
--validity 36500
+bin/keytool -genkeypair -alias localhost \
+  -keyalg RSA -keysize 4096 -keypass {key_password} \
+  -sigalg SHA256withRSA \
+  -keystore localhost.jks -storetype JKS -storepass {store_password} \
+  -dname "cn=localhost,ou=localhost,o=localhost,l=beijing,st=beijing,c=cn" \
+  -validity 36500
 ```
 
-3. Generate the certificate
+**2. Export the certificate.**
 
 ```shell
-bin/keytool -export -alias localhost -keystore localhost.jks -file  
localhost.crt -storepass localhost
+bin/keytool -export -alias localhost -keystore localhost.jks \
+  -file localhost.crt -storepass {store_password}
 ```
 
-4. Import the certificate
+**3. Import it into the JVM trust store** so a local Java client will accept 
it.
 
 ```shell
-bin/keytool -import -alias localhost -keystore jre/lib/security/cacerts -file 
localhost.crt -storepass changeit -noprompt
+bin/keytool -import -alias localhost -keystore jre/lib/security/cacerts \
+  -file localhost.crt -storepass changeit -noprompt
 ```
 
-5. Refer to the [Configurations](../gravitino-server-config.md) and append the 
configuration to the conf/gravitino.conf.
-   Configuration doesn't support resolving environment variables, so you 
should replace `${JAVA_HOME}` with the actual value.
-   Then, You can start the Gravitino server.
+**4. Configure the server.** Append the following to `conf/gravitino.conf`, 
then start Gravitino. Configuration files do not resolve environment variables, 
so write the expanded path rather than `${JAVA_HOME}`.
 
-```text
+```properties
 gravitino.server.webserver.host = localhost
 gravitino.server.webserver.enableHttps = true
-gravitino.server.webserver.keyStorePath = ${JAVA_HOME}/localhost.jks
-gravitino.server.webserver.keyStorePassword = localhost
-gravitino.server.webserver.managerPassword = localhost
+gravitino.server.webserver.keyStorePath = {java_home}/localhost.jks
+gravitino.server.webserver.keyStorePassword = {store_password}
+gravitino.server.webserver.managerPassword = {key_password}
 ```
 
-6. Request the Gravitino server
-
-- If you use Java, you can copy the code below to a file named Main.java
+**5. Connect.** From Java, the client takes the HTTPS URI directly:
 
 ```java
 import org.apache.gravitino.client.GravitinoClient;
@@ -112,9 +93,16 @@ public class Main {
 }
 ```
 
-- If you want to use the command `curl`, you can follow the commands:
+From `curl`, convert the certificate to PEM first:
 
 ```shell
 openssl x509 -inform der -in $JAVA_HOME/localhost.crt -out certificate.pem
-curl -v -X GET --cacert ./certificate.pem -H "Accept: 
application/vnd.gravitino.v1+json" -H "Content-Type: application/json" 
https://localhost:8433/api/version
+curl -v -X GET --cacert ./certificate.pem \
+  -H "Accept: application/vnd.gravitino.v1+json" \
+  https://localhost:8433/api/version
 ```
+
+## Further Reading
+
+- [Configurations](../gravitino-server-config.md) for the rest of the web 
server settings
+- [How to Authenticate](how-to-authenticate.md) for the authentication methods 
HTTPS protects
diff --git a/docs/security/local-users-and-groups.md 
b/docs/security/local-users-and-groups.md
new file mode 100644
index 0000000000..069ab8d31d
--- /dev/null
+++ b/docs/security/local-users-and-groups.md
@@ -0,0 +1,197 @@
+---
+title: "Local Users and Groups"
+slug: "/security/local-users-and-groups"
+keywords:
+  - security
+  - authentication
+  - basic authentication
+license: "This software is licensed under the Apache License version 2."
+---
+
+## Overview
+
+Apache Gravitino can store login identities in its own relational metadata 
store through the `idp-basic` plugin. Usernames, password hashes, and group 
membership live alongside the rest of the server's metadata, and clients 
authenticate with HTTP Basic credentials. Nothing outside Gravitino is required.
+
+The local user store authenticates callers to Gravitino. It does not issue 
tokens or assertions that other services can consume, so it is not a single 
sign-on system and not a replacement for Okta, Microsoft Entra ID, or Keycloak. 
Use it for proofs of concept, offline installations, and isolated deployments 
where a self-contained identity store is acceptable. For anything else, see 
[How to Authenticate](how-to-authenticate.md).
+
+Credentials travel in an HTTP header on every request, so run the server 
behind [HTTPS](how-to-use-https.md) wherever the network is not fully trusted.
+
+The management endpoints are served under `/api/idp/`, which reflects the 
plugin's original name rather than the feature's scope. For request and 
response schemas, see the [OpenAPI definition](../open-api/idp/openapi.yaml).
+
+## Quick Start
+
+**1. Configure the server.** Add the following to `gravitino.conf`. Both 
properties are required, since the `basic` authenticator refuses to start 
without the REST extension package registered.
+
+```properties
+gravitino.authenticators = basic
+gravitino.server.rest.extensionPackages = 
org.apache.gravitino.idp.web.rest.feature
+gravitino.authorization.serviceAdmins = admin
+```
+
+**2. Set the initial administrator password.** Every username in 
`gravitino.authorization.serviceAdmins` needs a stored password before it can 
call the management endpoints. Set this before the first start.
+
+```shell
+export GRAVITINO_INITIAL_ADMIN_PASSWORD='{admin_password}'
+```
+
+**3. Start Gravitino and confirm the administrator works.**
+
+```shell
+curl -s -H "Accept: application/vnd.gravitino.v1+json" \
+  -H "Authorization: Basic $(echo -n 'admin:{admin_password}' | base64)" \
+  http://localhost:8090/api/version
+```
+
+**4. Create a user.**
+
+```shell
+curl -s -X POST -H "Accept: application/vnd.gravitino.v1+json" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Basic $(echo -n 'admin:{admin_password}' | base64)" \
+  -d '{"user":"alice","password":"{user_password}"}' \
+  http://localhost:8090/api/idp/users
+```
+
+## Configuration
+
+| Configuration Item                        | Description                      
                                 | Example                                     |
+|-------------------------------------------|-------------------------------------------------------------------|---------------------------------------------|
+| `gravitino.authenticators`                | Must be `basic`, and must not 
include `simple`                    | `basic`                                   
  |
+| `gravitino.server.rest.extensionPackages` | Registers the user and group 
management endpoints                 | 
`org.apache.gravitino.idp.web.rest.feature` |
+| `gravitino.authorization.serviceAdmins`   | Comma-separated usernames 
allowed to manage users and groups      | `admin`                               
      |
+
+The local user store is incompatible with the `simple` authenticator, which is 
the server default and accepts the username a client supplies without checking 
a password. Both authenticators claim the same `Basic` authorization header, 
and the server uses the first one listed that claims it, so listing `simple` 
ahead of `basic` means passwords are never checked. Replace `simple` rather 
than adding to it.
+
+The Web UI reads `gravitino.authenticators` from the server and presents a 
username and password form when `basic` is the active authenticator.
+
+### Password and Username Rules
+
+These rules apply to user creation, password changes, and 
`GRAVITINO_INITIAL_ADMIN_PASSWORD` alike.
+
+| Rule            | Value                                     |
+|-----------------|-------------------------------------------|
+| Username        | Required, and must not contain a colon    |
+| Password length | 12 to 64 characters inclusive             |
+
+Passwords are reset by an administrator rather than changed by the user, so a 
password change request carries the new password only and no current password.
+
+## Managing Users and Groups
+
+All management endpoints are under `http://{host}:{port}/api/idp` and require 
Basic authentication as a service admin. Send `Accept: 
application/vnd.gravitino.v1+json` on every request, and `Content-Type: 
application/json` on requests with a body.
+
+### User Operations
+
+| Operation       | Method | Path                     | Body                   
                     |
+|-----------------|--------|--------------------------|---------------------------------------------|
+| Get a user      | GET    | `/api/idp/users/{user}`  | None                   
                     |
+| Add a user      | POST   | `/api/idp/users`         | 
`{"user":"alice","password":"{password}"}`  |
+| Reset a password| PUT    | `/api/idp/users/{user}`  | 
`{"password":"{new_password}"}`             |
+| Remove a user   | DELETE | `/api/idp/users/{user}`  | None                   
                     |
+
+The add-user body uses the field name `user` rather than `name`.
+
+```shell
+curl -s -X POST -H "Accept: application/vnd.gravitino.v1+json" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Basic $(echo -n 'admin:{admin_password}' | base64)" \
+  -d '{"user":"alice","password":"{user_password}"}' \
+  http://localhost:8090/api/idp/users
+```
+
+### Group Operations
+
+| Operation                | Method | Path                                     
   | Body                                                       |
+|--------------------------|--------|---------------------------------------------|------------------------------------------------------------|
+| Get a group              | GET    | `/api/idp/groups/{group}`                
   | None                                                       |
+| Add a group              | POST   | `/api/idp/groups`                        
   | `{"group":"engineering"}`                                  |
+| Remove a group           | DELETE | `/api/idp/groups/{group}?force={true 
false}`| None                                                       |
+| Change group membership  | PUT    | `/api/idp/groups/{group}/users`          
   | `{"usersToAdd":["alice"],"usersToRemove":["carol"]}`       |
+
+The add-group body uses the field name `group` rather than `name`. Removing a 
group that still has members fails unless `force=true`. A membership change 
requires at least one of `usersToAdd` or `usersToRemove`, and accepts both in a 
single request.
+
+```shell
+curl -s -X PUT -H "Accept: application/vnd.gravitino.v1+json" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Basic $(echo -n 'admin:{admin_password}' | base64)" \
+  -d '{"usersToAdd":["alice","bob"],"usersToRemove":["carol"]}' \
+  http://localhost:8090/api/idp/groups/engineering/users
+```
+
+## Granting Access to Metadata
+
+A local user can authenticate as soon as it exists, but it can only reach 
metadata once it is registered in a metalake and granted privileges there. The 
two are separate steps, and the username must match.
+
+```shell
+curl -s -X POST -H "Accept: application/vnd.gravitino.v1+json" \
+  -H "Content-Type: application/json" \
+  -H "Authorization: Basic $(echo -n 'admin:{admin_password}' | base64)" \
+  -d '{"name":"alice"}' \
+  http://localhost:8090/api/metalakes/{metalake}/users
+```
+
+When `gravitino.authorization.enable` is set to `true`, only service admins 
can create metalakes. See [Access Control](access-control.md) for roles, 
privileges, and ownership, and [Manage 
Metalakes](../manage-metalake-using-gravitino.md#create-a-metalake) for 
metalake creation.
+
+## Connecting Engines
+
+An engine reaches Gravitino by one of two paths, and each takes its own Basic 
credentials.
+
+### Through the Gravitino Connector
+
+Configure the connector for each engine, then add the credentials below. See 
[Spark 
Authentication](../spark-connector/spark-authentication-with-gravitino.md), 
[Flink 
Authentication](../flink-connector/flink-authentication-with-gravitino.md), and 
[Trino Authentication](../trino-connector/authentication.md) for the rest of 
the connector setup.
+
+```properties
+# Spark
+spark.sql.gravitino.authType=basic
+spark.sql.gravitino.basic.username={username}
+spark.sql.gravitino.basic.password={password}
+```
+
+```yaml
+# Flink
+table.catalog-store.gravitino.gravitino.client.auth.type: basic
+table.catalog-store.gravitino.gravitino.client.basic.username: {username}
+table.catalog-store.gravitino.gravitino.client.basic.password: {password}
+```
+
+```properties
+# Trino, in etc/catalog/gravitino.properties
+gravitino.client.authType=basic
+gravitino.client.basic.username={username}
+gravitino.client.basic.password={password}
+```
+
+Trino also needs `catalog.management=dynamic` in `etc/config.properties` and a 
restart before the catalogs appear.
+
+### Through the Iceberg REST Endpoint
+
+Engines can connect straight to the Iceberg REST service at 
`http://{host}:9001/iceberg/` with no Gravitino connector plugin. See [Connect 
Spark via Iceberg REST](../iceberg-rest-engine/spark.md), [Connect Flink via 
Iceberg REST](../iceberg-rest-engine/flink.md), and [Connect Trino via Iceberg 
REST](../iceberg-rest-engine/trino.md) for the rest of the setup.
+
+```properties
+# Spark
+spark.sql.catalog.{catalog}.rest.auth.type=basic
+spark.sql.catalog.{catalog}.rest.auth.basic.username={username}
+spark.sql.catalog.{catalog}.rest.auth.basic.password={password}
+```
+
+```sql
+-- Flink
+'rest.auth.type' = 'basic',
+'rest.auth.basic.username' = '{username}',
+'rest.auth.basic.password' = '{password}'
+```
+
+Trino has no native Basic mode for Iceberg REST and requires Trino 481 or 
later, so the header is set directly. Generate the encoded credentials with 
`echo -n '{username}:{password}' | base64`, then set:
+
+```properties
+# Trino, in etc/catalog/{catalog}.properties
+connector.name=iceberg
+iceberg.catalog.type=rest
+iceberg.rest-catalog.uri=http://localhost:9001/iceberg
+iceberg.rest-catalog.http-headers=Authorization: Basic {base64_credentials}
+```
+
+## Further Reading
+
+- [OpenAPI definition](../open-api/idp/openapi.yaml) for full request and 
response schemas
+- [How to Use HTTPS](how-to-use-https.md) for protecting credentials in transit
+- [Access Control](access-control.md) for what an authenticated user is 
allowed to do
diff --git a/docs/security/security.md b/docs/security/security.md
deleted file mode 100644
index c4d02963cc..0000000000
--- a/docs/security/security.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-title: "Security"
-slug: "/security/security"
-keyword: "security"
-license: "This software is licensed under the Apache License version 2."
----
-
-## Overview
-
-Gravitino provides features that ensure the highest levels of security for you.
-
-## Capabilities
-
-Gravitino has supported the following security features:
-
-### [Authorization Pushdown](./authorization-pushdown.md)
-
-### [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)
-
-### [Credential vending](credential-vending.md)
-
-### [CORS](how-to-use-cors.md)
diff --git a/docs/spark-connector/spark-authentication-with-gravitino.md 
b/docs/spark-connector/spark-authentication-with-gravitino.md
index d5c6849d1c..1a1dff900b 100644
--- a/docs/spark-connector/spark-authentication-with-gravitino.md
+++ b/docs/spark-connector/spark-authentication-with-gravitino.md
@@ -23,14 +23,14 @@ In the simple mode, the username originates from Spark, and 
is obtained using th
 ## Basic Mode
 
 In Basic mode, the Spark connector authenticates to the Gravitino server using 
HTTP Basic credentials
-against the built-in IDP. The Gravitino server must have Basic authentication 
enabled. See
+against the local user store. The Gravitino server must have Basic 
authentication enabled. See
 [How to authenticate](../security/how-to-authenticate.md#basic-mode) for 
server-side setup.
 
 | Property                           | Type   | Default Value | Description    
                                | Required            |
 
|------------------------------------|--------|---------------|------------------------------------------------|---------------------|
 | spark.sql.gravitino.authType       | string | `simple`      | Set to `basic` 
to enable Basic authentication. | Yes, for Basic mode |
-| spark.sql.gravitino.basic.username | string | (none)        | The built-in 
IDP username.                     | Yes, for Basic mode |
-| spark.sql.gravitino.basic.password | string | (none)        | The built-in 
IDP password.                     | Yes, for Basic mode |
+| spark.sql.gravitino.basic.username | string | (none)        | Username in 
the local user store.                     | Yes, for Basic mode |
+| spark.sql.gravitino.basic.password | string | (none)        | Password for 
that user.                     | Yes, for Basic mode |
 
 ### Basic Configuration Example
 
diff --git a/docs/trino-connector/authentication.md 
b/docs/trino-connector/authentication.md
index 0818b20db3..5ec6de6018 100644
--- a/docs/trino-connector/authentication.md
+++ b/docs/trino-connector/authentication.md
@@ -36,7 +36,7 @@ gravitino.user=admin
 
 ### Basic Authentication
 
-Basic authentication uses HTTP Basic credentials against the Gravitino 
built-in IDP. The Gravitino
+Basic authentication uses HTTP Basic credentials against the Gravitino local 
user store. The Gravitino
 server must have Basic authentication enabled. See
 [How to authenticate](../security/how-to-authenticate.md#basic-mode) for 
server-side setup.
 
@@ -47,7 +47,7 @@ connector.name=gravitino
 gravitino.metalake=metalake
 gravitino.uri=http://localhost:8090
 
-# Basic authentication with built-in IDP
+# Basic authentication with local user store
 gravitino.client.authType=basic
 gravitino.client.basic.username=admin
 gravitino.client.basic.password=YourSecureGravitinoPassword
@@ -58,8 +58,8 @@ gravitino.client.basic.password=YourSecureGravitinoPassword
 | Property                          | Description                              
                       | Default value | Required                   |
 
|-----------------------------------|-----------------------------------------------------------------|---------------|----------------------------|
 | `gravitino.client.authType`       | Authentication type: `simple`, `basic`, 
`oauth2`, or `kerberos` | (none)        | Yes (to enable Basic)      |
-| `gravitino.client.basic.username` | Built-in IDP username                    
                       | (none)        | Yes if authType is `basic` |
-| `gravitino.client.basic.password` | Built-in IDP password                    
                       | (none)        | Yes if authType is `basic` |
+| `gravitino.client.basic.username` | Local user store username                
                           | (none)        | Yes if authType is `basic` |
+| `gravitino.client.basic.password` | Local user store password                
                           | (none)        | Yes if authType is `basic` |
 
 ### OAuth2 Authentication
 
@@ -184,5 +184,5 @@ gravitino.client.session.forwardUser=true
 ### See Also
 
 - [Gravitino Server Authentication 
Configuration](../security/how-to-authenticate.md)
-- [How to use the built-in IDP](../security/how-to-use-built-in-idp.md)
+- [Local users and groups](../security/local-users-and-groups.md)
 - [Trino Connector Configuration](./configuration.md)
diff --git a/docs/webui-v2.md b/docs/webui-v2.md
index bc3fad8182..d45318725c 100644
--- a/docs/webui-v2.md
+++ b/docs/webui-v2.md
@@ -50,11 +50,11 @@ The Web V2 landing page depends on both the authentication 
mode and whether auth
 
   ![simple-with-login](./assets/webui-v2/simple-with-login.png)
 
-- When `gravitino.authenticators=basic` and the built-in IdP extension package 
is configured, the
-  Web UI shows a username and password login form backed by built-in IdP user 
metadata. See
-  [built-in IDP Web UI](security/how-to-use-built-in-idp.md#web-ui).
+- When `gravitino.authenticators=basic` and the local user REST extension 
package is registered, the
+  Web UI shows a username and password login form backed by local user 
metadata. See
+  [Local users and groups](security/local-users-and-groups.md).
 
-- When `gravitino.authenticators=oauth`, OAuth configuration is required for 
login. OAuth mode requires `gravitino.authorization.enable=true`. See the 
details in [Security](security/security.md)
+- When `gravitino.authenticators=oauth`, OAuth configuration is required for 
login. OAuth mode requires `gravitino.authorization.enable=true`. See the 
details in [Security](security/how-to-authenticate.md)
 
   ![oauth-login](./assets/webui-v2/oauth-login.png)
 
diff --git a/docs/webui.md b/docs/webui.md
index 28da4d45d4..e9df9d419a 100644
--- a/docs/webui.md
+++ b/docs/webui.md
@@ -12,13 +12,13 @@ license: "This software is licensed under the Apache 
License version 2."
 
 This document primarily outlines how users can manage metadata within Apache 
Gravitino using the web UI, the graphical interface is accessible through a web 
browser as an alternative to writing code or using the REST interface.
 
-Integrate [OAuth settings](security/security.md) to view, add, modify, and 
delete metalakes, create catalogs, and view catalogs, schemas, and tables, 
among other functions.
+Integrate [OAuth settings](security/how-to-authenticate.md) to view, add, 
modify, and delete metalakes, create catalogs, and view catalogs, schemas, and 
tables, among other functions.
 
 [Build](./how-to-build.md#quick-start) and 
[deploy](./getting-started/index.md#local-workstation) the Gravitino Web UI and 
open it in a browser at `http://<gravitino-host>:<gravitino-port>`, by default 
is [http://localhost:8090](http://localhost:8090).
 
 ## Initial Page
 
-The web UI homepage displayed in Gravitino depends on the configuration 
parameter for OAuth mode, see the details in [Security](security/security.md).
+The web UI homepage displayed in Gravitino depends on the configuration 
parameter for OAuth mode, see the details in 
[Security](security/how-to-authenticate.md).
 
 Set parameter for `gravitino.authenticators`: [`simple`](#simple-mode), 
[`basic`](../security/how-to-authenticate.md#basic-mode), or 
[`oauth`](#oauth-mode). Simple mode is the default authentication option. If 
multiple authenticators are set, the first one is taken by default.
 
@@ -59,7 +59,7 @@ Such as Safari need to enable the developer menu, and select 
`Disable Cross-Orig
 
 ![webui-login-with-oauth](./assets/webui/login-with-oauth.png)
 
-1. Enter the values corresponding to your specific configuration. For detailed 
instructions, refer to [Security](security/security.md).
+1. Enter the values corresponding to your specific configuration. For detailed 
instructions, refer to [Security](security/how-to-authenticate.md).
 
 2. Click on the `LOGIN` button takes you to the homepage.
 

Reply via email to