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:
@@ -33,6 +33,13 @@
|
|||||||
- [Ошибки и rate limits](integration/api/errors.md) — error codes, retry strategy
|
- [Ошибки и rate limits](integration/api/errors.md) — error codes, retry strategy
|
||||||
- [Лучшие практики](integration/api/best-practices.md) — robust integration
|
- [Лучшие практики](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 %}
|
{% endlist %}
|
||||||
|
|
||||||
## Среды
|
## Среды
|
||||||
|
|||||||
@@ -14,10 +14,22 @@ Base URL:
|
|||||||
|
|
||||||
## OpenAPI / Swagger UI
|
## OpenAPI / Swagger UI
|
||||||
|
|
||||||
|
Interactive playground с **Keycloak login** (PKCE flow, без client_secret):
|
||||||
|
|
||||||
- **Swagger UI:** `https://ordinis.k8s.264.nstart.cloud/swagger-ui.html`
|
- **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 JSON:** `https://ordinis.k8s.264.nstart.cloud/v3/api-docs`
|
||||||
- **Spec YAML:** `https://ordinis.k8s.264.nstart.cloud/v3/api-docs.yaml`
|
- **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'ить клиент:
|
Из spec'а можно generate'ить клиент:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ springdoc:
|
|||||||
operations-sorter: method
|
operations-sorter: method
|
||||||
tags-sorter: alpha
|
tags-sorter: alpha
|
||||||
display-request-duration: true
|
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
|
show-actuator: false
|
||||||
packages-to-scan: cloud.nstart.terravault.ordinis.restapi.web
|
packages-to-scan: cloud.nstart.terravault.ordinis.restapi.web
|
||||||
|
|
||||||
|
|||||||
+36
-7
@@ -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.Contact;
|
||||||
import io.swagger.v3.oas.models.info.Info;
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
import io.swagger.v3.oas.models.info.License;
|
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.SecurityRequirement;
|
||||||
import io.swagger.v3.oas.models.security.SecurityScheme;
|
import io.swagger.v3.oas.models.security.SecurityScheme;
|
||||||
import io.swagger.v3.oas.models.servers.Server;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@@ -23,8 +27,15 @@ import java.util.List;
|
|||||||
@Configuration
|
@Configuration
|
||||||
public class OpenApiConfig {
|
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
|
@Bean
|
||||||
public OpenAPI ordinisOpenAPI() {
|
public OpenAPI ordinisOpenAPI() {
|
||||||
|
String authUrl = oauthIssuer + "/protocol/openid-connect/auth";
|
||||||
|
String tokenUrl = oauthIssuer + "/protocol/openid-connect/token";
|
||||||
|
|
||||||
return new OpenAPI()
|
return new OpenAPI()
|
||||||
.info(new Info()
|
.info(new Info()
|
||||||
.title("Ordinis НСИ ЦУОД — Admin / Read API")
|
.title("Ordinis НСИ ЦУОД — Admin / Read API")
|
||||||
@@ -43,28 +54,46 @@ public class OpenApiConfig {
|
|||||||
но с поддержкой `?lang=ru/en` для flatten i18n и `?at=<iso>` для
|
но с поддержкой `?lang=ru/en` для flatten i18n и `?at=<iso>` для
|
||||||
bitemporal queries.
|
bitemporal queries.
|
||||||
|
|
||||||
**Auth:** Bearer JWT от Keycloak (`auth.nstart.space/realms/nstart`).
|
**Авторизация:**
|
||||||
Роль `ordinis-public` достаточна для чтения. На staging авторизация
|
- **Try it out** в Swagger UI — нажми кнопку Authorize → выбери
|
||||||
может быть выключена (`ORDINIS_AUTH_REQUIRED=false`).
|
"oauth2" → Keycloak login flow (PKCE, без client_secret).
|
||||||
|
- **m2m интеграции** — service account через client_credentials
|
||||||
|
flow (см. /docs/integration/api/auth.html).
|
||||||
|
|
||||||
**Документация интеграции:** `docs/integration/altum-imaging-order.md`,
|
**Полная интеграция:** https://ordinis.k8s.264.nstart.cloud/docs/
|
||||||
`docs/tech/api-integration.md`.
|
|
||||||
""")
|
""")
|
||||||
.contact(new Contact()
|
.contact(new Contact()
|
||||||
.name("Ordinis team")
|
.name("Ordinis team")
|
||||||
.email("zimin.an@nstart.space"))
|
.email("zimin.an@nstart.space"))
|
||||||
.license(new License().name("Internal").url("https://nstart.space")))
|
.license(new License().name("Internal").url("https://nstart.space")))
|
||||||
.servers(List.of(
|
.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.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")))
|
new Server().url("http://localhost:8080").description("local dev")))
|
||||||
.components(new Components()
|
.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",
|
.addSecuritySchemes("bearerAuth",
|
||||||
new SecurityScheme()
|
new SecurityScheme()
|
||||||
.type(SecurityScheme.Type.HTTP)
|
.type(SecurityScheme.Type.HTTP)
|
||||||
.scheme("bearer")
|
.scheme("bearer")
|
||||||
.bearerFormat("JWT")
|
.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"));
|
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user