Route CMS OIDC through Authentik host alias
This commit is contained in:
parent
7a96aaaaf1
commit
33ab11ab08
|
|
@ -45,6 +45,8 @@ docker compose --env-file infra/.env -f infra/docker-compose.yml up -d --build
|
|||
|
||||
Compose-сервис `authentik-bootstrap` создает OIDC-приложение `dc-cms`, группы `dc-cms:*` и стартового администратора из `infra/.env`. После первичного входа временный пароль нужно заменить в Authentik; следующие deploy не перетрут пароль, пока `CMS_BOOTSTRAP_FORCE_PASSWORD=false`.
|
||||
|
||||
CMS обращается к OIDC discovery внутри compose-сети через host-alias Authentik-домена (`CMS_AUTH_DOMAIN`), а не напрямую в контейнер `authentik-server`. Это сохраняет тот же Host/issuer-контекст, который видит браузер.
|
||||
|
||||
## Synology
|
||||
|
||||
Для Synology используется отдельный env-шаблон `infra/.env.synology.example`. Рабочий файл должен лежать рядом как `infra/.env.synology` и не должен попадать в artifact.
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ CMS_BOOTSTRAP_FORCE_PASSWORD=false
|
|||
CMS_AUTH_ENABLED=true
|
||||
CMS_BASE_URL=http://cms.local.nodedc:8210
|
||||
CMS_OIDC_ISSUER=http://cms-auth.local.nodedc:8210/application/o/dc-cms/
|
||||
CMS_OIDC_INTERNAL_BASE_URL=http://authentik-server:9000
|
||||
CMS_OIDC_CLIENT_ID=dc-cms
|
||||
CMS_OIDC_CLIENT_SECRET=replace-with-random-secret
|
||||
CMS_OIDC_REDIRECT_URI=http://cms.local.nodedc:8210/auth/callback
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ CMS_BOOTSTRAP_FORCE_PASSWORD=false
|
|||
CMS_AUTH_ENABLED=true
|
||||
CMS_BASE_URL=https://cms.dcserve.ru
|
||||
CMS_OIDC_ISSUER=https://auth.dcserve.ru/application/o/dc-cms/
|
||||
CMS_OIDC_INTERNAL_BASE_URL=http://authentik-server:9000
|
||||
CMS_OIDC_CLIENT_ID=dc-cms
|
||||
CMS_OIDC_CLIENT_SECRET=replace-with-random-secret
|
||||
CMS_OIDC_REDIRECT_URI=https://cms.dcserve.ru/auth/callback
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ services:
|
|||
SITE_ROOT: /sites/nodedc
|
||||
CMS_AUTH_ENABLED: ${CMS_AUTH_ENABLED:-true}
|
||||
CMS_OIDC_ISSUER: ${CMS_OIDC_ISSUER:?cms oidc issuer required}
|
||||
CMS_OIDC_INTERNAL_BASE_URL: ${CMS_OIDC_INTERNAL_BASE_URL:-http://authentik-server:9000}
|
||||
CMS_OIDC_INTERNAL_BASE_URL: http://${CMS_AUTH_DOMAIN:-cms-auth.local.nodedc}
|
||||
CMS_OIDC_CLIENT_ID: ${CMS_OIDC_CLIENT_ID:?cms oidc client id required}
|
||||
CMS_OIDC_CLIENT_SECRET: ${CMS_OIDC_CLIENT_SECRET:?cms oidc client secret required}
|
||||
CMS_OIDC_REDIRECT_URI: ${CMS_OIDC_REDIRECT_URI:?cms oidc redirect uri required}
|
||||
|
|
|
|||
|
|
@ -368,12 +368,14 @@ function oidcInternalFetchUrl(rawUrl) {
|
|||
|
||||
async function fetchOidc(rawUrl, options = {}) {
|
||||
return fetch(oidcInternalFetchUrl(rawUrl), {
|
||||
redirect: "manual",
|
||||
...options,
|
||||
headers: options.headers,
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchOidcJson(rawUrl, options = {}) {
|
||||
const fetchUrl = oidcInternalFetchUrl(rawUrl);
|
||||
const response = await fetchOidc(rawUrl, {
|
||||
...options,
|
||||
headers: {
|
||||
|
|
@ -381,8 +383,31 @@ async function fetchOidcJson(rawUrl, options = {}) {
|
|||
...(options.headers || {}),
|
||||
},
|
||||
});
|
||||
const payload = await response.json().catch(() => null);
|
||||
return { response, payload };
|
||||
const body = await response.text().catch(() => "");
|
||||
let payload = null;
|
||||
try {
|
||||
payload = body ? JSON.parse(body) : null;
|
||||
} catch {
|
||||
payload = null;
|
||||
}
|
||||
return { response, payload, body, fetchUrl };
|
||||
}
|
||||
|
||||
function oidcJsonError(label, response, body, fetchUrl) {
|
||||
const contentType = response.headers.get("content-type") || "";
|
||||
const location = response.headers.get("location") || "";
|
||||
const bodyPreview = String(body || "")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.slice(0, 180);
|
||||
const parts = [
|
||||
`${label}: ${response.status}`,
|
||||
`url=${fetchUrl}`,
|
||||
contentType ? `content-type=${contentType}` : "",
|
||||
location ? `location=${location}` : "",
|
||||
bodyPreview ? `body=${bodyPreview}` : "",
|
||||
].filter(Boolean);
|
||||
return parts.join("; ");
|
||||
}
|
||||
|
||||
function publicOidcUrl(rawUrl) {
|
||||
|
|
@ -416,9 +441,9 @@ async function loadOidcDiscovery() {
|
|||
if (!discoveryPromise) {
|
||||
discoveryPromise = (async () => {
|
||||
const wellKnownUrl = new URL(".well-known/openid-configuration", authConfig.issuer);
|
||||
const { response, payload } = await fetchOidcJson(wellKnownUrl);
|
||||
const { response, payload, body, fetchUrl } = await fetchOidcJson(wellKnownUrl);
|
||||
if (!response.ok || !payload) {
|
||||
throw new Error(`Не удалось загрузить OIDC discovery: ${response.status}`);
|
||||
throw new Error(oidcJsonError("Не удалось загрузить OIDC discovery", response, body, fetchUrl));
|
||||
}
|
||||
discoveryCache = normalizeDiscovery(payload);
|
||||
return discoveryCache;
|
||||
|
|
@ -435,9 +460,9 @@ async function jwksForDiscovery(discovery) {
|
|||
if (!jwksPromise || jwksUriCache !== discovery.jwks_uri) {
|
||||
jwksUriCache = discovery.jwks_uri;
|
||||
jwksPromise = (async () => {
|
||||
const { response, payload } = await fetchOidcJson(discovery.jwks_uri);
|
||||
const { response, payload, body, fetchUrl } = await fetchOidcJson(discovery.jwks_uri);
|
||||
if (!response.ok || !payload) {
|
||||
throw new Error(`Не удалось загрузить OIDC JWKS: ${response.status}`);
|
||||
throw new Error(oidcJsonError("Не удалось загрузить OIDC JWKS", response, body, fetchUrl));
|
||||
}
|
||||
jwksCache = createLocalJWKSet(payload);
|
||||
return jwksCache;
|
||||
|
|
|
|||
Loading…
Reference in New Issue