Identity API
The identity API provides machine-readable user and group directory lookups for
SSSD and other system-level clients. It is mounted under /api/identity/ and is
entirely distinct from the interactive login and admin APIs.
All four endpoints are gated by Bearer token authentication with the
directory.read scope. Tokens are obtained via the OAuth2 token endpoint (typically
with the kerberos_client_auth method for enrolled machines). GSSAPI is not used
directly on these endpoints — the AP-REQ is presented once at /token to obtain
the Bearer token, and all subsequent identity calls use that token.
Authentication
Every request must carry a valid access token with the directory.read scope:
GET /api/identity/users?username=alice&exact=true
Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K0pXVCJ9...
Error responses:
| Status | Body | Meaning |
|---|---|---|
401 Unauthorized | {"error":"missing_token"} | No Authorization: Bearer header. |
401 Unauthorized | {"error":"invalid_token"} | Token signature invalid, expired, or JTI revoked. |
403 Forbidden | {"error":"insufficient_scope"} | Token is valid but does not contain directory.read. |
SSSD two-phase lookup model
SSSD’s ahdapa_lookup() uses a mandatory two-phase model:
Phase 1 — find the object by name and obtain its id:
GET /api/identity/users?username=alice&exact=true
GET /api/identity/groups?search=admins&exact=true
Phase 2 — use the id from Phase 1 as a URI segment to resolve memberships:
GET /api/identity/users/{id}/groups
GET /api/identity/groups/{name}/members
The id field in Phase 1 responses serves as the Phase 2 path segment. For
users, id is the fully-qualified UPN (alice@EXAMPLE.COM). For groups, id
is the group CN.
JSON object contracts
The shape of returned objects is non-negotiable — SSSD’s add_posix_to_json_string_array()
uses key presence to distinguish users from groups.
User object
{
"id": "alice@EXAMPLE.COM",
"username": "alice",
"name": "Alice Smith",
"given_name": "Alice",
"family_name": "Smith",
"email": "alice@example.com",
"uid_number": 10001,
"gid_number": 10001,
"home_directory": "/home/alice",
"login_shell": "/bin/bash",
"gecos": "Alice Smith,,,"
}
id— fully-qualified UPN (uid@REALM). Mandatory. Used as the Phase 2 URI segment.username— LDAPuidattribute, short name only. Mandatory. The presence ofusernamein a response object is the signal SSSD uses to classify the object as a user (not a group).- All other fields are optional and are omitted from the JSON when not set for the user.
Group object
{
"id": "admins",
"name": "admins",
"gid_number": 20001
}
id— group CN. Mandatory. Serves as the Phase 2 path segment.name— group CN. Mandatory. The absence ofusernamecombined with the presence ofnameis the signal SSSD uses to classify the object as a group.gid_number— optional.- Group objects MUST NOT contain a
usernamefield.
Endpoints
GET /api/identity/users
Phase 1 user search.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
username | yes | Username to search for. The @REALM suffix is stripped before lookup — both alice and alice@EXAMPLE.COM resolve to the same entry. |
exact | yes | Must be true. Partial or fuzzy matching is not supported; exact=false returns 400 {"error":"exact_required"}. |
Response: 200 OK with a JSON array. Returns a single-element array on match,
or an empty array [] when the user is not found. Never returns 404.
Example:
curl -s \
-H "Authorization: Bearer $TOKEN" \
"https://idp.example.com/api/identity/users?username=alice&exact=true"
[
{
"id": "alice@EXAMPLE.COM",
"username": "alice",
"name": "Alice Smith",
"email": "alice@example.com",
"uid_number": 10001,
"gid_number": 10001,
"home_directory": "/home/alice",
"login_shell": "/bin/bash"
}
]
GET /api/identity/users/{id}/groups
Phase 2 group-membership lookup for a user.
Path parameter:
| Parameter | Description |
|---|---|
{id} | Short uid (e.g. alice) or fully-qualified UPN (e.g. alice@EXAMPLE.COM). Short form is expanded using server.realm. |
Response: 200 OK with a JSON array of group objects. Returns an empty array
when the user is not found or has no group memberships. Never returns 404.
Note: Group objects returned by this endpoint contain only id and name (and
gid_number when available from the source). No per-group attribute lookups beyond
what is stored in the user’s membership list are performed.
Example:
curl -s \
-H "Authorization: Bearer $TOKEN" \
"https://idp.example.com/api/identity/users/alice@EXAMPLE.COM/groups"
[
{ "id": "admins", "name": "admins" },
{ "id": "developers", "name": "developers" }
]
GET /api/identity/groups
Phase 1 group search.
Query parameters:
| Parameter | Required | Description |
|---|---|---|
search | yes | Group name (CN) to search for. |
exact | yes | Must be true. Partial matching is not supported; exact=false returns 400 {"error":"exact_required"}. |
Response: 200 OK with a JSON array. Returns a single-element array on match,
or an empty array when the group is not found. Never returns 404.
Example:
curl -s \
-H "Authorization: Bearer $TOKEN" \
"https://idp.example.com/api/identity/groups?search=admins&exact=true"
[
{ "id": "admins", "name": "admins", "gid_number": 20001 }
]
GET /api/identity/groups/{name}/members
Phase 2 group member enumeration.
Path parameter:
| Parameter | Description |
|---|---|
{name} | Group CN. Must match exactly. |
Response: 200 OK with a JSON array of user objects. Each member entry
contains only id (fully-qualified UPN) and username (short uid) — no
per-member attribute lookups are performed. Returns an empty array when the
group is not found. Never returns 404.
Example:
curl -s \
-H "Authorization: Bearer $TOKEN" \
"https://idp.example.com/api/identity/groups/admins/members"
[
{ "id": "alice@EXAMPLE.COM", "username": "alice" },
{ "id": "bob@EXAMPLE.COM", "username": "bob" }
]
Data source resolution order
Each endpoint searches data sources in order and returns the result from the first source that has a match:
-
Static users file — if
[users] fileis configured and the entry exists there, it is returned immediately. POSIX attributes (uid_number,gid_number,home_directory,login_shell,gecos) are returned when set in the static file. -
FreeIPA LDAP / IPA API — if
[ipa] uriis configured, a live LDAP or JSON-RPC lookup is performed. POSIX attributes are populated from the LDAP entry (standard POSIX schema:uidNumber,gidNumber,homeDirectory,loginShell,gecos).Group membership for IPA users uses the RFC2307bis schema via FreeIPA’s
memberOfplugin. The lookup is two-phase: (1) fetch the user entry’smemberOfattribute, which IPA’smemberOfplugin maintains as a transitive backlink covering both direct and indirect (nested) group memberships; (2) filter those DNs to entries withobjectClass=posixGroup— non-POSIX IPA groups are excluded. Only groups that have agidNumberare returned to the identity API caller. The legacymemberUidattribute is not consulted.
If neither source contains the entry, an empty array is returned.
SSSD deployment example
The typical SSSD id_provider = idp deployment uses kerberos_client_auth to
obtain a Bearer token with directory.read scope for the identity lookups.
Register a static template client (one-time admin step):
curl -s -b session.jar \
-X POST -H 'Content-Type: application/json' \
-d '{
"client_name": "SSSD Machine Template",
"token_endpoint_auth_method": "kerberos_client_auth",
"kerberos_principal_pattern": "host/*@EXAMPLE.COM",
"kerberos_hbac_service": "sssd-idp",
"scopes": ["openid", "directory.read"],
"grant_types": ["client_credentials"]
}' \
https://idp.example.com/api/admin/clients | python3 -m json.tool
Alternatively, seed this client from the static clients file at startup (see
Configuration — [clients]).
SSSD on each enrolled machine obtains a token using its host keytab and then calls the identity endpoints:
# Phase 0: get a Bearer token via kerberos_client_auth
kinit -k -t /etc/krb5.keytab host/node1.example.com@EXAMPLE.COM
TOKEN=$(curl -s -X POST https://idp.example.com/token \
--negotiate -u: \
-d "grant_type=client_credentials" \
-d "client_id=<template_client_id>" \
-d "scope=openid directory.read" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
# Phase 1: look up a user
curl -s -H "Authorization: Bearer $TOKEN" \
"https://idp.example.com/api/identity/users?username=alice&exact=true"
# Phase 2: get the user's groups
curl -s -H "Authorization: Bearer $TOKEN" \
"https://idp.example.com/api/identity/users/alice@EXAMPLE.COM/groups"
See FreeIPA Co-deployment — SSSD secretless deployment for the full end-to-end setup guide.