58 lines
2.1 KiB
Rust
58 lines
2.1 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use actix_web::{HttpResponse, web};
|
|
use serde::Serialize;
|
|
|
|
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
|
use crate::error::AppError;
|
|
use crate::service::AppService;
|
|
use crate::session::Session;
|
|
|
|
/// Response payload for `POST /auth/ws-token`.
|
|
#[derive(Debug, Serialize, utoipa::ToSchema)]
|
|
pub struct WsTokenResponse {
|
|
/// Short-lived JWT prefixed with "Bearer " for use in the Socket.IO CONNECT auth packet.
|
|
pub token: String,
|
|
/// Unix timestamp (seconds) when the token expires.
|
|
pub expires_at: i64,
|
|
}
|
|
|
|
#[utoipa::path(
|
|
post,
|
|
path = "/api/v1/auth/ws-token",
|
|
tag = "Auth",
|
|
operation_id = "authWsToken",
|
|
summary = "Issue a short-lived WebSocket token",
|
|
description = "Issue a short-lived JWT (30 minutes) scoped to IM WebSocket access. \
|
|
The token is signed by the appks signing key and can be verified by imks either \
|
|
locally (via cached signing keys) or via RPC. The returned token should be passed \
|
|
as `{ token: <value> }` in the Socket.IO CONNECT auth packet. Requires an \
|
|
authenticated session.",
|
|
responses(
|
|
(status = 200, description = "Token issued successfully.", body = ApiResponse<WsTokenResponse>),
|
|
(status = 401, description = "The current session is unauthenticated or the login state has expired.", body = ApiErrorResponse),
|
|
(status = 500, description = "Token issuance or Redis write failed.", body = ApiErrorResponse)
|
|
)
|
|
)]
|
|
pub async fn handle(
|
|
service: web::Data<AppService>,
|
|
session: Session,
|
|
) -> Result<HttpResponse, AppError> {
|
|
let user_uid = session.user().ok_or(AppError::Unauthorized)?;
|
|
|
|
let issued = service
|
|
.internal_auth
|
|
.issue_token(
|
|
&user_uid.to_string(),
|
|
1800, // 30-minute TTL (frontend refreshes every 25 min)
|
|
vec!["im:read".into(), "im:write".into()],
|
|
HashMap::new(),
|
|
)
|
|
.await?;
|
|
|
|
Ok(HttpResponse::Ok().json(ApiResponse::new(WsTokenResponse {
|
|
token: format!("Bearer {}", issued.access_token),
|
|
expires_at: issued.expires_at,
|
|
})))
|
|
}
|