clienttokenhttp

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 27, 2026 License: AGPL-3.0 Imports: 18 Imported by: 0

Documentation

Overview

Package clienttokenhttp — токен-эндпоинт платформы (задача kacho#898, приёмка F2 §9.1 п. 1).

Клиент предъявляет подписанное утверждение, мы сверяем подпись его открытым ключом из своей же таблицы и выдаём токен доступа по виду выдачи «учётные данные клиента». Форма запроса и форма ответа — стандартные (RFC 6749 §4.4, RFC 7523 §2.2): предъявитель — чужая библиотека, и ответ обязан быть тем, который она умеет прочитать, иначе отказ выглядит для неё сбоем сети.

Единый тон наружу и различимость внутрь

Всякий отказ, наступивший ПОСЛЕ того, как в запросе назван клиент — включая «такого клиента нет», — отдаёт побайтово ОДНО И ТО ЖЕ. Различимый отказ есть оракул: по нему устанавливают, существует ли клиент, жив ли он, какой у него алгоритм и какие идентификаторы однократности уже заняты. Каждый ответ сам по себе безобиден, а вместе они дают карту.

Различимыми остаются ровно ПЯТЬ отказов формы, и все пять решаются ДО того, как запрос назвал хоть какого-нибудь клиента: метод, потолок тела, неразбираемая форма, вид выдачи вне перечня и повторённый параметр утверждения. Они не сообщают о клиенте ничего, потому что клиента на этом шаге ещё нет, и стандартные коды у них обязаны быть свои — иначе чужая библиотека прочтёт «слишком большое тело» как «неверный клиент» и будет чинить не то.

Три отказа по темпу — тоже свои (kaname#315)

Приёмка `docs/engineering/acceptance/ceremony-pace-is-named-by-number.md` называет на этом эндпоинте три оси:

  • П3 — неудавшихся доказательств клиента за окно на источник: источник прислал слишком много, 429 со сроком до момента, когда в окне останется предел минус один отказ;
  • П1 — потолок одновременных обменов, все четыре вида выдачи: занята НАША ёмкость, и занял её не обязательно этот вызывающий, — 503 и `Retry-After: 1`, та же форма, что у занятого проверяющего секрета церемонии;
  • П2 — темп обменов на идентификатор клиента, только машинные полосы: судит проверяющий по ЗАЯВЛЕННОМУ идентификатору до реестра, 429.

Ни одна не сливается с отказом аутентификации: слитая, она заставила бы клиента чинить учётные данные, которые исправны. Все три решаются ДО обращения к реестру и потому о записи реестра не сообщают ничего.

Порядок решений — несущий (Р3)

Метод → потолок тела → разбор формы → вид выдачи из закрытого перечня → П3 → П1 → полоса. Отказы формы решаются до всех осей и остаются различимыми: ось темпа не прячет «неверный метод» под «повторите позже». Источник, исчерпавший П3, места под потолком не занимает. П3 решается при входе по счёту, набранному к этому моменту, а растёт по исходу — отказом доказательства клиента (Р5): на машинных полосах это отказ проверяющего, кроме двух исходов нашей стороны (`registry-unavailable`, `replay-store-unavailable`) и отказов формы и темпа; на полосах церемонии — `invalid_client`, о котором сообщает полоса (LaneVerdict).

Различимость для НАС живёт с другой стороны провода: у каждого исхода свой счётчик и своя запись в журнале. Без счётчика мёртвый контроль невидим — проверка, не отказавшая ни разу за всё время жизни, неотличима от проверки, которая работает и просто не встречала нарушителя.

Index

Constants

View Source
const TokenPath = "/iam/v1/token"

TokenPath — объявленный путь эндпоинта.

#nosec G101 -- это АДРЕС поверхности, а не учётные данные: строка попадает в маршрут и в документацию клиента, её знает всякий, кто обращается к сервису. Слово в адресе называет предмет выдачи, а не хранимый секрет.

Variables

This section is empty.

Functions

func DeclaredOutcomes added in v0.2.0

func DeclaredOutcomes() []string

DeclaredOutcomes — закрытый словарь исходов этой полосы строками.

Отдаётся наружу ради читателя величин: набор рядов витрины обязан совпадать с набором клеток переписи by construction. Выведен из того же clientassertion.Outcomes, которым засеяна перепись, поэтому второй копией словаря не является.

Types

type CeremonyLane added in v0.5.0

type CeremonyLane interface {
	// Grants — виды выдачи, которые обслуживает полоса.
	Grants() []string
	// ServeGrant отвечает на запрос вида grant.
	ServeGrant(w http.ResponseWriter, r *http.Request, grant string) LaneVerdict
}

CeremonyLane — полосы церемонии OAuth на этом же эндпоинте: `authorization_code` и `refresh_token` (приёмка LINE-A-1, группы B и G).

Эндпоинт судит метод, потолок тела и разбор формы для ВСЕХ полос, а вид выдачи — по закрытому перечню: машинные полосы названы поимённо ниже, полосы церемонии — словом церемонии (Grants). Полоса получает разобранную форму. Реализует `ceremonyhttp.TokenLane`.

type Config

type Config struct {
	// BodyCeiling — потолок тела запроса в байтах. ОБЯЗАТЕЛЕН.
	//
	// Умолчания здесь нет намеренно. Величина, которую построение подставляет
	// молча, не может быть предметом стража старта: страж, требующий её
	// задания, зелен при любом входе, потому что незаданной она не бывает. Тело
	// этого запроса — форма с одним подписанным утверждением, и его потолок
	// объявляет тот, кто поднимает сервис.
	BodyCeiling int64
	// InFlightCeiling — потолок одновременных обменов на этом процессе (П1),
	// все четыре вида выдачи. ОБЯЗАТЕЛЕН по тому же доводу, что потолок тела:
	// ноль означал бы «без потолка», а величина, подставленная построением,
	// стражу старта не видна.
	//
	// Обмен сверх потолка отвергается сразу, а не ждёт места: ожидание держало
	// бы соединение и горутину ровно тогда, когда их и так слишком много.
	InFlightCeiling int
	Logger          *slog.Logger
	// Ceremony — полосы церемонии. nil — церемония не собрана (сборка мимо
	// корня либо выключенный вне боевого режима эндпоинт), и её виды выдачи —
	// вне перечня.
	Ceremony CeremonyLane
	// FailedProofs — окно отказов доказательства клиента на источник (П3).
	// ОБЯЗАТЕЛЬНО: эндпоинт без него не решал бы П3 ни разу.
	FailedProofs *failurewindow.Window
	// Source — адрес источника запроса по правилу Р7 (`issuingsource`).
	// ОБЯЗАТЕЛЕН: ключ П3.
	Source func(*http.Request) string
}

Config — настройка эндпоинта.

type Handler

type Handler struct {
	// contains filtered or unexported fields
}

Handler — токен-эндпоинт.

func NewHandler

func NewHandler(cfg Config, verifier Verifier, issuer Issuer) (*Handler, error)

NewHandler строит эндпоинт. Неполная провязка — отказ построения: эндпоинт без проверяющего принимал бы кого угодно, без выдачи — не выдавал бы никому, и оба состояния обнаружились бы на первом запросе, а не на старте.

func (*Handler) Outcomes

func (h *Handler) Outcomes() map[clientassertion.Outcome]uint64

Outcomes — перепись исходов. Читается сборщиком метрик.

func (*Handler) ServeHTTP

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Issuer

type Issuer interface {
	Issue(ctx context.Context, in client_token.Input) (client_token.Output, clientassertion.Outcome, error)
}

Issuer — порт выдачи.

type LaneVerdict added in v0.5.0

type LaneVerdict uint8

LaneVerdict — что полоса церемонии сообщает эндпоинту о доказательстве клиента. Судит по нему ось П3: засчитывается отказ доказательства клиента, а не всякий отказ обмена (приёмка Р5).

const (
	// LaneProofNotRefused — доказательство клиента не отвергнуто: принято либо
	// до него не дошло (отказ формы). `invalid_grant` сюда же: его производит
	// клиент, уже доказавший себя.
	LaneProofNotRefused LaneVerdict = iota
	// LaneProofRefused — отвергнуто доказательство клиента: `invalid_client`.
	LaneProofRefused
)

type Verifier

type Verifier interface {
	// Verify — полоса аутентификации клиента (RFC 7523 §2.2): утверждение
	// доказывает личность клиента, ключ берётся из строки реестра.
	Verify(ctx context.Context, assertionType, raw string) (clientassertion.Result, error)
	// VerifyFederated — федеративная полоса (RFC 7523 §2.1): утверждение
	// подписал внешний издатель, ключ берётся из нашего перечня доверенных
	// издателей.
	VerifyFederated(ctx context.Context, raw string) (clientassertion.Result, error)
}

Verifier — порт проверяющего утверждение, ОБЕ полосы.

Один порт на две полосы, а не два: эндпоинт обязан выбирать полосу сам, по объявленному виду выдачи. Разнеси их по двум портам — и появилась бы посадка, где провязана одна, а вторая молча отвергает всё.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL