feat(swagger): OAuth2/Keycloak PKCE flow в Swagger UI + docs links

OpenApiConfig:
- Добавлена OAuth2 security scheme (authorization_code flow с Keycloak
  realm `nstart`). Auth/token URLs из ordinis.openapi.oauth.issuer-uri
  configuration property (default: auth.nstart.space/realms/nstart).
- BearerAuth scheme сохранён secondary — для pre-issued tokens из CI/scripts.
- Servers list переупорядочен — prod первый, staging второй.
- Description обновлён с pointer на /docs/ для full integration guide.

application.yml:
- springdoc.swagger-ui.oauth: client-id (env override), use-pkce-with-
  authorization-code-grant=true. Public client `ordinis-swagger`
  настраивается отдельно в Keycloak — see keycloak-setup-swagger-client.md
  в ordinis-infra repo.
- ORDINIS_OPENAPI_OAUTH_ISSUER_URI env var для override realm на разных
  окружениях.

docs/integration/api/read-endpoints.md:
- Swagger UI section обновлён: акцент на Authorize button + Keycloak
  PKCE login (no client_secret needed), tip про bearerAuth fallback.

docs/index.md:
- Добавлен tab "Я хочу пощупать API" с link на Swagger UI.

После deploy:
- https://ordinis.k8s.264.nstart.cloud/swagger-ui.html  — Swagger UI
- https://ordinis.k8s.264.nstart.cloud/v3/api-docs      — OpenAPI spec
Authorize → выбрать oauth2 → Keycloak login (PKCE) → Try it out с JWT.
This commit is contained in:
Zimin A.N.
2026-05-08 00:08:29 +03:00
parent c12f515f43
commit b601c4636f
4 changed files with 63 additions and 7 deletions
+7
View File
@@ -33,6 +33,13 @@
- [Ошибки и rate limits](integration/api/errors.md) — error codes, retry strategy
- [Лучшие практики](integration/api/best-practices.md) — robust integration
- Я хочу пощупать API
Открой [Swagger UI](https://ordinis.k8s.264.nstart.cloud/swagger-ui.html) —
там можно нажать **Authorize** → войти через Keycloak (PKCE, без secret) →
и сразу делать "Try it out" с реальным JWT. Authorization, error codes,
schemas — всё через interactive UI.
{% endlist %}
## Среды
+12
View File
@@ -14,10 +14,22 @@ Base URL:
## OpenAPI / Swagger UI
Interactive playground с **Keycloak login** (PKCE flow, без client_secret):
- **Swagger UI:** `https://ordinis.k8s.264.nstart.cloud/swagger-ui.html`
→ нажми **Authorize** → выбери `oauth2` → войдёшь через Keycloak.
После login каждый "Try it out" автоматически добавит JWT.
- **Spec JSON:** `https://ordinis.k8s.264.nstart.cloud/v3/api-docs`
- **Spec YAML:** `https://ordinis.k8s.264.nstart.cloud/v3/api-docs.yaml`
{% note tip %}
Если у тебя уже есть pre-issued service token (например для CI scripts) —
выбери схему `bearerAuth` в Authorize dialog и paste'ни raw JWT. PKCE flow
тогда не нужен.
{% endnote %}
Из spec'а можно generate'ить клиент:
```bash
@@ -84,6 +84,14 @@ springdoc:
operations-sorter: method
tags-sorter: alpha
display-request-duration: true
# OAuth2 / Keycloak interactive login (PKCE flow). См. OpenApiConfig.
# Public client `ordinis-swagger` создаётся в Keycloak realm `nstart` с
# redirect URI = https://<host>/swagger-ui/oauth2-redirect.html
oauth:
client-id: ${ORDINIS_SWAGGER_CLIENT_ID:ordinis-swagger}
use-pkce-with-authorization-code-grant: true
use-basic-authentication-with-access-code-grant: false
scope-separator: " "
show-actuator: false
packages-to-scan: cloud.nstart.terravault.ordinis.restapi.web
@@ -5,9 +5,13 @@ import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.OAuthFlow;
import io.swagger.v3.oas.models.security.OAuthFlows;
import io.swagger.v3.oas.models.security.Scopes;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -23,8 +27,15 @@ import java.util.List;
@Configuration
public class OpenApiConfig {
/** Keycloak issuer URI — base для auth/token endpoints. По умолчанию prod realm. */
@Value("${ordinis.openapi.oauth.issuer-uri:https://auth.nstart.space/realms/nstart}")
private String oauthIssuer;
@Bean
public OpenAPI ordinisOpenAPI() {
String authUrl = oauthIssuer + "/protocol/openid-connect/auth";
String tokenUrl = oauthIssuer + "/protocol/openid-connect/token";
return new OpenAPI()
.info(new Info()
.title("Ordinis НСИ ЦУОД — Admin / Read API")
@@ -43,28 +54,46 @@ public class OpenApiConfig {
но с поддержкой `?lang=ru/en` для flatten i18n и `?at=<iso>` для
bitemporal queries.
**Auth:** Bearer JWT от Keycloak (`auth.nstart.space/realms/nstart`).
Роль `ordinis-public` достаточна для чтения. На staging авторизация
может быть выключена (`ORDINIS_AUTH_REQUIRED=false`).
**Авторизация:**
- **Try it out** в Swagger UI — нажми кнопку Authorize → выбери
"oauth2" → Keycloak login flow (PKCE, без client_secret).
- **m2m интеграции** — service account через client_credentials
flow (см. /docs/integration/api/auth.html).
**Документация интеграции:** `docs/integration/altum-imaging-order.md`,
`docs/tech/api-integration.md`.
**Полная интеграция:** https://ordinis.k8s.264.nstart.cloud/docs/
""")
.contact(new Contact()
.name("Ordinis team")
.email("zimin.an@nstart.space"))
.license(new License().name("Internal").url("https://nstart.space")))
.servers(List.of(
new Server().url("https://ordinis.k8s.265.nstart.cloud").description("staging (cluster.265)"),
new Server().url("https://ordinis.k8s.264.nstart.cloud").description("prod (cluster.142)"),
new Server().url("https://ordinis.k8s.265.nstart.cloud").description("staging (cluster.265)"),
new Server().url("http://localhost:8080").description("local dev")))
.components(new Components()
// Primary scheme: OAuth2 PKCE (для interactive Swagger UI users).
// Keycloak realm `nstart`, public client `ordinis-swagger` с PKCE.
.addSecuritySchemes("oauth2",
new SecurityScheme()
.type(SecurityScheme.Type.OAUTH2)
.description("Keycloak OAuth2 — interactive login через Swagger UI с PKCE flow.")
.flows(new OAuthFlows()
.authorizationCode(new OAuthFlow()
.authorizationUrl(authUrl)
.tokenUrl(tokenUrl)
.scopes(new Scopes()
.addString("openid", "OIDC user profile")
.addString("profile", "User profile")
.addString("email", "Email")))))
// Secondary scheme: pre-issued bearer token (для curl / scripts).
.addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
.description("Keycloak realm `nstart`. Service account для m2m: попросить у Ordinis team.")))
.description("Pre-issued JWT (например через client_credentials у service account).")))
// Both schemes available — user picks one в Authorize dialog.
.addSecurityItem(new SecurityRequirement().addList("oauth2"))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"));
}
}