cat_login.rs 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. use std::path::Path;
  2. use anyhow::{anyhow, Result};
  3. use cdk::mint_url::MintUrl;
  4. use cdk::nuts::MintInfo;
  5. use cdk::wallet::MultiMintWallet;
  6. use cdk::OidcClient;
  7. use clap::Args;
  8. use serde::{Deserialize, Serialize};
  9. use crate::token_storage;
  10. #[derive(Args, Serialize, Deserialize)]
  11. pub struct CatLoginSubCommand {
  12. /// Mint url
  13. mint_url: MintUrl,
  14. /// Username
  15. username: String,
  16. /// Password
  17. password: String,
  18. }
  19. pub async fn cat_login(
  20. multi_mint_wallet: &MultiMintWallet,
  21. sub_command_args: &CatLoginSubCommand,
  22. work_dir: &Path,
  23. ) -> Result<()> {
  24. let mint_url = sub_command_args.mint_url.clone();
  25. // Ensure the mint exists
  26. if !multi_mint_wallet.has_mint(&mint_url).await {
  27. multi_mint_wallet.add_mint(mint_url.clone()).await?;
  28. }
  29. let mint_info = multi_mint_wallet
  30. .fetch_mint_info(&mint_url)
  31. .await?
  32. .ok_or(anyhow!("Mint info not found"))?;
  33. let (access_token, refresh_token) = get_access_token(
  34. &mint_info,
  35. &sub_command_args.username,
  36. &sub_command_args.password,
  37. )
  38. .await;
  39. // Save tokens to file in work directory
  40. if let Err(e) =
  41. token_storage::save_tokens(work_dir, &mint_url, &access_token, &refresh_token).await
  42. {
  43. println!("Warning: Failed to save tokens to file: {e}");
  44. } else {
  45. println!("Tokens saved to work directory");
  46. }
  47. println!("\nAuthentication successful! 🎉\n");
  48. println!("\nYour tokens:");
  49. println!("access_token: {access_token}");
  50. println!("refresh_token: {refresh_token}");
  51. Ok(())
  52. }
  53. async fn get_access_token(mint_info: &MintInfo, user: &str, password: &str) -> (String, String) {
  54. let openid_discovery = mint_info
  55. .nuts
  56. .nut21
  57. .clone()
  58. .expect("Nut21 defined")
  59. .openid_discovery;
  60. let client_id = mint_info
  61. .nuts
  62. .nut21
  63. .clone()
  64. .expect("Nut21 defined")
  65. .client_id;
  66. let oidc_client = OidcClient::new(openid_discovery, None);
  67. // Get the token endpoint from the OIDC configuration
  68. let token_url = oidc_client
  69. .get_oidc_config()
  70. .await
  71. .expect("Failed to get OIDC config")
  72. .token_endpoint;
  73. // Create the request parameters
  74. let params = [
  75. ("grant_type", "password"),
  76. ("client_id", &client_id),
  77. ("scope", "openid offline_access"),
  78. ("username", user),
  79. ("password", password),
  80. ];
  81. // Make the token request directly
  82. let client = reqwest::Client::new();
  83. let response = client
  84. .post(token_url)
  85. .form(&params)
  86. .send()
  87. .await
  88. .expect("Failed to send token request");
  89. let token_response: serde_json::Value = response
  90. .json()
  91. .await
  92. .expect("Failed to parse token response");
  93. let access_token = token_response["access_token"]
  94. .as_str()
  95. .expect("No access token in response")
  96. .to_string();
  97. let refresh_token = token_response["refresh_token"]
  98. .as_str()
  99. .expect("No refresh token in response")
  100. .to_string();
  101. (access_token, refresh_token)
  102. }