Делегированные учётные данные
Делегированные учётные данные (delegated credential) — это токен доступа,
который передаёт кому-то ограниченную часть ваших прав, и который этот
получатель не может расширить.
Используйте их, когда работа совместная: подрядчик прогоняет тесты в одном
проекте, CI-задача не должна иметь доступ к stage, MCP-клиент подключается от
имени ассистента, интеграция с партнёром, которой вы доверяете не полностью. Для
этого плохо подходит API-токен, потому что его владелец может изменить его права.
Делегированные учётные данные после выпуска изменить нельзя — ни их получателю,
ни вам. Вместо изменения выпустите новые.
Эндпоинты живут на admin-узле, под POST /api/v1/auth/delegated-credentials.
Токен несёт префикс mkd_ — именно по нему все части Mockarty отличают
делегированные учётные данные от API-токена (mk_) или токена интеграции
(mki_) ещё до обращения к базе.
Чем отличаются от API-токена
| API-токен | Делегированные учётные данные | |
|---|---|---|
| Namespace | значение по умолчанию, которое владелец может изменить | набор, зафиксированный при выпуске; получатель не может его дополнить |
| Действия | список, который владелец может править | зафиксированы при выпуске |
| Срок действия | необязателен | обязателен и не более 365 дней вперёд |
| Кто может изменить область | его владелец | никто — эндпоинта, записывающего область, не существует |
| Чем управляет | токенами своего владельца | только теми делегированными учётными данными, которые выпустил сам |
| Префикс токена | mk_ |
mkd_ |
Практическая разница: API-токен, выданный подрядчику, тот может расширить на все
namespace с правом удаления. Делегированные учётные данные, выданные тому же
подрядчику, остаются в названных вами namespace, с названными вами действиями, до
названного вами момента — либо до отзыва.
Зафиксированная область
Три свойства фиксируются при выпуске:
| Свойство | Что означает | Что может сделать получатель |
|---|---|---|
| Namespace | namespace, в которых учётные данные могут работать | ничего. Запрос с namespace вне набора отклоняется, а не перенаправляется молча |
| Действия | read, write, delete |
ничего. Список действий подставляется из самих учётных данных при каждом запросе |
| Срок действия | момент, когда они перестают работать | ничего. Он обязателен и не более 365 дней вперёд |
Namespace, названный в заголовке, в параметре запроса или в теле JSON, не может
сдвинуть границу. Namespace, в котором может работать запрос, берётся из самих
учётных данных; то, что вы отправили, может только выбрать один из
зафиксированных namespace, но не добавить новый.
Выпуск
Тот, кто выпускает, — это тот, кто прошёл аутентификацию: человек, вошедший в
кабинет, или сервис с API-токеном. Вы можете выпустить учётные данные только с
областью внутри собственного доступа, а если вызывающий — сам делегированные
учётные данные, то только внутри их зафиксированной области. Поэтому цепочка
делегирований может только сужаться.
CLI
mockarty-cli delegated-credential create \
--name ci-reader \
--description "nightly regression job" \
--namespaces staging \
--actions read \
--expires 30d
--namespaces стоит во множественном числе, потому что это набор, к которому
привязаны учётные данные. Глобальный флаг --namespace — про другое: это
namespace, в котором работает ваша сессия CLI.
cURL
curl -X POST http://localhost:5770/api/v1/auth/delegated-credentials \
-H "Authorization: Bearer mk_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "ci-reader",
"description": "nightly regression job",
"namespaces": ["staging"],
"allowed_actions": ["read"],
"expires_at": "2026-12-31T00:00:00Z"
}'
Go
expires := time.Now().UTC().Add(30 * 24 * time.Hour)
issued, err := client.DelegatedCredentials().Create(ctx, mockarty.DelegatedCredentialCreateRequest{
Name: "ci-reader",
Description: "nightly regression job",
Namespaces: []string{"staging"},
AllowedActions: []string{"read"},
ExpiresAt: &expires,
})
fmt.Println(issued.Token) // выводится один раз
Python
from datetime import datetime, timedelta, timezone
from mockarty import DelegatedCredentialCreateRequest
issued = client.delegated_credentials.create(DelegatedCredentialCreateRequest(
name="ci-reader",
description="nightly regression job",
namespaces=["staging"],
allowed_actions=["read"],
expires_at=datetime.now(timezone.utc) + timedelta(days=30),
))
print(issued.token) # выводится один раз
Java
DelegatedCredentialWithToken issued = client.delegatedCredentials().create(
DelegatedCredentialCreateRequest.builder()
.name("ci-reader")
.description("nightly regression job")
.namespaces(List.of("staging"))
.allowedActions(List.of("read"))
.expiresAt(Instant.now().plus(Duration.ofDays(30)))
.build());
System.out.println(issued.getToken()); // выводится один раз
В ответе токен приходит один раз. Он не хранится и не может быть прочитан
повторно — сохраняется только его верификатор. Поэтому сохраните его там, откуда
интеграция сможет его взять. Если потеряли — смените токен (rotate), а не
выпускайте новые учётные данные: так история аудита останется на одной сущности.
Не указывайте allowed_actions — тогда будут разрешены все действия (read,
write, delete). Набор namespace и срок действия всё равно обязательны: без них
это был бы API-токен, то есть ровно то, что эти учётные данные заменяют.
Использование
Получатель предъявляет их так же, как предъявлял бы API-токен:
MOCKARTY_API_KEY=mkd_... mockarty-cli mock list
client := mockarty.NewClient("http://localhost:5770",
mockarty.WithAPIKey("mkd_..."))
Для интеграции с проектом положите токен в секреты этого проекта и читайте его в
ту же переменную, в которую попал бы API-токен: изменений на стороне клиента не
нужно, область применения применяет сервер.
Попытка выйти за пределы зафиксированной области отклоняется с 403, причём в
ответе называется учётная запись, а не вызывающий, чтобы оператор мог отличить
отказ по области от отказа по правам.
Смена токена (rotate)
Смена выпускает новый токен и оставляет область ровно такой, какой она была.
Прежний токен перестаёт работать сразу. Это нужно, когда токен утёк, а работа
должна продолжаться:
CLI
mockarty-cli delegated-credential rotate 5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51
cURL
curl -X POST http://localhost:5770/api/v1/auth/delegated-credentials/5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51/rotate \
-H "Authorization: Bearer mk_YOUR_TOKEN"
Go
rotated, err := client.DelegatedCredentials().Rotate(ctx, "5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51")
fmt.Println(rotated.Token)
Python
rotated = client.delegated_credentials.rotate("5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51")
print(rotated.token)
Java
DelegatedCredentialWithToken rotated =
client.delegatedCredentials().rotate("5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51");
System.out.println(rotated.getToken());
Смена токена — это не изменение области. Если работе теперь нужен другой
namespace, другое действие или более поздний срок, выпустите новые учётные
данные: тогда изменение получит собственную сущность в истории аудита, а не
окажется незаметной правкой тех учётных данных, которые уже у кого-то на руках.
Отзыв
Отзыв необратим и действует на всех узлах кластера, а не только на том, который
ответил на запрос. Отмены отзыва намеренно нет — «временно отозванные» это ровно
то окно, которого у общих учётных данных быть не должно. Повторный отзыв — не
ошибка, а отсутствие изменений.
CLI
mockarty-cli delegated-credential revoke 5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51
cURL
curl -X DELETE http://localhost:5770/api/v1/auth/delegated-credentials/5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51 \
-H "Authorization: Bearer mk_YOUR_TOKEN"
Go
err := client.DelegatedCredentials().Revoke(ctx, "5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51")
Python
client.delegated_credentials.revoke("5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51")
Java
client.delegatedCredentials().revoke("5f3c9d10-2b7a-4c8e-9d1f-8a6b0c2e4d51");
Список
Тот, кто выпустил, видит все выпущенные им учётные данные, новые сверху. Сами
делегированные учётные данные видят только то, что выпустили сами — не
учётные данные своего издателя и не соседние.
CLI
mockarty-cli delegated-credential list
cURL
curl http://localhost:5770/api/v1/auth/delegated-credentials \
-H "Authorization: Bearer mk_YOUR_TOKEN"
Go
page, err := client.DelegatedCredentials().List(ctx)
for _, cred := range page.Credentials {
fmt.Println(cred.ID, cred.Name, cred.Namespaces, cred.ExpiresAt)
}
Python
for cred in client.delegated_credentials.list().credentials:
print(cred.id, cred.name, cred.namespaces, cred.expires_at)
Java
for (DelegatedCredential cred : client.delegatedCredentials().list().getCredentials()) {
System.out.println(cred.getId() + " " + cred.getNamespaces());
}
Чего делегированные учётные данные намеренно не могут
- Их нельзя расширить. Ни один эндпоинт не записывает namespace, действия или
срок действия. Смена токена заменяет только токен. - Они не могут управлять API-токенами владельца. Создание, чтение,
редактирование и отзыв API-токенов отклоняются: там они действуют как свой
издатель и иначе могли бы выпустить себе токен без ограничений. - Они не могут отозвать или сменить своего издателя. Они управляют только
теми учётными данными, которые выпустили сами, поэтому не могут уничтожить или
заменить те, от которых зависят. - Они не могут отозвать или сменить соседние учётные данные, выпущенные рядом.
- Они не могут жить дольше года. Срок действия более чем на 365 дней вперёд
отклоняется. - Отзыв нельзя отменить.
- Их нельзя выбрать заголовком. Namespace в заголовке, параметре запроса или
теле может только выбрать один из зафиксированных namespace. - Они не работают по gRPC. Поверхность gRPC аутентифицирует только
API-токены, поэтому делегированный токен там отклоняется — это граница, на
которую эти учётные данные не распространяются. - Их нельзя выпустить из сессии MCP. Выпуск учётных данных — действие
оператора, за которым стоит человек, поэтому эти пять эндпоинтов не входят в
каталог инструментов, который просматривает ассистент: инструмент, выпускающий
учётные данные, передаёт вызывающему выбор области. Используйте кабинет, CLI или
SDK. - У них нет собственного ограничения частоты запросов. Делегированные учётные
данные не считаются по отдельному бюджету запросов на токен; действуют
ограничения аккаунта издателя. Если интеграции нужен лимит, поставьте его на
входе.
Полезно знать ещё вот что:
- Учётные данные, издатель которых отключён, перестают работать раньше срока.
Издатель должен оставаться активным пользователем, иначе учётные данные не
принимаются. - Отзыв рассылается на все узлы кластера. Узел, не получивший рассылку, перестаёт
принимать учётные данные примерно в течение минуты: сначала отзывайте, а
кратковременное пересечение считайте ожидаемым, а не неудачей отзыва. - Сам токен никогда не попадает в аудит или журнал. В записи аудита есть только
отображаемая метка — первые символы токена, которых достаточно, чтобы сопоставить
строку в списке с сообщением об утечке.
Эндпоинты
| Метод | Путь | Описание |
|---|---|---|
| POST | /api/v1/auth/delegated-credentials |
Выпустить учётные данные; токен возвращается один раз |
| GET | /api/v1/auth/delegated-credentials |
Список учётных данных, доступных вызывающему |
| GET | /api/v1/auth/delegated-credentials/:id |
Прочитать одни учётные данные |
| POST | /api/v1/auth/delegated-credentials/:id/rotate |
Заменить токен, сохранив область |
| DELETE | /api/v1/auth/delegated-credentials/:id |
Отозвать навсегда |
Учётные данные вне полномочий вызывающего отвечают 404, а не 403 — иначе
делегированные учётные данные могли бы перебирать учётные данные своего издателя
по одному идентификатору.
Выпуск, смена токена и отзыв записываются в аудит с идентификатором самих учётных
данных, поэтому разбор инцидента может вытащить всю историю одних учётных данных
одним фильтром.
Где ими управлять в кабинете
Откройте API-токены в кабинете. Владелец namespace увидит там панель
Делегированные учётные данные со списком выпущенного, с зафиксированной
областью, состоянием и последним использованием, а также действия выпуска, смены
токена и отзыва.
См. также
- Справочник API — таблица эндпоинтов аутентификации
- Руководство по CLI — контексты и учётные данные CLI
- Руководство администратора — пользователи, роли и namespace