Skip to main content

raps_cli/mcp/
auth_guidance.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2024-2025 Dmytro Yemelianov
3
4//! Authentication guidance module for MCP server
5//!
6//! Provides structured guidance content, tool-auth mappings, and helper functions
7//! for native authentication support in the MCP server.
8//!
9//! Items in this module are pre-built for integration into the MCP auth flow.
10
11use raps_kernel::auth::AuthClient;
12use raps_kernel::config::Config;
13
14/// Authentication requirement for MCP tools
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum AuthRequirement {
17    /// Requires 2-legged OAuth (client credentials)
18    TwoLegged,
19    /// Requires 3-legged OAuth (user authorization)
20    ThreeLegged,
21    /// Works with either auth type
22    Either,
23}
24
25/// Current authentication state
26#[derive(Debug, Clone)]
27pub struct AuthState {
28    /// Whether APS_CLIENT_ID is configured
29    pub has_client_id: bool,
30    /// Whether APS_CLIENT_SECRET is configured
31    pub has_client_secret: bool,
32    /// Whether 2-legged auth succeeds
33    pub two_legged_valid: bool,
34    /// Whether 3-legged token exists and is valid
35    pub three_legged_valid: bool,
36    /// Whether 3-legged token exists but is expired
37    pub three_legged_expired: bool,
38}
39
40impl AuthState {
41    /// Check if any credentials are configured
42    pub fn has_any_credentials(&self) -> bool {
43        self.has_client_id || self.has_client_secret
44    }
45
46    /// Check if 2-legged credentials are complete
47    pub fn has_complete_2leg_credentials(&self) -> bool {
48        self.has_client_id && self.has_client_secret
49    }
50}
51
52// ============================================================================
53// Instruction Constants
54// ============================================================================
55
56/// Full onboarding guide for first-time users
57pub const SETUP_INSTRUCTIONS: &str = r#"
58To set up authentication for RAPS MCP Server:
59
601. Go to https://aps.autodesk.com/ (Autodesk Platform Services)
612. Sign in or create an Autodesk account
623. Create a new application or select an existing one
634. Copy your Client ID and Client Secret
645. Set environment variables before starting the MCP server:
65
66   For Unix/macOS:
67     export APS_CLIENT_ID="your_client_id"
68     export APS_CLIENT_SECRET="your_client_secret"
69
70   For Windows (PowerShell):
71     $env:APS_CLIENT_ID="your_client_id"
72     $env:APS_CLIENT_SECRET="your_client_secret"
73
74   Or add to your MCP server configuration (claude_desktop_config.json):
75     {
76       "mcpServers": {
77         "raps": {
78           "command": "raps",
79           "args": ["serve"],
80           "env": {
81             "APS_CLIENT_ID": "your_client_id",
82             "APS_CLIENT_SECRET": "your_client_secret"
83           }
84         }
85       }
86     }
87
88For more information: https://rapscli.xyz/docs/auth
89"#;
90
91/// Missing client ID guidance
92pub const MISSING_CLIENT_ID: &str = r#"
93Missing: APS_CLIENT_ID environment variable
94
95The Client ID is required for all APS operations. To get your Client ID:
961. Go to https://aps.autodesk.com/
972. Navigate to your application's settings
983. Copy the "Client ID" value
994. Set APS_CLIENT_ID environment variable
100"#;
101
102/// Missing client secret guidance
103pub const MISSING_CLIENT_SECRET: &str = r#"
104Missing: APS_CLIENT_SECRET environment variable
105
106The Client Secret is required for authentication. To get your Client Secret:
1071. Go to https://aps.autodesk.com/
1082. Navigate to your application's settings
1093. Copy or regenerate the "Client Secret" value
1104. Set APS_CLIENT_SECRET environment variable
111
112Note: Keep your Client Secret secure and never share it publicly.
113"#;
114
115/// Prompt to perform 3-legged authentication
116pub const THREE_LEGGED_PROMPT: &str = r#"
117To access BIM 360/ACC data (hubs, projects, folders, files, issues, RFIs), you need to log in with your Autodesk account.
118
119Use the `auth_login` tool to start the authentication process, or run:
120  raps auth login
121
122This will open a browser window for you to sign in with your Autodesk credentials.
123"#;
124
125/// Tool availability summary header
126pub const TOOL_AVAILABILITY_HEADER: &str = "\nTool Availability:\n";
127
128// ============================================================================
129// Tool-Auth Mapping
130// ============================================================================
131
132/// Get the authentication requirement for a tool
133pub fn get_tool_auth_requirement(tool_name: &str) -> AuthRequirement {
134    match tool_name {
135        // Auth tools - work with either
136        "auth_test" | "auth_status" | "auth_login" | "auth_logout" => AuthRequirement::Either,
137
138        // OSS tools - 2-legged only (including v4.4 upload/download/copy)
139        "bucket_list"
140        | "bucket_create"
141        | "bucket_get"
142        | "bucket_delete"
143        | "object_list"
144        | "object_delete"
145        | "object_signed_url"
146        | "object_urn"
147        | "object_upload"
148        | "object_upload_batch"
149        | "object_download"
150        | "object_info"
151        | "object_copy"
152        | "object_delete_batch" => AuthRequirement::TwoLegged,
153
154        // Derivative tools - 2-legged only
155        "translate_start" | "translate_status" => AuthRequirement::TwoLegged,
156
157        // Admin tools - 2-legged (account-level)
158        "admin_project_list"
159        | "admin_operation_list"
160        | "admin_operation_status"
161        | "admin_operation_resume"
162        | "admin_operation_cancel" => AuthRequirement::TwoLegged,
163
164        // Admin user tools - 2-legged
165        "admin_user_add"
166        | "admin_user_remove"
167        | "admin_user_update_role"
168        | "admin_folder_rights" => AuthRequirement::TwoLegged,
169
170        // Data Management tools - 3-legged required
171        "hub_list" | "project_list" | "folder_list" | "folder_create" | "item_info"
172        | "item_versions" => AuthRequirement::ThreeLegged,
173
174        // Project Management tools (v4.4) - 3-legged required
175        "project_info" | "project_users_list" | "folder_contents" => AuthRequirement::ThreeLegged,
176
177        // ACC Project Admin tools (v4.4) - 3-legged required
178        "project_create" | "project_user_add" | "project_users_import" => {
179            AuthRequirement::ThreeLegged
180        }
181
182        // Item Management tools (v4.4) - 3-legged required
183        "item_create" | "item_delete" | "item_rename" => AuthRequirement::ThreeLegged,
184
185        // ACC/BIM 360 tools - 3-legged required
186        "issue_list"
187        | "issue_get"
188        | "issue_create"
189        | "issue_update"
190        | "rfi_list"
191        | "rfi_get"
192        | "rfi_create"
193        | "rfi_update"
194        | "acc_assets_list"
195        | "asset_create"
196        | "asset_update"
197        | "asset_delete"
198        | "acc_submittals_list"
199        | "submittal_create"
200        | "submittal_update"
201        | "acc_checklists_list"
202        | "checklist_create"
203        | "checklist_update" => AuthRequirement::ThreeLegged,
204
205        // Default to 2-legged for unknown tools
206        _ => AuthRequirement::TwoLegged,
207    }
208}
209
210// ============================================================================
211// Auth State Helper
212// ============================================================================
213
214/// Compute current authentication state from config and auth client
215pub async fn get_auth_state(config: &Config, auth_client: &AuthClient) -> AuthState {
216    let has_client_id = !config.client_id.is_empty();
217    let has_client_secret = !config.client_secret.is_empty();
218
219    // Check 2-legged validity
220    let two_legged_valid = if has_client_id && has_client_secret {
221        auth_client.get_token().await.is_ok()
222    } else {
223        false
224    };
225
226    // Check 3-legged validity
227    let (three_legged_valid, three_legged_expired) = match auth_client.get_3leg_token().await {
228        Ok(_) => (true, false),
229        Err(e) => {
230            let err_msg = e.to_string().to_lowercase();
231            if err_msg.contains("expired") || err_msg.contains("refresh") {
232                (false, true)
233            } else {
234                (false, false)
235            }
236        }
237    };
238
239    AuthState {
240        has_client_id,
241        has_client_secret,
242        two_legged_valid,
243        three_legged_valid,
244        three_legged_expired,
245    }
246}
247
248// ============================================================================
249// Error Guidance
250// ============================================================================
251
252/// Convert an authentication error into user-friendly guidance
253pub fn format_error_guidance(error: &str) -> String {
254    let error_lower = error.to_lowercase();
255
256    if error_lower.contains("client_id") || error_lower.contains("aps_client_id") {
257        return format!(
258            "Authentication Error: Missing Client ID\n{}",
259            MISSING_CLIENT_ID
260        );
261    }
262
263    if error_lower.contains("client_secret") || error_lower.contains("aps_client_secret") {
264        return format!(
265            "Authentication Error: Missing Client Secret\n{}",
266            MISSING_CLIENT_SECRET
267        );
268    }
269
270    if error_lower.contains("401") || error_lower.contains("unauthorized") {
271        return format!(
272            r#"Authentication Error: Invalid Credentials
273
274Your credentials were rejected by Autodesk. Troubleshooting steps:
2751. Verify APS_CLIENT_ID is correct (typically 32 characters)
2762. Verify APS_CLIENT_SECRET is correct (typically 64 characters)
2773. Check that your app is active at https://aps.autodesk.com/
2784. Ensure credentials are for the correct environment (production vs sandbox)
279
280Error details: {}
281"#,
282            error
283        );
284    }
285
286    if error_lower.contains("network")
287        || error_lower.contains("connection")
288        || error_lower.contains("timeout")
289    {
290        return format!(
291            r#"Authentication Error: Network Issue
292
293Unable to reach Autodesk authentication servers. Troubleshooting steps:
2941. Check your internet connection
2952. Verify firewall settings allow access to *.autodesk.com
2963. Try again in a few moments
297
298Error details: {}
299"#,
300            error
301        );
302    }
303
304    if error_lower.contains("expired") || error_lower.contains("refresh") {
305        return format!(
306            r#"Authentication Error: Token Expired
307
308Your 3-legged authentication token has expired. To re-authenticate:
3091. Use the `auth_login` tool, or
3102. Run `raps auth login` in your terminal
311
312Error details: {}
313"#,
314            error
315        );
316    }
317
318    // Generic fallback
319    format!(
320        r#"Authentication Error
321
322An error occurred during authentication. Please check your setup:
3231. Verify APS_CLIENT_ID and APS_CLIENT_SECRET are set correctly
3242. Check your network connection
3253. Visit https://rapscli.xyz/docs/troubleshooting for more help
326
327Error details: {}
328"#,
329        error
330    )
331}
332
333// ============================================================================
334// Tool Availability Summary
335// ============================================================================
336
337/// Get a summary of tool availability based on auth state
338pub fn get_tool_availability_summary(state: &AuthState) -> String {
339    let mut summary = String::from(TOOL_AVAILABILITY_HEADER);
340
341    // OSS tools
342    if state.two_legged_valid {
343        summary.push_str("  ✓ OSS (bucket_*, object_*) - available\n");
344    } else {
345        summary.push_str("  ✗ OSS (bucket_*, object_*) - requires 2-legged auth\n");
346    }
347
348    // Derivative tools
349    if state.two_legged_valid {
350        summary.push_str("  ✓ Derivative (translate_*) - available\n");
351    } else {
352        summary.push_str("  ✗ Derivative (translate_*) - requires 2-legged auth\n");
353    }
354
355    // Admin tools
356    if state.two_legged_valid {
357        summary.push_str("  ✓ Admin (admin_*) - available\n");
358    } else {
359        summary.push_str("  ✗ Admin (admin_*) - requires 2-legged auth\n");
360    }
361
362    // Data Management tools
363    if state.three_legged_valid {
364        summary.push_str("  ✓ Data Management (hub_*, project_*, folder_*, item_*) - available\n");
365    } else {
366        summary.push_str(
367            "  ✗ Data Management (hub_*, project_*, folder_*, item_*) - requires 3-legged auth\n",
368        );
369    }
370
371    // ACC tools
372    if state.three_legged_valid {
373        summary.push_str("  ✓ ACC (issue_*, rfi_*, acc_*) - available\n");
374    } else {
375        summary.push_str("  ✗ ACC (issue_*, rfi_*, acc_*) - requires 3-legged auth\n");
376    }
377
378    summary
379}