@ocenkamobi/om-oidc-client-cjs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -0
- package/axios.d.ts +12 -0
- package/axios.js +16 -0
- package/cache.js +69 -0
- package/got.d.ts +8 -0
- package/got.js +26 -0
- package/index.d.ts +72 -0
- package/index.js +75 -0
- package/package.json +69 -0
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# @ocenkamobi/om-oidc-client-cjs
|
|
2
|
+
|
|
3
|
+
Клиент сервисных аккаунтов auth.ocenka.mobi для легаси-серверов: CommonJS,
|
|
4
|
+
Node 12+, без зависимостей. Делает две вещи: аутентифицирует запрос к `/token`
|
|
5
|
+
через `client_secret_basic` и кэширует ответ — одинаковый запрос получает тот же
|
|
6
|
+
токен, пока тот не начнёт истекать.
|
|
7
|
+
|
|
8
|
+
Библиотека отвечает за авторизацию сервисных аккаунтов — про реализацию грантов
|
|
9
|
+
в [auth.ocenka.mobi](https://gitlab.com/ocenkamobi/auth.ocenka.mobi/-/blob/main/docs/README.md)
|
|
10
|
+
ничего не знает.
|
|
11
|
+
|
|
12
|
+
Для ESM и Node 20+ —
|
|
13
|
+
[`@ocenkamobi/om-oidc-client`](https://gitlab.com/om-misc/om-oidc-client).
|
|
14
|
+
|
|
15
|
+
## Подключение
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm i @ocenkamobi/om-oidc-client-cjs
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```js
|
|
22
|
+
// oidc.js
|
|
23
|
+
const { createOmOidcClient } = require('@ocenkamobi/om-oidc-client-cjs')
|
|
24
|
+
|
|
25
|
+
module.exports = createOmOidcClient({
|
|
26
|
+
authority: 'https://auth.ocenka.mobi',
|
|
27
|
+
clientId: process.env.CLIENT_ID,
|
|
28
|
+
clientSecret: process.env.CLIENT_SECRET
|
|
29
|
+
})
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
const oidc = require('./oidc')
|
|
34
|
+
|
|
35
|
+
const { access_token: accessToken } = await oidc.token({
|
|
36
|
+
grant_type: 'client_credentials',
|
|
37
|
+
resource: 'https://express.ocenka.mobi',
|
|
38
|
+
scope: 'openid profile org express'
|
|
39
|
+
})
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Клиент создаётся один на каждый сервисный аккаунт и держит отдельный кэш.
|
|
43
|
+
|
|
44
|
+
| опция | по умолчанию | значение |
|
|
45
|
+
| -------------- | ------------ | --------------------------------------------------------------- |
|
|
46
|
+
| `authority` | — | сервер авторизации, токены запрашиваются у `${authority}/token` |
|
|
47
|
+
| `clientId` | — | `client_id` сервисного аккаунта |
|
|
48
|
+
| `clientSecret` | — | `client_secret` сервисного аккаунта |
|
|
49
|
+
| `minTtl` | `60` | сколько секунд осталось в токене, чтобы его взяли из кэша |
|
|
50
|
+
| `maxEntries` | `500` | сколько записей помещается в кэш |
|
|
51
|
+
|
|
52
|
+
## Кэш
|
|
53
|
+
|
|
54
|
+
- Ключ — тело запроса. Порядок полей и скоупов в `scope` значения не имеет,
|
|
55
|
+
пустые поля не учитываются.
|
|
56
|
+
- Токен отдаётся из кэша, пока ему осталось жить не меньше `minTtl` секунд.
|
|
57
|
+
Срок берётся из `expires_in`.
|
|
58
|
+
- Одновременные одинаковые запросы делают один сетевой вызов.
|
|
59
|
+
- Из кэша вытесняются токены, которыми не пользуются: каждое чтение токена из
|
|
60
|
+
кэша поднимает его.
|
|
61
|
+
- Ошибки не кэшируются.
|
|
62
|
+
|
|
63
|
+
```js
|
|
64
|
+
await oidc.token(body, { minTtl: 300 }) // токен нужен на долгую операцию
|
|
65
|
+
await oidc.token(body, { force: true }) // мимо кэша
|
|
66
|
+
oidc.invalidate(body)
|
|
67
|
+
oidc.clear()
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Всё тело вызова `await oidc.token(body)` — это ключ для кэша. Все ключи
|
|
71
|
+
хэшируются.
|
|
72
|
+
|
|
73
|
+
[Отложенный токен](https://gitlab.com/ocenkamobi/auth.ocenka.mobi/-/blob/main/docs/deferred-token.md)
|
|
74
|
+
(`requested_token_type`) выдаётся как любой другой ответ и кэшируется на свои
|
|
75
|
+
7 суток, но bearer-токеном не является: плагины его в заголовок не поставят, а
|
|
76
|
+
кинут ошибку. Обменивать его на access-токен нужно самому — обычным запросом.
|
|
77
|
+
|
|
78
|
+
## Ошибки
|
|
79
|
+
|
|
80
|
+
Отказ сервера — `OmOidcTokenError` с полями `status`, `error`,
|
|
81
|
+
`error_description`. Ретраев нет. Запрос к `/token` прерывается через 10 секунд.
|
|
82
|
+
|
|
83
|
+
## got 11
|
|
84
|
+
|
|
85
|
+
```js
|
|
86
|
+
const got = require('got')
|
|
87
|
+
const { tokenHooks, withToken } = require('@ocenkamobi/om-oidc-client-cjs/got')
|
|
88
|
+
|
|
89
|
+
const api = got.extend(tokenHooks(oidc))
|
|
90
|
+
|
|
91
|
+
await api.get('https://express.ocenka.mobi/api/orders', withToken({
|
|
92
|
+
grant_type: 'client_credentials',
|
|
93
|
+
resource: 'https://express.ocenka.mobi'
|
|
94
|
+
})).json()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`withToken()` кладёт параметры в `context` запроса. Без него токен не ставится.
|
|
98
|
+
|
|
99
|
+
Дефолт для инстанса задаётся тем же `withToken()` вторым аргументом `extend`, а
|
|
100
|
+
снимается `withToken(false)`:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const express = got.extend(
|
|
104
|
+
tokenHooks(oidc),
|
|
105
|
+
withToken({ grant_type: 'client_credentials', resource: 'https://express.ocenka.mobi' })
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
await express.get('https://express.ocenka.mobi/api/orders')
|
|
109
|
+
await express.get('https://express.ocenka.mobi/api/health', withToken(false))
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## axios
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
const axios = require('axios')
|
|
116
|
+
const { attachToken } = require('@ocenkamobi/om-oidc-client-cjs/axios')
|
|
117
|
+
|
|
118
|
+
const api = axios.create()
|
|
119
|
+
attachToken(api, oidc)
|
|
120
|
+
|
|
121
|
+
await api.get('https://express.ocenka.mobi/api/orders', {
|
|
122
|
+
oidc: { grant_type: 'client_credentials', resource: 'https://express.ocenka.mobi' }
|
|
123
|
+
})
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Токен ставится только запросам с `oidc` — без него заголовок не появится.
|
|
127
|
+
|
|
128
|
+
При необходимости параметры можно задать один раз: axios сливает конфиг инстанса
|
|
129
|
+
с конфигом запроса, включая `oidc`.
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
const express = axios.create({
|
|
133
|
+
oidc: { grant_type: 'client_credentials', resource: 'https://express.ocenka.mobi' }
|
|
134
|
+
})
|
|
135
|
+
attachToken(express, oidc)
|
|
136
|
+
|
|
137
|
+
await express.get('https://express.ocenka.mobi/api/orders') // с токеном
|
|
138
|
+
await express.get('https://express.ocenka.mobi/api/health', { oidc: false }) // без токена
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`attachToken` возвращает функцию, снимающую интерцептор.
|
|
142
|
+
|
|
143
|
+
## Тесты
|
|
144
|
+
|
|
145
|
+
```sh
|
|
146
|
+
pnpm test # vitest на текущей ноде
|
|
147
|
+
pnpm test:node12 # смоук в node:12-alpine, нужен Docker
|
|
148
|
+
```
|
package/axios.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AxiosInstance } from 'axios'
|
|
2
|
+
import type { OmOidcClient, OmOidcTokenRequest } from './index'
|
|
3
|
+
|
|
4
|
+
declare module 'axios' {
|
|
5
|
+
interface AxiosRequestConfig {
|
|
6
|
+
/** Параметры запроса токена: с ними запрос уходит с `Authorization: Bearer …` */
|
|
7
|
+
oidc?: OmOidcTokenRequest | false
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Возвращает функцию, снимающую интерцептор */
|
|
12
|
+
export declare function attachToken(instance: AxiosInstance, client: OmOidcClient): () => void
|
package/axios.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function attachToken (instance, client) {
|
|
4
|
+
const id = instance.interceptors.request.use(async (config) => {
|
|
5
|
+
if (config.oidc) {
|
|
6
|
+
const { access_token: accessToken, token_type: tokenType } = await client.token(config.oidc)
|
|
7
|
+
if (tokenType !== 'Bearer') throw new Error(`Not Bearer token, token_type: ${tokenType}`)
|
|
8
|
+
config.headers.Authorization = `Bearer ${accessToken}`
|
|
9
|
+
}
|
|
10
|
+
return config
|
|
11
|
+
})
|
|
12
|
+
/** Возвращает функцию, убирающую интерцептор */
|
|
13
|
+
return () => instance.interceptors.request.eject(id)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { attachToken }
|
package/cache.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const crypto = require('crypto')
|
|
4
|
+
|
|
5
|
+
const DEFAULT_MAX_ENTRIES = 500
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Хранит ответы `/token` до истечения их `expires_in`. Вытесняется то, чем дольше всех не
|
|
9
|
+
* пользовались: одноразовые токены обменов не должны выбивать из кэша тот, что нужен всем.
|
|
10
|
+
*/
|
|
11
|
+
function createTokenCache (maxEntries = DEFAULT_MAX_ENTRIES) {
|
|
12
|
+
const entries = new Map()
|
|
13
|
+
const pending = new Map()
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
key: cacheKey,
|
|
17
|
+
|
|
18
|
+
get (key, minTtl) {
|
|
19
|
+
const entry = entries.get(key)
|
|
20
|
+
if (!entry) return undefined
|
|
21
|
+
|
|
22
|
+
const remaining = entry.expiresAt - Date.now()
|
|
23
|
+
if (remaining <= 0) {
|
|
24
|
+
entries.delete(key)
|
|
25
|
+
return undefined
|
|
26
|
+
}
|
|
27
|
+
if (remaining < minTtl * 1000) return undefined
|
|
28
|
+
|
|
29
|
+
// Обращение возвращает запись в конец очереди
|
|
30
|
+
entries.delete(key)
|
|
31
|
+
entries.set(key, entry)
|
|
32
|
+
return entry.value
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
issue (key, load) {
|
|
36
|
+
let promise = pending.get(key)
|
|
37
|
+
if (!promise) {
|
|
38
|
+
promise = load()
|
|
39
|
+
.then((value) => {
|
|
40
|
+
entries.delete(key)
|
|
41
|
+
if (entries.size >= maxEntries) entries.delete(entries.keys().next().value)
|
|
42
|
+
entries.set(key, { value, expiresAt: Date.now() + value.expires_in * 1000 })
|
|
43
|
+
return value
|
|
44
|
+
})
|
|
45
|
+
.finally(() => pending.delete(key))
|
|
46
|
+
pending.set(key, promise)
|
|
47
|
+
}
|
|
48
|
+
return promise
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
delete (key) {
|
|
52
|
+
entries.delete(key)
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
clear () {
|
|
56
|
+
entries.clear()
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function cacheKey (request) {
|
|
62
|
+
const canonical = Object.entries(request)
|
|
63
|
+
.filter((entry) => entry[1])
|
|
64
|
+
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
65
|
+
.map(([name, value]) => [name, name === 'scope' ? value.split(/\s+/).filter(Boolean).sort().join(' ') : value])
|
|
66
|
+
return crypto.createHash('sha256').update(JSON.stringify(canonical)).digest('hex')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = { createTokenCache }
|
package/got.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExtendOptions } from 'got'
|
|
2
|
+
import type { OmOidcClient, OmOidcTokenRequest } from './index'
|
|
3
|
+
|
|
4
|
+
/** Хуки для `got.extend()` */
|
|
5
|
+
export declare function tokenHooks(client: OmOidcClient): ExtendOptions
|
|
6
|
+
|
|
7
|
+
/** Опции запроса got с параметрами токена */
|
|
8
|
+
export declare function withToken(request: OmOidcTokenRequest | false): { context: { oidc: OmOidcTokenRequest | false } }
|
package/got.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/** Хуки для `got.extend()` */
|
|
4
|
+
function tokenHooks (client) {
|
|
5
|
+
return {
|
|
6
|
+
hooks: {
|
|
7
|
+
beforeRequest: [
|
|
8
|
+
async (options) => {
|
|
9
|
+
const request = options.context.oidc
|
|
10
|
+
if (request) {
|
|
11
|
+
const { access_token: accessToken, token_type: tokenType } = await client.token(request)
|
|
12
|
+
if (tokenType !== 'Bearer') throw new Error(`Not Bearer token, token_type: ${tokenType}`)
|
|
13
|
+
options.headers.authorization = `Bearer ${accessToken}`
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Опции запроса got с параметрами токена */
|
|
22
|
+
function withToken (request) {
|
|
23
|
+
return { context: { oidc: request } }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports = { tokenHooks, withToken }
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/** Автодополнение известных значений, но принимается любая строка (аналог LiteralUnion из type-fest) */
|
|
2
|
+
type Known<T extends string> = T | (string & Record<never, never>)
|
|
3
|
+
|
|
4
|
+
export interface OmOidcClientOptions {
|
|
5
|
+
/** Сервер авторизации, токены запрашиваются у `${authority}/token` */
|
|
6
|
+
authority: string
|
|
7
|
+
clientId: string
|
|
8
|
+
clientSecret: string
|
|
9
|
+
/** Минимальное время жизни токена для использования из кэша, в секундах. По умолчанию 60 */
|
|
10
|
+
minTtl?: number
|
|
11
|
+
/** Максимально количество записей в кэше. По умолчанию 500 */
|
|
12
|
+
maxEntries?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Гранты сервисных аккаунтов auth.ocenka.mobi; значение свободное — сервер знает и другие */
|
|
16
|
+
export type OmOidcGrantType = Known<
|
|
17
|
+
| 'client_credentials'
|
|
18
|
+
| 'urn:ietf:params:oauth:grant-type:token-exchange'
|
|
19
|
+
| 'urn:ocenkamobi:params:oauth:grant-type:client-credentials-act'
|
|
20
|
+
| 'urn:ocenkamobi:params:oauth:grant-type:impersonation'
|
|
21
|
+
>
|
|
22
|
+
|
|
23
|
+
/** Идентификаторы типов токенов по RFC 8693 §3; значение свободное */
|
|
24
|
+
export type OmOidcTokenType = Known<
|
|
25
|
+
| 'urn:ietf:params:oauth:token-type:access_token'
|
|
26
|
+
| 'urn:ocenkamobi:params:oauth:token-type:deferred_token'
|
|
27
|
+
>
|
|
28
|
+
|
|
29
|
+
/** Тело запроса к `/token` как есть: грант и параметры задаёт вызывающий */
|
|
30
|
+
export interface OmOidcTokenRequest {
|
|
31
|
+
grant_type: OmOidcGrantType
|
|
32
|
+
resource?: string
|
|
33
|
+
audience?: string
|
|
34
|
+
scope?: string
|
|
35
|
+
subject?: string
|
|
36
|
+
subject_token?: string
|
|
37
|
+
subject_token_type?: OmOidcTokenType
|
|
38
|
+
actor_token?: string
|
|
39
|
+
actor_token_type?: OmOidcTokenType
|
|
40
|
+
requested_token_type?: OmOidcTokenType
|
|
41
|
+
[param: string]: string | undefined
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface OmOidcTokenResponse {
|
|
45
|
+
access_token: string
|
|
46
|
+
token_type: string
|
|
47
|
+
expires_in: number
|
|
48
|
+
scope?: string
|
|
49
|
+
issued_token_type?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface OmOidcTokenOptions {
|
|
53
|
+
/** Переопределяет `minTtl` клиента для этого вызова */
|
|
54
|
+
minTtl?: number
|
|
55
|
+
/** Запросить новый токен, минуя кэш */
|
|
56
|
+
force?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface OmOidcClient {
|
|
60
|
+
token(request: OmOidcTokenRequest, options?: OmOidcTokenOptions): Promise<OmOidcTokenResponse>
|
|
61
|
+
invalidate(request: OmOidcTokenRequest): void
|
|
62
|
+
clear(): void
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export declare class OmOidcTokenError extends Error {
|
|
66
|
+
readonly status: number
|
|
67
|
+
readonly error: string
|
|
68
|
+
readonly error_description?: string
|
|
69
|
+
constructor(status: number, error: string, error_description?: string)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export declare function createOmOidcClient(options: OmOidcClientOptions): OmOidcClient
|
package/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const http = require('http')
|
|
4
|
+
const https = require('https')
|
|
5
|
+
const { createTokenCache } = require('./cache.js')
|
|
6
|
+
|
|
7
|
+
const TIMEOUT = 10000
|
|
8
|
+
|
|
9
|
+
class OmOidcTokenError extends Error {
|
|
10
|
+
constructor (status, error, errorDescription) {
|
|
11
|
+
super(errorDescription ? `${error}: ${errorDescription}` : error)
|
|
12
|
+
this.name = 'OmOidcTokenError'
|
|
13
|
+
this.status = status
|
|
14
|
+
this.error = error
|
|
15
|
+
this.error_description = errorDescription
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function createOmOidcClient ({ authority, clientId, clientSecret, minTtl = 60, maxEntries }) {
|
|
20
|
+
const endpoint = `${authority.replace(/\/+$/, '')}/token`
|
|
21
|
+
// RFC 6749 §2.3.1: части кодируются как form-urlencoded до base64
|
|
22
|
+
const credentials = Buffer.from(`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`)
|
|
23
|
+
const authorization = `Basic ${credentials.toString('base64')}`
|
|
24
|
+
const cache = createTokenCache(maxEntries)
|
|
25
|
+
|
|
26
|
+
async function request (req) {
|
|
27
|
+
const body = new URLSearchParams(Object.entries(req).filter((entry) => entry[1])).toString()
|
|
28
|
+
const res = await post(endpoint, {
|
|
29
|
+
authorization,
|
|
30
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
31
|
+
'content-length': Buffer.byteLength(body)
|
|
32
|
+
}, body)
|
|
33
|
+
let data = {}
|
|
34
|
+
try {
|
|
35
|
+
data = JSON.parse(res.text)
|
|
36
|
+
} catch (e) {}
|
|
37
|
+
if (res.status !== 200 || !data.access_token) {
|
|
38
|
+
throw new OmOidcTokenError(res.status, data.error || 'invalid_response', data.error_description)
|
|
39
|
+
}
|
|
40
|
+
return data
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
async token (req, options = {}) {
|
|
45
|
+
const key = cache.key(req)
|
|
46
|
+
const ttl = options.minTtl != null ? options.minTtl : minTtl
|
|
47
|
+
const cached = options.force ? undefined : cache.get(key, ttl)
|
|
48
|
+
return cached || cache.issue(key, () => request(req))
|
|
49
|
+
},
|
|
50
|
+
invalidate (req) {
|
|
51
|
+
cache.delete(cache.key(req))
|
|
52
|
+
},
|
|
53
|
+
clear () {
|
|
54
|
+
cache.clear()
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function post (url, headers, body) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const client = url.startsWith('http:') ? http : https
|
|
62
|
+
const req = client.request(url, { method: 'POST', headers }, (res) => {
|
|
63
|
+
let text = ''
|
|
64
|
+
res.setEncoding('utf8')
|
|
65
|
+
res.on('data', (chunk) => { text += chunk })
|
|
66
|
+
res.on('end', () => resolve({ status: res.statusCode, text }))
|
|
67
|
+
res.on('error', reject)
|
|
68
|
+
})
|
|
69
|
+
req.setTimeout(TIMEOUT, () => req.destroy(new Error(`Таймаут запроса к ${url}`)))
|
|
70
|
+
req.on('error', reject)
|
|
71
|
+
req.end(body)
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = { createOmOidcClient, OmOidcTokenError }
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ocenkamobi/om-oidc-client-cjs",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Клиент сервисных аккаунтов auth.ocenka.mobi с кэшем токенов — CommonJS для Node 12",
|
|
5
|
+
"homepage": "https://gitlab.com/om-misc/om-oidc-client-cjs#readme",
|
|
6
|
+
"bugs": {
|
|
7
|
+
"url": "https://gitlab.com/om-misc/om-oidc-client-cjs/issues"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+ssh://git@gitlab.com/om-misc/om-oidc-client-cjs.git"
|
|
12
|
+
},
|
|
13
|
+
"license": "UNLICENSED",
|
|
14
|
+
"author": {
|
|
15
|
+
"name": "@isjs",
|
|
16
|
+
"email": "stas@isjs.ru"
|
|
17
|
+
},
|
|
18
|
+
"type": "commonjs",
|
|
19
|
+
"main": "index.js",
|
|
20
|
+
"types": "index.d.ts",
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=12"
|
|
23
|
+
},
|
|
24
|
+
"exports": {
|
|
25
|
+
".": {
|
|
26
|
+
"types": "./index.d.ts",
|
|
27
|
+
"default": "./index.js"
|
|
28
|
+
},
|
|
29
|
+
"./axios": {
|
|
30
|
+
"types": "./axios.d.ts",
|
|
31
|
+
"default": "./axios.js"
|
|
32
|
+
},
|
|
33
|
+
"./got": {
|
|
34
|
+
"types": "./got.d.ts",
|
|
35
|
+
"default": "./got.js"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"files": [
|
|
39
|
+
"index.js",
|
|
40
|
+
"index.d.ts",
|
|
41
|
+
"cache.js",
|
|
42
|
+
"axios.js",
|
|
43
|
+
"axios.d.ts",
|
|
44
|
+
"got.js",
|
|
45
|
+
"got.d.ts"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"test": "vitest run",
|
|
49
|
+
"test:node12": "docker run --rm -v \"$PWD\":/app -w /app node:12-alpine node test/node12.js"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"axios": ">=0.21",
|
|
53
|
+
"got": "11"
|
|
54
|
+
},
|
|
55
|
+
"peerDependenciesMeta": {
|
|
56
|
+
"axios": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"got": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"packageManager": "pnpm@11.11.0",
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"axios": "^1.20.0",
|
|
66
|
+
"got": "^11.8.6",
|
|
67
|
+
"vitest": "^4.1.11"
|
|
68
|
+
}
|
|
69
|
+
}
|