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
//! GCP auth provides authentication using service accounts Google Cloud Platform (GCP)
//! 
//! The library can be used in two ways:
//! 
//! - Invoking the library inside GCP environment fetches the default service account for the service and
//! the application is authenticated using that particular account
//! - Providing a path to service account JSON configuration file using GOOGLE_APPLICATION_CREDENTIALS environment
//! variable. The service account configuration file can be downloaded in the IAM service when displaying service account detail.
//! The downloaded JSON file should be provided without any further modification.
//! 
//! The tokens are single-use and as such they shouldn't be cached and for each use a new token should be requested.
//! Library handles token caching for their lifetime and so it won't make a request if a token with appropriate scope
//! is available.
//! 
//! # Default service account
//! 
//! When running inside GCP the library can be asked directly without any further configuration to provide a Bearer token
//! for the current service account of the service.
//! 
//! ```async
//! let authentication_manager = gcp_auth::init().await?;
//! let token = authentication_manager.get_token().await?;
//! ```
//! 
//! # Custom service account
//! 
//! When running outside of GCP e.g on development laptop to allow finer granularity for permission a
//! custom service account can be used. To use a custom service account a configuration file containing key
//! has to be downloaded in IAM service for the service account you intend to use. The configuration file has to
//! be available to the application at run time. The path to the configuration file is specified by 
//! `GOOGLE_APPLICATION_CREDENTIALS` environment variable.
//! 
//! ```async
//! // GOOGLE_APPLICATION_CREDENTIALS environtment variable is set-up
//! let authentication_manager = gcp_auth::init().await?;
//! let token = authentication_manager.get_token().await?;
//! ```

#![deny(missing_docs)]
#![deny(warnings)]
#![allow(clippy::pedantic)]

mod error;
mod authentication_manager;
mod jwt;
mod types;
mod default_service_account;
mod default_authorized_user;
mod custom_service_account;
mod prelude {
    pub(crate) use {
        crate::types::HyperClient, crate::types::Token,
        serde::Deserialize, serde::Serialize, std::collections::HashMap,
        std::path::Path, crate::error::GCPAuthError, hyper::Request, bytes::buf::Buf, async_trait::async_trait
    };
}
pub use authentication_manager::AuthenticationManager;
pub use types::Token;
pub use error::GCPAuthError;

use hyper::Client;
use hyper_tls::HttpsConnector;
use tokio::sync::Mutex;

/// Initialize GCP authentication
/// 
/// Returns `AuthenticationManager` which can be used to obtain tokens
pub async fn init() -> Result<AuthenticationManager, GCPAuthError> {
    let https = HttpsConnector::new();
    let client = Client::builder().build::<_, hyper::Body>(https);

    let default = default_service_account::DefaultServiceAccount::new(&client).await;
    if let Ok(service_account) = default {
        return Ok(AuthenticationManager {
            client: client.clone(),
            service_account: Mutex::new(Box::new(service_account)),
        });
    }
    let custom = custom_service_account::CustomServiceAccount::new().await;
    if let Ok(service_account) = custom {
        return Ok(AuthenticationManager {
            client,
            service_account: Mutex::new(Box::new(service_account)),
        });
    }
    let user = default_authorized_user::DefaultAuthorizedUser::new(&client).await;
    if let Ok(user_account) = user {
        return Ok(AuthenticationManager {
            client,
            service_account: Mutex::new(Box::new(user_account)),
        });
    }
    Err(GCPAuthError::NoAuthMethod(Box::new(custom.unwrap_err()), Box::new(default.unwrap_err())))
}