1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! OAuth 2.0 Client Credentials Flow example.
//!
//! This example demonstrates automated server-to-server authentication
//! using the OAuth 2.0 Client Credentials grant type. **No browser or
//! user interaction is required** - this is fully automated.
//!
//! # Use Cases
//!
//! - Server applications and backend services
//! - Automated scripts and CLI tools
//! - CI/CD pipelines
//! - Any scenario without human interaction
//!
//! # Setup
//!
//! 1. Create a `.env` file in the project root with:
//! ```env
//! ARCGIS_CLIENT_ID=your_client_id
//! ARCGIS_CLIENT_SECRET=your_client_secret
//! ```
//!
//! 2. Obtain credentials from ArcGIS Developer dashboard:
//! https://developers.arcgis.com/applications
//!
//! # Running
//!
//! ```sh
//! cargo run --example client_credentials_flow
//! ```
//!
//! The example will:
//! 1. Automatically fetch an access token (no browser needed!)
//! 2. Display the token information
//! 3. Demonstrate automatic token caching
use arcgis::example_tracker::ExampleTracker;
use arcgis::{AuthProvider, ClientCredentialsAuth};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Initialize tracing for structured logging
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
// Start accountability tracking
let tracker = ExampleTracker::new("client_credentials_flow")
.methods(&["get_token"])
.service_type("ClientCredentialsAuth")
.start();
tracing::info!("🔐 ArcGIS OAuth 2.0 Client Credentials Flow Example");
tracing::info!("✨ Fully automated - no browser interaction required!");
// Load credentials from .env file (ARCGIS_CLIENT_ID and ARCGIS_CLIENT_SECRET automatically loaded)
tracing::info!("📋 Creating OAuth Client Credentials authenticator from environment");
let auth = ClientCredentialsAuth::from_env()?;
tracing::info!("✅ Authenticator created");
// Verify authenticator was created successfully
// (from_env() returns Result, so reaching this point verifies successful creation)
// 1. Get access token (fetched automatically on first use)
tracing::info!("🔑 Fetching access token");
let token = auth.get_token().await?;
// Verify token was retrieved
assert!(!token.is_empty(), "Access token should not be empty");
assert!(
token.len() > 100,
"Access token should be substantial (>100 chars), got {} chars",
token.len()
);
tracing::info!(
token_preview = %&token[..20.min(token.len())],
"✅ Access token obtained"
);
// 2. Get token again (should return cached token)
tracing::info!("🔄 Getting token again (should use cache)");
let token2 = auth.get_token().await?;
let tokens_match = token == token2;
// Verify token caching works
assert!(
tokens_match,
"Second token request should return cached token (tokens should match)"
);
assert!(!token2.is_empty(), "Cached token should not be empty");
tracing::info!(tokens_match = tokens_match, "✅ Token retrieved from cache");
// 3. Show token info
tracing::info!("📊 Token Information:");
tracing::info!(
token_length = token.len(),
token_type = "Bearer",
lifetime = "~2 hours",
"Token details"
);
tracing::info!("🎉 Authentication successful!");
tracing::info!("💡 The ClientCredentialsAuth is now authenticated and can be");
tracing::info!(" used with ArcGISClient to make authenticated API requests");
tracing::info!("📝 Token will automatically refresh when it expires");
tracing::info!(" No manual token management required!");
tracing::info!("🚀 Example usage:");
tracing::info!(" let client = ArcGISClient::new(auth);");
tracing::info!(" // All API calls automatically use refreshed tokens");
// Mark tracking as successful
tracker.success();
Ok(())
}