Skip to main content

rustauth_plugins/api_key/routes/
delete.rs

1use http::{Method, StatusCode};
2use serde::{Deserialize, Serialize};
3
4use super::{
5    body, config_id_matches, current_identity, endpoint, error, json, SharedConfigurations,
6};
7use crate::api_key::errors;
8use crate::api_key::options::ApiKeyReference;
9use crate::api_key::organization::{ensure_organization_permission, owns_user_key, ApiKeyAction};
10use crate::api_key::storage::ApiKeyStore;
11
12#[derive(Debug, Clone, Deserialize, Serialize, Default)]
13#[serde(rename_all = "camelCase")]
14pub struct DeleteApiKeyRequest {
15    pub key_id: String,
16    pub config_id: Option<String>,
17}
18
19#[derive(Debug, Clone, Serialize)]
20struct DeleteApiKeyResponse {
21    success: bool,
22}
23
24pub fn delete_endpoint(
25    configurations: SharedConfigurations,
26) -> rustauth_core::api::AsyncAuthEndpoint {
27    endpoint(
28        "/api-key/delete",
29        Method::POST,
30        configurations,
31        |context, request, configurations| async move {
32            let input: DeleteApiKeyRequest = body(&request)?;
33            let options = configurations.resolve(input.config_id.as_deref())?;
34            let Some(identity) = current_identity(&context, &request).await? else {
35                return error(StatusCode::UNAUTHORIZED, errors::UNAUTHORIZED_SESSION);
36            };
37            let store = ApiKeyStore::new(&context, &options);
38            let Some(api_key) = store.get_by_id(&input.key_id).await? else {
39                return error(StatusCode::NOT_FOUND, errors::KEY_NOT_FOUND);
40            };
41            let expected_config_id = options.config_id.as_deref().unwrap_or("default");
42            if !config_id_matches(&api_key.config_id, expected_config_id) {
43                return error(StatusCode::NOT_FOUND, errors::KEY_NOT_FOUND);
44            }
45            match options.reference {
46                ApiKeyReference::User
47                    if owns_user_key(
48                        options.reference,
49                        &api_key.reference_id,
50                        &identity.user.id,
51                    ) => {}
52                ApiKeyReference::User => {
53                    return error(StatusCode::NOT_FOUND, errors::KEY_NOT_FOUND)
54                }
55                ApiKeyReference::Organization => {
56                    if let Err(error) = ensure_organization_permission(
57                        &context,
58                        &identity.user.id,
59                        &api_key.reference_id,
60                        ApiKeyAction::Delete,
61                    )
62                    .await
63                    {
64                        return error_response_from_rustauth(error);
65                    }
66                }
67            }
68            store.delete(&api_key).await?;
69            json(StatusCode::OK, &DeleteApiKeyResponse { success: true })
70        },
71    )
72}
73
74fn error_response_from_rustauth(
75    error: rustauth_core::error::RustAuthError,
76) -> Result<rustauth_core::api::ApiResponse, rustauth_core::error::RustAuthError> {
77    let message = error.to_string();
78    if message.contains(errors::message(errors::USER_NOT_MEMBER_OF_ORGANIZATION)) {
79        return super::error(
80            StatusCode::FORBIDDEN,
81            errors::USER_NOT_MEMBER_OF_ORGANIZATION,
82        );
83    }
84    if message.contains(errors::message(errors::ORGANIZATION_PLUGIN_REQUIRED)) {
85        return super::error(
86            StatusCode::INTERNAL_SERVER_ERROR,
87            errors::ORGANIZATION_PLUGIN_REQUIRED,
88        );
89    }
90    super::error(
91        StatusCode::FORBIDDEN,
92        errors::INSUFFICIENT_API_KEY_PERMISSIONS,
93    )
94}