f1caa62f28
Tests / frontend-test (pull_request) Successful in 3m14s
Локально путь к сертификату вёл в ~/Downloads у каждого свой, а в контейнере задавался двумя отдельными переменными. Теперь каталог один и подменяется целиком через SECRETS_DIR: по умолчанию secrets/ в корне репозитория, в контейнере /certs (смонтированный каталог с хоста, GRAPH_CERT_DIR не меняется). Имена файлов внутри фиксированы, поэтому GRAPH_CERT_PATH и GRAPH_KEY_PATH больше не нужны. Содержимое secrets/ в .gitignore, кроме README. Отсутствие файлов теперь даёт понятную ошибку вместо ENOENT — на свежем клоне это основной сценарий отказа. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
78 lines
4.4 KiB
Markdown
78 lines
4.4 KiB
Markdown
# hr-appeals-auth
|
||
|
||
Сервис получает app-only токен Azure AD (JWT client
|
||
assertion) и используется для авторизованных запросов к внутреннему API
|
||
`api.tnsu.kz`. Сам токен наружу не отдаётся — из HTTP-эндпоинтов сервиса
|
||
доступны только `/healthz` и `/diag/token` (последний подтверждает, что
|
||
токен получен, но не возвращает его значение). Детали получения токена — в
|
||
[graphAuth.ts](src/graphAuth.ts).
|
||
|
||
## Переменные окружения
|
||
|
||
| Переменная | Назначение |
|
||
|---|---|
|
||
| `GRAPH_TENANT_ID` | Tenant ID Azure AD |
|
||
| `GRAPH_CLIENT_ID` | Client ID App Registration, к которому привязан сертификат `SPFxDeploy` |
|
||
| `SECRETS_DIR` | Каталог с `SPFxDeploy_cert.pem` и `SPFxDeploy_key.pem` (имена файлов фиксированы). По умолчанию — [`secrets/`](../secrets/README.md) в корне репозитория; в контейнере `/certs`, куда смонтирован каталог с хоста. Задавать нужно только чтобы подменить каталог целиком |
|
||
| `GRAPH_SCOPE` | OAuth-scope запрашиваемого токена. По умолчанию — App ID URI самого `api.tnsu.kz` (`api://9232d9e2-.../.default`), задавать не нужно. Токен для `https://graph.microsoft.com/.default` тут не подойдёт: `api.tnsu.kz` проверяет `aud` входящего JWT и отвечает 401 |
|
||
| `PORT` | Порт HTTP-сервера сервиса, по умолчанию `3000` (локально — `3100`, см. `.env.example`) |
|
||
|
||
## Локальный запуск
|
||
|
||
Из корня репозитория `npm run dev` поднимает сервис вместе с фронтендом. Отдельно:
|
||
|
||
```bash
|
||
cp .env.example .env # tenant/client id и порт
|
||
npm run dev # tsc + запуск с чтением .env (--env-file, Node 20+)
|
||
```
|
||
|
||
Сертификат и ключ при этом должны лежать в [`secrets/`](../secrets/README.md) —
|
||
в git они не хранятся.
|
||
|
||
Проверить, что авторизация работает: `curl http://localhost:3100/diag/token` →
|
||
`{"ok":true}`. Если вернулась ошибка — в ней будет ответ Azure AD целиком.
|
||
|
||
## Как обратиться к api.tnsu.kz с авторизацией
|
||
|
||
Токен запрашивается через `getAppOnlyToken()` и передаётся в заголовке
|
||
`Authorization: Bearer <token>`. Функция сама кэширует токен до истечения,
|
||
повторно вызывать её на каждый запрос не накладно — за сеть она сходит
|
||
только когда кэш устарел.
|
||
|
||
```ts
|
||
import { getAppOnlyToken } from './graphAuth';
|
||
|
||
async function callApiTnsu(path: string, init: RequestInit = {}) {
|
||
const token = await getAppOnlyToken();
|
||
|
||
const response = await fetch(`https://api.tnsu.kz${path}`, {
|
||
...init,
|
||
headers: {
|
||
...init.headers,
|
||
Authorization: `Bearer ${token}`
|
||
}
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`api.tnsu.kz ответил ${response.status}: ${await response.text()}`);
|
||
}
|
||
|
||
return response.json();
|
||
}
|
||
```
|
||
|
||
Перед тем как писать реальные вызовы, убедитесь, что обмен сертификата на
|
||
токен вообще работает — на запущенном сервисе:
|
||
|
||
```bash
|
||
curl http://localhost:3000/diag/token
|
||
# {"ok":true} — сертификат подошёл, токен получен и закэширован
|
||
# {"ok":false,"error":"..."} — см. текст ошибки: неверный tenant/client id,
|
||
# истёкший сертификат, неверный scope и т.п.
|
||
```
|
||
|
||
Если `/diag/token` отвечает `ok:true`, но реальный вызов `api.tnsu.kz`
|
||
падает с `401`/`403` — вероятная причина в `GRAPH_SCOPE`: токен получен, но
|
||
выпущен не для этого API (Azure AD не проверяет аудиторию на этапе выдачи
|
||
токена — она проверяется уже на стороне `api.tnsu.kz`).
|