diff --git a/docs/index.md b/docs/index.md index 8ee5088..f64d791 100644 --- a/docs/index.md +++ b/docs/index.md @@ -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 %} ## Среды diff --git a/docs/integration/api/read-endpoints.md b/docs/integration/api/read-endpoints.md index 68bf4e8..7e743dc 100644 --- a/docs/integration/api/read-endpoints.md +++ b/docs/integration/api/read-endpoints.md @@ -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 diff --git a/ordinis-app/src/main/resources/application.yml b/ordinis-app/src/main/resources/application.yml index cfca407..7b8e426 100644 --- a/ordinis-app/src/main/resources/application.yml +++ b/ordinis-app/src/main/resources/application.yml @@ -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:///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 diff --git a/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/config/OpenApiConfig.java b/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/config/OpenApiConfig.java index 09de265..dd18dce 100644 --- a/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/config/OpenApiConfig.java +++ b/ordinis-rest-api/src/main/java/cloud/nstart/terravault/ordinis/restapi/config/OpenApiConfig.java @@ -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=` для 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")); } }