Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

StatusBodyMeaning
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 — LDAP uid attribute, short name only. Mandatory. The presence of username in 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 of username combined with the presence of name is the signal SSSD uses to classify the object as a group.
  • gid_number — optional.
  • Group objects MUST NOT contain a username field.

Endpoints

GET /api/identity/users

Phase 1 user search.

Query parameters:

ParameterRequiredDescription
usernameyesUsername to search for. The @REALM suffix is stripped before lookup — both alice and alice@EXAMPLE.COM resolve to the same entry.
exactyesMust 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:

ParameterDescription
{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:

ParameterRequiredDescription
searchyesGroup name (CN) to search for.
exactyesMust 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:

ParameterDescription
{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:

  1. Static users file — if [users] file is 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.

  2. FreeIPA LDAP / IPA API — if [ipa] uri is 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 memberOf plugin. The lookup is two-phase: (1) fetch the user entry’s memberOf attribute, which IPA’s memberOf plugin maintains as a transitive backlink covering both direct and indirect (nested) group memberships; (2) filter those DNs to entries with objectClass=posixGroup — non-POSIX IPA groups are excluded. Only groups that have a gidNumber are returned to the identity API caller. The legacy memberUid attribute 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.