22_identity_providers_federation/
22_identity_providers_federation.rs1use std::sync::Arc;
16use tokio::sync::RwLock;
17use wami::arn::{TenantPath, WamiArn};
18use wami::context::WamiContext;
19use wami::service::IdentityProviderService;
20use wami::store::memory::InMemoryWamiStore;
21use wami::types::Tag;
22use wami::wami::identity::identity_provider::{
23 AddClientIDToOpenIDConnectProviderRequest, CreateOpenIDConnectProviderRequest,
24 CreateSAMLProviderRequest, ListOpenIDConnectProvidersRequest, ListSAMLProvidersRequest,
25 RemoveClientIDFromOpenIDConnectProviderRequest, UpdateOpenIDConnectProviderThumbprintRequest,
26 UpdateSAMLProviderRequest,
27};
28
29#[tokio::main]
30async fn main() -> Result<(), Box<dyn std::error::Error>> {
31 env_logger::init();
32
33 println!("=== WAMI Example 22: Identity Providers for Federation ===\n");
34
35 let store = Arc::new(RwLock::new(InMemoryWamiStore::default()));
37
38 let context = WamiContext::builder()
40 .instance_id("123456789012")
41 .tenant_path(TenantPath::single(0))
42 .caller_arn(
43 WamiArn::builder()
44 .service(wami::arn::Service::Iam)
45 .tenant_path(TenantPath::single(0))
46 .wami_instance("123456789012")
47 .resource("user", "admin")
48 .build()?,
49 )
50 .is_root(false)
51 .build()?;
52
53 let service = IdentityProviderService::new(store.clone());
55
56 println!("๐ Setting up federated authentication with SAML and OIDC providers\n");
57
58 println!("๐ Part 1: SAML Providers (Enterprise Federation)\n");
63
64 println!("Creating Okta SAML provider...");
66 let okta_metadata = r#"<?xml version="1.0" encoding="UTF-8"?>
67<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
68 entityID="http://www.okta.com/exampleid"
69 validUntil="2025-12-31T23:59:59Z">
70 <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
71 <KeyDescriptor use="signing">
72 <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
73 <ds:X509Data>
74 <ds:X509Certificate>MIIDpDCCAoygAwIBAgIGAWk...</ds:X509Certificate>
75 </ds:X509Data>
76 </ds:KeyInfo>
77 </KeyDescriptor>
78 <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
79 Location="https://example.okta.com/app/exampleid/sso/saml"/>
80 </IDPSSODescriptor>
81</EntityDescriptor>"#;
82
83 let okta_request = CreateSAMLProviderRequest {
84 name: "OktaEnterpriseProvider".to_string(),
85 saml_metadata_document: okta_metadata.to_string(),
86 tags: Some(vec![
87 Tag {
88 key: "Environment".to_string(),
89 value: "Production".to_string(),
90 },
91 Tag {
92 key: "IdP".to_string(),
93 value: "Okta".to_string(),
94 },
95 ]),
96 };
97
98 let okta_provider = service.create_saml_provider(&context, okta_request).await?;
99 println!("โ
Created Okta SAML provider: {}", okta_provider.arn);
100 println!(" Name: {}", okta_provider.saml_provider_name);
101 if let Some(valid_until) = okta_provider.valid_until {
102 println!(" Valid until: {}", valid_until);
103 }
104 println!(" Tags: {:?}\n", okta_provider.tags);
105
106 println!("Creating Azure AD SAML provider...");
108 let azure_metadata = r#"<?xml version="1.0" encoding="UTF-8"?>
109<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
110 entityID="https://sts.windows.net/tenant-id/">
111 <IDPSSODescriptor protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
112 <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
113 Location="https://login.microsoftonline.com/tenant-id/saml2"/>
114 </IDPSSODescriptor>
115</EntityDescriptor>"#;
116
117 let azure_request = CreateSAMLProviderRequest {
118 name: "AzureAD-Enterprise".to_string(),
119 saml_metadata_document: azure_metadata.to_string(),
120 tags: Some(vec![Tag {
121 key: "IdP".to_string(),
122 value: "AzureAD".to_string(),
123 }]),
124 };
125
126 let azure_provider = service
127 .create_saml_provider(&context, azure_request)
128 .await?;
129 println!("โ
Created Azure AD SAML provider: {}", azure_provider.arn);
130 println!(" Name: {}\n", azure_provider.saml_provider_name);
131
132 println!("Updating Okta SAML provider metadata (certificate rotation)...");
134 let updated_metadata =
135 okta_metadata.replace("MIIDpDCCAoygAwIBAgIGAWk...", "MIIDnEWCERTIFICATE...");
136 let update_request = UpdateSAMLProviderRequest {
137 arn: okta_provider.arn.clone(),
138 saml_metadata_document: updated_metadata,
139 };
140 let updated_okta = service.update_saml_provider(update_request).await?;
141 println!("โ
Updated Okta SAML provider metadata\n");
142
143 println!("Listing all SAML providers:");
145 let (saml_providers, is_truncated, _) = service
146 .list_saml_providers(ListSAMLProvidersRequest::default())
147 .await?;
148 for (i, provider) in saml_providers.iter().enumerate() {
149 println!(
150 " {}. {} ({})",
151 i + 1,
152 provider.saml_provider_name,
153 provider.arn
154 );
155 }
156 println!(" Total: {} provider(s)", saml_providers.len());
157 if is_truncated {
158 println!(" (more available)");
159 }
160 println!();
161
162 println!("๐ Part 2: OIDC Providers (Modern Federation)\n");
167
168 println!("Creating Google OIDC provider...");
170 let google_request = CreateOpenIDConnectProviderRequest {
171 url: "https://accounts.google.com".to_string(),
172 client_id_list: vec!["1234567890-abcdefghijklmnop.apps.googleusercontent.com".to_string()],
173 thumbprint_list: vec![
174 "c3846bf24b9e93ca64274c0ec67c1ecc5e024ffb".to_string(),
176 ],
177 tags: Some(vec![
178 Tag {
179 key: "IdP".to_string(),
180 value: "Google".to_string(),
181 },
182 Tag {
183 key: "Usage".to_string(),
184 value: "WebApp".to_string(),
185 },
186 ]),
187 };
188
189 let google_provider = service
190 .create_oidc_provider(&context, google_request)
191 .await?;
192 println!("โ
Created Google OIDC provider: {}", google_provider.arn);
193 println!(" URL: {}", google_provider.url);
194 println!(" Client IDs: {:?}", google_provider.client_id_list);
195 println!(" Thumbprints: {:?}\n", google_provider.thumbprint_list);
196
197 println!("Creating Auth0 OIDC provider...");
199 let auth0_request = CreateOpenIDConnectProviderRequest {
200 url: "https://myapp.us.auth0.com".to_string(),
201 client_id_list: vec!["Auth0ClientIdExample123456".to_string()],
202 thumbprint_list: vec!["9e99a48a9960b14926bb7f3b02e22da2b0ab7280".to_string()],
203 tags: Some(vec![Tag {
204 key: "IdP".to_string(),
205 value: "Auth0".to_string(),
206 }]),
207 };
208
209 let auth0_provider = service
210 .create_oidc_provider(&context, auth0_request)
211 .await?;
212 println!("โ
Created Auth0 OIDC provider: {}", auth0_provider.arn);
213 println!(" URL: {}\n", auth0_provider.url);
214
215 println!("๐ง Part 3: Managing OIDC Provider Configuration\n");
220
221 println!("Adding additional client ID to Google provider...");
223 let add_client_request = AddClientIDToOpenIDConnectProviderRequest {
224 arn: google_provider.arn.clone(),
225 client_id: "0987654321-zyxwvutsrqpon.apps.googleusercontent.com".to_string(),
226 };
227 let updated_google = service.add_client_id(add_client_request).await?;
228 println!(
229 "โ
Added client ID. Total client IDs: {}",
230 updated_google.client_id_list.len()
231 );
232 println!(" Client IDs: {:?}\n", updated_google.client_id_list);
233
234 println!("Updating Auth0 thumbprints (certificate rotation)...");
236 let update_thumbprint_request = UpdateOpenIDConnectProviderThumbprintRequest {
237 arn: auth0_provider.arn.clone(),
238 thumbprint_list: vec![
239 "9e99a48a9960b14926bb7f3b02e22da2b0ab7280".to_string(),
240 "a053375bfe84e8b748782c7cee15827a6af5a405".to_string(), ],
242 };
243 let updated_auth0 = service
244 .update_oidc_thumbprints(update_thumbprint_request)
245 .await?;
246 println!(
247 "โ
Updated thumbprints. Total: {}",
248 updated_auth0.thumbprint_list.len()
249 );
250 println!(" Thumbprints: {:?}\n", updated_auth0.thumbprint_list);
251
252 println!("Removing original Google client ID...");
254 let remove_client_request = RemoveClientIDFromOpenIDConnectProviderRequest {
255 arn: google_provider.arn.clone(),
256 client_id: "1234567890-abcdefghijklmnop.apps.googleusercontent.com".to_string(),
257 };
258 let final_google = service.remove_client_id(remove_client_request).await?;
259 println!(
260 "โ
Removed client ID. Remaining client IDs: {}",
261 final_google.client_id_list.len()
262 );
263 println!(" Client IDs: {:?}\n", final_google.client_id_list);
264
265 println!("Listing all OIDC providers:");
267 let (oidc_providers, is_truncated, _) = service
268 .list_oidc_providers(ListOpenIDConnectProvidersRequest::default())
269 .await?;
270 for (i, provider) in oidc_providers.iter().enumerate() {
271 println!(" {}. {} ({})", i + 1, provider.url, provider.arn);
272 }
273 println!(" Total: {} provider(s)", oidc_providers.len());
274 if is_truncated {
275 println!(" (more available)");
276 }
277 println!();
278
279 println!("๐ท๏ธ Part 4: Tagging Identity Providers\n");
284
285 println!("Adding tags to Google OIDC provider...");
287 service
288 .tag_identity_provider(
289 &google_provider.arn,
290 vec![
291 Tag {
292 key: "CostCenter".to_string(),
293 value: "Engineering".to_string(),
294 },
295 Tag {
296 key: "Compliance".to_string(),
297 value: "SOC2".to_string(),
298 },
299 ],
300 )
301 .await?;
302 println!("โ
Added tags\n");
303
304 println!("Listing tags for Google provider:");
306 let tags = service
307 .list_identity_provider_tags(&google_provider.arn)
308 .await?;
309 for tag in &tags {
310 println!(" - {}: {}", tag.key, tag.value);
311 }
312 println!();
313
314 println!("Removing 'Usage' tag...");
316 service
317 .untag_identity_provider(&google_provider.arn, vec!["Usage".to_string()])
318 .await?;
319 println!("โ
Removed tag\n");
320
321 println!("๐ Part 5: Provider Usage Tracking\n");
326
327 let google_final = service.get_oidc_provider(&google_provider.arn).await?;
329 if let Some(provider) = google_final {
330 println!("Google OIDC Provider:");
331 println!(" URL: {}", provider.url);
332 println!(" Usage count: {} principals", provider.usage_count);
333 println!(" Created: {}", provider.create_date);
334 println!(" Tags: {} tag(s)", provider.tags.len());
335 }
336 println!();
337
338 let okta_final = service.get_saml_provider(&updated_okta.arn).await?;
339 if let Some(provider) = okta_final {
340 println!("Okta SAML Provider:");
341 println!(" Name: {}", provider.saml_provider_name);
342 println!(" Usage count: {} principals", provider.usage_count);
343 println!(" Created: {}", provider.create_date);
344 if let Some(valid_until) = provider.valid_until {
345 println!(" Valid until: {}", valid_until);
346 }
347 }
348 println!();
349
350 println!("๐งน Part 6: Cleanup (Optional)\n");
355
356 println!("Note: In production, you'd typically keep identity providers configured.");
357 println!("For this demo, we'll list what we've created:\n");
358
359 let (all_saml, _, _) = service
360 .list_saml_providers(ListSAMLProvidersRequest::default())
361 .await?;
362 let (all_oidc, _, _) = service
363 .list_oidc_providers(ListOpenIDConnectProvidersRequest::default())
364 .await?;
365
366 println!("Summary:");
367 println!(" - {} SAML providers configured", all_saml.len());
368 println!(" - {} OIDC providers configured", all_oidc.len());
369 println!(
370 " - Total: {} identity providers",
371 all_saml.len() + all_oidc.len()
372 );
373 println!();
374
375 println!("=== Example 22 Complete ===");
384 println!();
385 println!("Key Takeaways:");
386 println!(" 1. SAML providers are ideal for enterprise SSO (Okta, Azure AD)");
387 println!(" 2. OIDC providers are perfect for modern web apps (Google, Auth0)");
388 println!(" 3. Both support certificate rotation via update operations");
389 println!(" 4. Tagging helps organize providers by environment, compliance, etc.");
390 println!(" 5. Usage tracking shows which providers are actively used");
391 println!();
392 println!("Next Steps:");
393 println!(" - Configure trust relationships in your roles to use these providers");
394 println!(" - Set up AssumeRoleWithSAML or AssumeRoleWithWebIdentity for authentication");
395 println!(" - Monitor provider usage and update certificates before expiration");
396
397 Ok(())
398}