@ocenkamobi/om-oidc-client
v1.0.0
Published
Клиент сервисных аккаунтов auth.ocenka.mobi с кэшем токенов
Downloads
219
Readme
@ocenkamobi/om-oidc-client
Клиент сервисных аккаунтов auth.ocenka.mobi для серверов на Node 20+. Делает
две вещи: аутентифицирует запрос к /token через client_secret_basic и
кэширует ответ — одинаковый запрос получает тот же токен, пока тот не начнёт
истекать.
Библиотека отвечает за авторизацию сервисных аккаунтов — про реализацию грантов в auth.ocenka.mobi ничего не знает.
Для CommonJS и Node 12 —
@ocenkamobi/om-oidc-client-cjs.
Подключение
npm i @ocenkamobi/om-oidc-clientimport { createOmOidcClient } from '@ocenkamobi/om-oidc-client'
export const oidc = createOmOidcClient({
authority: 'https://auth.ocenka.mobi',
clientId: process.env.CLIENT_ID!,
clientSecret: process.env.CLIENT_SECRET!,
})
const { access_token } = await oidc.token({
grant_type: 'client_credentials',
resource: 'https://express.ocenka.mobi',
scope: 'openid profile org express',
})Клиент создаётся один на каждый сервисный аккаунт и держит отдельный кэш.
| опция | по умолчанию | значение |
| -------------- | ------------ | --------------------------------------------------------------- |
| authority | — | сервер авторизации, токены запрашиваются у ${authority}/token |
| clientId | — | client_id сервисного аккаунта |
| clientSecret | — | client_secret сервисного аккаунта |
| minTtl | 60 | сколько секунд осталось в токене, чтобы его взяли из кэша |
| maxEntries | 500 | сколько записей помещается в кэш |
Кэш
- Ключ — тело запроса. Порядок полей и скоупов в
scopeзначения не имеет, пустые поля не учитываются. - Токен отдаётся из кэша, пока ему осталось жить не меньше
minTtlсекунд. Срок берётся изexpires_in. - Одновременные одинаковые запросы делают один сетевой вызов.
- Из кэша вытесняются токены, которыми не пользуются: каждое чтение токена из кэша поднимает его.
- Ошибки не кэшируются.
await oidc.token(body, { minTtl: 300 }) // токен нужен на долгую операцию
await oidc.token(body, { force: true }) // мимо кэша
oidc.invalidate(body)
oidc.clear()Всё тело вызова await oidc.token(body) — это ключ для кэша. Все ключи
хэшируются.
Отложенный токен
(requested_token_type) выдаётся как любой другой ответ и кэшируется на свои
7 суток, но bearer-токеном не является: плагины его в заголовок не поставят, а
кинут ошибку. Обменивать его на access-токен нужно самому — обычным запросом.
Ошибки
Отказ сервера — OmOidcTokenError с полями status, error,
error_description. Ретраев нет. Запрос к /token прерывается через 10 секунд.
axios
import axios from 'axios'
import { attachToken } from '@ocenkamobi/om-oidc-client/axios'
const api = axios.create()
attachToken(api, oidc)
await api.get('https://express.ocenka.mobi/api/reports', {
oidc: { grant_type: 'client_credentials', resource: 'https://express.ocenka.mobi' },
})Токен ставится только запросам с oidc — без него заголовок не появится.
При необходимости параметры можно задать один раз: axios сливает конфиг инстанса
с конфигом запроса, включая oidc.
const express = axios.create({
oidc: { grant_type: 'client_credentials', resource: 'https://express.ocenka.mobi' },
})
attachToken(express, oidc)
await express.get('https://express.ocenka.mobi/api/orders') // с токеном
await express.get('https://express.ocenka.mobi/api/health', { oidc: false }) // без токенаattachToken возвращает функцию, снимающую интерцептор.
got
import got from 'got'
import { tokenHooks, withToken } from '@ocenkamobi/om-oidc-client/got'
const api = got.extend(tokenHooks(oidc))
await api.get('https://express.ocenka.mobi/api/reports', withToken({
grant_type: 'client_credentials',
resource: 'https://express.ocenka.mobi',
}))withToken() кладёт параметры в context запроса. Без него токен не ставится.
Дефолт для инстанса задаётся тем же withToken() вторым аргументом extend, а
снимается withToken(false):
const express = got.extend(
tokenHooks(oidc),
withToken({ grant_type: 'client_credentials', resource: 'https://express.ocenka.mobi' }),
)
await express.get('https://express.ocenka.mobi/api/orders')
await express.get('https://express.ocenka.mobi/api/health', withToken(false))