Skip to content
Clone
e870254
74 lines 3.1 KB Rust
use crate::session::{RequestId, Session};
use actix_web::web;
use crate::envelope::{Envelope, NoData};
use crate::server::AuthSvc;
use crate::server::app_token::types::{AppTokenRequest, AppTokenResponse};
use crate::server::errors::AuthError;
#[utoipa::path(
post,
path = "/api/v1/oauth/app-tokens",
tag = "auth",
operation_id = "auth_app_token_issue",
summary = "签发内部应用令牌",
description = "认证中心用 Ed25519 私钥签发 JWT(`alg=EdDSA`),`aud` 为目标 app 的 audience,`client_id` 为 app 标识。任何服务只需 `/.well-known/jwks.json` 的公钥即可本地验签。`ttl_seconds` 取值 1 ~ 3600,不填或填 0 用默认 900。会话未绑定账号返回 `unauthenticated`(401);app 未在 `apps` 清单里返回 `app_not_found`(404);私钥未配置返回 `unavailable`(503)。",
request_body = AppTokenRequest,
responses(
(
status = 200,
description = "令牌已签发,`token` 是 EdDSA JWT",
body = Envelope<AppTokenResponse>,
example = json!({"code":200,"message":"success","data":{"app_id":"api","audience":"api","key_id":"app-local-1","token":"eyJhbGciOiJFZERTQSIsImtpZCI6ImsxIiwidHlwIjoiSldUIn0.…","issued_at":"2026-01-02T03:04:05Z","expires_at":"2026-01-02T03:19:05Z"}})
),
(
status = 400,
description = "app_id 为空或 ttl_seconds 超出上限(`malformed_request`、`invalid_expiry`)",
body = Envelope<NoData>,
example = json!({"code": "invalid_expiry", "message": "expires_at is invalid"})
),
(
status = 401,
description = "会话未绑定账号(`unauthenticated`)",
body = Envelope<NoData>,
example = json!({"code": "unauthenticated", "message": "authentication required"})
),
(
status = 404,
description = "app_id 不在认证中心的 `apps` 清单里(`app_not_found`)",
body = Envelope<NoData>,
example = json!({"code": "app_not_found", "message": "app does not exist"})
),
(
status = 503,
description = "签名密钥未配置(`unavailable`)",
body = Envelope<NoData>,
example = json!({"code": "unavailable", "message": "dependency is unavailable: jwt.private_key is required to issue tokens"})
),
(
status = 500,
description = "服务端故障(`internal`、`serialization`)",
body = Envelope<NoData>,
example = json!({"code": "internal", "message": "internal server error"})
)
)
)]
pub async fn app_token_issue(
svc: web::Data<AuthSvc>,
session: Session,
request_id: RequestId,
body: web::Json<AppTokenRequest>,
) -> Result<Envelope<AppTokenResponse>, AuthError> {
let user_id = svc
.require_session(&session, Some(request_id.as_str()))
.await?;
let response = svc
.issue_app_token(
&session,
user_id,
body.into_inner(),
Some(request_id.as_str()),
)
.await?;
Ok(Envelope::ok(response))
}