Open stevemk14ebr opened 9 months ago
can you try just placing the source_credentials
object in the credentials file? You will see that it expects different fields than the top-level ones in your example.
@dermesser
Also not working for me (Failed to authenticate: JSONError(Error("missing field `accessToken`", line: 17, column: 1))
). The documentation is really not clear at all. Despite hours of trying, I'm unable to connect in Impersonation to my service account just to view and add events to my calendar into my principal account.
It would be really great if you can provide a fully working example from A to Z, so the good way to do it. Thank you in advance, it helps a lot.
gcloud auth application-default login --impersonate-service-account calendar@homelab-422502.iam.gserviceaccount.com
Result should looks like that :
{
"delegates": [],
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/calendar@homelab-422502.iam.gserviceaccount.com:generateAccessToken",
"source_credentials": {
"account": "",
"client_id": "<>.apps.googleusercontent.com",
"client_secret": "<>",
"refresh_token": "<>",
"type": "authorized_user",
"universe_domain": "googleapis.com"
},
"type": "impersonated_service_account"
}
let user_secret =
read_authorized_user_secret("private/service-account-credentials.json").await?;
let email = "calendar@homelab-422502.iam.gserviceaccount.com";
info!("User secret: {:?}", user_secret);
let auth = ServiceAccountImpersonationAuthenticator::builder(user_secret.clone(), email)
.build()
.await
.expect("authenticator");
let scopes = &["https://www.googleapis.com/auth/calendar"];
let token = match auth.token(scopes).await {
Ok(token) => token,
Err(e) => {
error!("Failed to authenticate: {:?}", e);
process::exit(1);
}
};
info!("Token: {:?}", token);
let hub = CalendarHub::new(
hyper::Client::builder().build(
hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
.https_only()
.enable_http2()
.build(),
),
auth,
);
let calendar = hub.calendar_list().list().doit().await?;
info!("Calendar ID: {:?}", calendar.1);
Tried following your comment about source_credentials
private/service-account-credentials.json:
{
"account": "",
"client_id": "<>.apps.googleusercontent.com",
"client_secret": "<Redacted>",
"refresh_token": "<Redacted>",
"type": "authorized_user", // also tried with impersonated_service_account
"universe_domain": "googleapis.com"
}
Seems similar to https://github.com/firebase/firebase-admin-node/issues/1861 like ImpersonatedServiceAccount type needs implemented. Otherwise seems similar to AuthorizedUserAuthenticator
I finally found a workaround. First go to your service account in GCP and under permissions click grant access
Add your personal user account as a principal to the service account with the service token creator role:
Wait a few minutes. And then you can do this:
gcloud auth application-default login
And in rust use those crediantials while impersonating by this:
async fn get_user_impersonation_auth() -> Result<Authenticator<HttpsConnector<HttpConnector>>> {
let home_dir = env::var("HOME")?;
// Construct the full path
let path = format!("{}/{}", home_dir, ".config/gcloud/application_default_credentials.json");
// Continue with the rest of your code
let secret = oauth2::read_authorized_user_secret(&path).await.unwrap();
let service_account = "my_service_account@blah.iam.gserviceaccount.com";
let authenticator = oauth2::ServiceAccountImpersonationAuthenticator::builder(secret, service_account).build().await?;
Ok(authenticator)
}
Executing:
With the code:
Results in:
This is unexpected, as the client_id value is present
this service_account_impersonation example also fails with this credential but with the error:
At the time of use instead.
This is important as this gcloud command is the recommended way to do local development without code changes. A user is expected to impersonate as a service account and the application will then behave as if it is live in production actually using said service account.