@ocenkamobi/om-oidc-client-cjs
v1.0.0
Published
Клиент сервисных аккаунтов auth.ocenka.mobi с кэшем токенов — CommonJS для Node 12
Readme
@ocenkamobi/om-oidc-client-cjs
Клиент сервисных аккаунтов auth.ocenka.mobi для легаси-серверов: CommonJS,
Node 12+, без зависимостей. Делает две вещи: аутентифицирует запрос к /token
через client_secret_basic и кэширует ответ — одинаковый запрос получает тот же
токен, пока тот не начнёт истекать.
Библиотека отвечает за авторизацию сервисных аккаунтов — про реализацию грантов в auth.ocenka.mobi ничего не знает.
Для ESM и Node 20+ —
@ocenkamobi/om-oidc-client.
Подключение
npm i @ocenkamobi/om-oidc-client-cjs// oidc.js
const { createOmOidcClient } = require('@ocenkamobi/om-oidc-client-cjs')
module.exports = createOmOidcClient({
authority: 'https://auth.ocenka.mobi',
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET
})const oidc = require('./oidc')
const { access_token: accessToken } = 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 секунд.
got 11
const got = require('got')
const { tokenHooks, withToken } = require('@ocenkamobi/om-oidc-client-cjs/got')
const api = got.extend(tokenHooks(oidc))
await api.get('https://express.ocenka.mobi/api/orders', withToken({
grant_type: 'client_credentials',
resource: 'https://express.ocenka.mobi'
})).json()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))axios
const axios = require('axios')
const { attachToken } = require('@ocenkamobi/om-oidc-client-cjs/axios')
const api = axios.create()
attachToken(api, oidc)
await api.get('https://express.ocenka.mobi/api/orders', {
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 возвращает функцию, снимающую интерцептор.
Тесты
pnpm test # vitest на текущей ноде
pnpm test:node12 # смоук в node:12-alpine, нужен Docker