Service account delegation with Google workspace - Entity Not found - c#

I am trying to access the Google Directory api as a test this method domains.list using a service account.
If I use the try me on that page logging in with my domain admin email. It works and I get a response back. So the method and the customer id i am passing should be working.
I followed the instructions here Perform Google Workspace Domain-Wide Delegation of Authority to create a service account and enable domain wide delegation.
If I check both my workspace account and Google cloud console. The delegation appears to be configured. with my domain admin email set as principle.
My code:
namespace Daimto.Sample.WorkspaceAdmin
{
class Program
{
private static readonly string[] Scopes = {DirectoryService.Scope.AdminDirectoryDomain};
private static readonly string PathToServiceAccountKeyFile = #"C:\YouTube\workspaceserviceaccount-e4823a933ae3.json";
private static readonly string CustomerId = "C01lp3chxa";
private static readonly string workspaceAdmin = "xxx#daimto.com";
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
var credential = LoadGoogleCredentails();
var service = CreateDirectoryService(credential);
var request = service.Domains.List(CustomerId);
var result = request.Execute();
foreach (var domain in result.Domains)
{
Console.WriteLine(domain.DomainName);
}
}
private static DirectoryService CreateDirectoryService(GoogleCredential credential)
{
return new (new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Daimto Testing Workspace with service account"
}
);
}
private static GoogleCredential LoadGoogleCredentails()
{
return GoogleCredential.FromFile(PathToServiceAccountKeyFile)
.Impersonate(new ImpersonatedCredential.Initializer(workspaceAdmin))
.CreateScoped(Scopes);
}
}
}
error
Running code as it appears above.
"error": {
"code": 404,
"message": "Requested entity was not found.",
"errors": [
{
"message": "Requested entity was not found.",
"domain": "global",
"reason": "notFound"
}
Not found implies to me that its not even able to access the domain.
However if I remove the line for impersonation. Then I get this error
Not Authorized to access this resource/api [403]
Errors [
Message[Not Authorized to access this resource/api] Location[ - ] Reason[forbidden] Domain[global]
]
This implies to me that without the impersonation it doesn't have access.
So I am confused. With impersonation it cant find it, without impersonation it can find it but doesn't have access?
The only clue I can find in the documentation is this started note.
So what exactly do they mean by this Only users with access to the Admin APIs can access the Admin SDK Directory API I am admin shouldn't i have access? Should access be configured if so where?

Ok this required a lot of digging.
There are two methods in the Google .Net client library Impersonate() and CreateWithUser()
Impersonate Allows this credential to impersonate the ImpersonatedCredential.Initializer.TargetPrincipal. Only ServiceAccountCredentialand UserCredential support impersonation, so this method will throw <see InvalidOperationException if this credential's
UnderlyingCredential is not of one of those supported types.
while
CreateWithUser If the credential supports Domain Wide Delegation, this method creates a copy of the credential with the specified user. Otherwise, it throws <see InvalidOperationException. At the moment only ServiceAccountCredential supports Domain Wide Delegation.
So the key here is are you trying to delegate to a user or impersonate a user. There is a difference.
So by using CreateWithUser it now works.
private static GoogleCredential LoadGoogleCredentails()
{
return GoogleCredential.FromFile(PathToServiceAccountKeyFile)
.CreateScoped(Scopes)
.CreateWithUser(workspaceAdmin);
}

Related

Why do I get 401 Unauthorized when granting access via Azure AD App-Only?

I followed this article but I modified the sample code a bit to use GetContextAsync instead of GetAzureADAppOnlyAuthenticatedContext, I get the error "The remote server returned an error: (401) Unauthorized." every single time.
AuthenticationManager authManager = new AuthenticationManager(clientId, certPath,certPassword,tenantId);
using (ClientContext cc = await authManager.GetContextAsync(_siteUrl))
{
cc.Load(cc.Web, p => p.Title);
await cc.ExecuteQueryAsync();
Console.WriteLine(cc.Web.Title);
}
The error is thrown at await cc.ExecuteQueryAsync();
I have uploaded the self-signed certificate onto Azure portal
and granted the permission
My app is a Winforms app using .NET framework 4.7
PnP.Framework 1.11
P/s: What do I enter for the tenantId param? At the moment I'm using the Directory (tenant) ID from the Overview page
I have tried to reproduce the same in my environment.
Created self signed certificate
It is then being uploaded to my app registration
Checked the manifest to confirm
"keyCredentials": [
{
"customKeyIdentifier": "xxx",
"endDate": "2023-01-31T00:00:00Z",
"keyId": "xxx",
"startDate": "2023-01-23T00:00:00Z",
"type": "AsymmetricX509Cert",
"usage": "Verify",
"value": "xxx",
"displayName": "my new ssc"
}
using postman
code given:
using System;
using PnP.Framework;
//using OfficeDevPnP.Core;
//using Microsoft.SharePoint;
//using Microsoft.SharePoint.Client;
using System.Threading.Tasks;
namespace spoapprepo
{
class Program
{
static async void Main(string[] args)
{
var clientId = "xxx";
var certPath = "C:\\xxx\\selfsigned.pfx";
var certPassword = "xxx";
var tenantId = "xxx";
var siteUrl= "https://contoso.sharepoint.com";
AuthenticationManager authManager = new AuthenticationManager(clientId, certPath, certPassword, tenantId);
Try{
using (var cc = await authManager.GetContextAsync(siteUrl))
{
cc.Load(cc.Web, p => p.Title);
await cc.ExecuteQueryAsync();
Console.WriteLine(cc.Web.Title);
}
Console.WriteLine("Hello World!");
}
}
}
}
catch(ex)
{
Ex.message();
}
Try using if-else and try-catch block to catch the exact error.
As getcontext uses current users credentials , but here we are intended to use app only context.
If users login is not having privileges to access the sharepoint or when entered wrong Login details , the 401 unauthorized usually occurs.
If the user profile needs to be read , it needs user.read permission.
But note the limitation Accessing SharePoint using an application context, also known as app-only | Microsoft Learn here.
User Profile CSOM write operations do not work with Azure AD application Only read operations work.
For writing you need to user to login, use SharePoint App-Only principal
Reference : azure active directory - SharePoint PnP AuthenticationManager login with current user - Stack Overflow

Google.Apis.GoogleAnalyticsAdmin.v1alpha raising insufficientPermissions when trying to get google admin analytics account detail in C#?

I wanted to get the account detail, after authenticating I added to the scope section to grant the permission, but when running the code it raised an error insufficient permission, I don't know what step I missed, how would I be able to fix this error?
private static GoogleAnalyticsAdminService _analyticsService;
public static void IntializeAnalytics() {
GoogleCredential credential = GoogleCredential.GetApplicationDefault();
if (CloudManager.Credential.IsCreateScopedRequired)
{
credential = CloudManager.Credential.CreateScoped(
GoogleAnalyticsAdminService.Scope.AnalyticsReadonly, GoogleAnalyticsAdminService.Scope.AnalyticsManageUsers,
GoogleAnalyticsAdminService.Scope.AnalyticsManageUsersReadonly, GoogleAnalyticsAdminService.Scope.AnalyticsEdit);
}
_analyticsService = new GoogleAnalyticsAdminService(
new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = CloudManager.ApplicationName
});
var response = _analyticsService.Accounts.Get("accounts/Default Account for Firebase").Execute();
}
Error message :
Unhandled exception. The service analyticsadmin has thrown an exception.
HttpStatusCode is Forbidden.
Google.Apis.Requests.RequestError
Request had insufficient authentication scopes. [403]
Errors [
Message[Insufficient Permission] Location[ - ] Reason[insufficientPermissions] Domain[global]
]
Google.GoogleApiException: The service analyticsadmin has thrown an exception. HttpStatusCode is Forbidden. Request had insufficient authentication scopes.
at Google.Apis.Requests.ClientServiceRequest`1.ParseResponse(HttpResponseMessage response)
at Google.Apis.Requests.ClientServiceRequest`1.Execute()
Request had insufficient authentication scopes.
Means that when you authorized your application you did not request the proper scopes. Im not sure where you got that code from Its nothing like the code I normally use.
using Google.Analytics.Admin.V1Beta;
Console.WriteLine("Hello, World!");
// Path to the service account credentials file.
const string credentialsJsonPath = #"C:\Development\FreeLance\GoogleSamples\Credentials\ServiceAccountCred.json";
// Property on Google analytics which the service account has been granted access to.
const string propertyId = "XXXXXX";
var client = new AnalyticsAdminServiceClientBuilder()
{
CredentialsPath = credentialsJsonPath
}.Build();
var response = client.ListAccounts(new ListAccountsRequest());
foreach (var account in response) {
Console.WriteLine(account.Name);
}
Console.ReadLine();

Authentication with p12 key give error User cannot access account

I have a MVC web-application that update the products on the eCommerce site. Now we enrolled into the google merchant center and my objective is to update the products at the same time. I am using the Google.Apis.ShoppingContent.v2_1 API.
This is my API Credentials
This is my API service account
I have used the google account email address for the user as well as the service account email but with the same result.
I have the following
static string[] Scopes = { ShoppingContentService.Scope.Content};
static string P12Secret = #"~\Content\XXXXXX-5cab03fb904a.p12";
static string userName = "serviceaccount#gserviceaccount.com";
static public async Task RunTest2()
{
var certificate = new X509Certificate2(P12Secret, "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(userName)
{
Scopes = Scopes
}.FromCertificate(certificate));
var service = new ShoppingContentService(new BaseClientService.Initializer
{
ApplicationName = ApplicationName,
HttpClientInitializer = credential
});
try
{
var result = await service.Products.List("My MerchantID").ExecuteAsync();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
When I execute var result = await service.Products.List("My MerchantID").ExecuteAsync(); I get the error
e.Message = "Google.Apis.Requests.RequestError\nUser cannot access account 123456789 [401]\r\nErrors [\r\n\tMessage[User cannot access account 123456789 ] Location[ - ] Reason[auth/account_access_denied] Domain[content.ContentErrorDomain]\r\n]\r\n"
Documentaiton
Service accounts are special Google accounts that can be used by applications to access Google APIs programmatically via OAuth 2.0. A service account uses an OAuth 2.0 flow that does not require human authorization. Instead, it uses a key file that only your application can access. This guide discusses how to access the Content API for Shopping with service accounts.
Service accounts need to be pre authorized. If its not then it doesnt have access to any data.
User cannot access account 123456789
Means that it does not have access you have forgotten to grant it access. Check the Documentaiton look for the section below follow all of the steps.

Gmail API Set Forwarding with Service Account

Can anyone help as I'm getting bad request failed precondition errors when calling Gmail API to set forward address? Below is a C# .Net console app I'm trying do this with. I have delegated Domain Wide Authority to the Service Account.
Error:
Google.Apis.Requests.RequestError Bad Request [400] Errors [ Message[Bad Request] Location[ - ] Reason[failedPrecondition] Domain[global] ]
I think I was missing the User to impersonate. So, I added the user and now I get the following error.
Error:
Error:"unauthorized_client", Description:"Client is unauthorized to retrieve access tokens using this method.", Uri:""
namespace GmailForwarder
{
class Program
{
static string ApplicationName = "GmailForwarder";
static void Main(string[] args)
{
ServiceAccountCredential credential;
string serviceAccount = "gmailforwarder#gmailforwarder.iam.gserviceaccount.com";
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
try
{
// Create credential
credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccount)
{
User = "wtestboonew#chicagobooth.edu",
Scopes = new[] { GmailService.Scope.GmailSettingsSharing }
}.FromCertificate(certificate));
// Create Gmail API service.
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
string user = "wtestboonew#chicagobooth.edu"; // test gmail account
string fwdAddr = "acw5274#gmail.com";
ForwardingAddress fwdAddress = new ForwardingAddress();
fwdAddress.ForwardingEmail = fwdAddr;
var createFwdAddressResult = service.Users.Settings.ForwardingAddresses.Create(fwdAddress,"me").Execute();
}
catch (Exception ex)
{
}
}
}
}
This worked for me when I used : https://mail.google.com/ and https://www.googleapis.com/auth/gmail.settings.sharing OAuth2 scopes
I had this problem too (400 code). I was having two problems:
1) As ACW says, I was missing "https://mail.google.com/" from the list of scopes required, appart from "https://www.googleapis.com/auth/gmail.settings.sharing".
2) I was missing to put the scopes wrapped in quotes ("") in the administration console (when specifying the scopes for the service account).
Hope it helps someone

Google Analytics throws 403 error

I am attempting to download metric data from Google Analytics using C# and am performing user authentication with OAuth 2.0. I'm using the Installed Application authorisation flow, which requires logging into Google and copy-and-pasting a code into the application. I'm following the code taken from google-api-dotnet-client:
private void DownloadData()
{
Service = new AnalyticsService(new BaseClientService.Initializer() {
Authenticator = CreateAuthenticator(),
});
var request = service.Data.Ga.Get(AccountID, StartDate, EndDate, Metrics);
request.Dimensions = Dimensions;
request.StartIndex = 1;
request.MaxResults = 10000;
var response = request.Execute(); // throws Google.GoogleApiException
}
private IAuthenticator CreateAuthenticator()
{
var provider = new NativeApplicationClient(GoogleAuthenticationServer.Description) {
ClientIdentifier = "123456789012.apps.googleusercontent.com",
ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx",
};
return new OAuth2Authenticator<NativeApplicationClient>(provider, Login);
}
private static IAuthorizationState Login(NativeApplicationClient arg)
{
// Generate the authorization URL.
IAuthorizationState state = new AuthorizationState(new[] { AnalyticsService.Scopes.AnalyticsReadonly.GetStringValue() });
state.Callback = new Uri(NativeApplicationClient.OutOfBandCallbackUrl);
Uri authUri = arg.RequestUserAuthorization(state);
// Request authorization from the user by opening a browser window.
Process.Start(authUri.ToString());
Console.Write("Google Authorization Code: ");
string authCode = Console.ReadLine();
// Retrieve the access token by using the authorization code.
state = arg.ProcessUserAuthorization(authCode, state);
return state;
}
The Google account xxxxxx#gmail.com registered the Client ID and secret. The same account has full administration rights in Google Analytics. When I try to pull data from Google Analytics, it goes through the authorisation process, which appears to work properly. Then it fails with:
Google.GoogleApiException
Google.Apis.Requests.RequestError
User does not have sufficient permissions for this profile. [403]
Errors [
Message[User does not have sufficient permissions for this profile.] Location[ - ] Reason [insufficientPermissions] Domain[global]
]
I've been struggling with this for a few hours. I've double checked that the correct user is being used, and is authorised on Google Analytics. I'm at a loss as to what is misconfigured. Any ideas as to what requires configuring or changing?
If auth seems to be working working then my suggestion is that you make sure you're providing the correct ID because based on your code snippet:
var request = service.Data.Ga.Get(AccountID, StartDate, EndDate, Metrics);
one can only assume that you're using the Account ID. If so, that is incorrect and you'd receive the error you've encountered. You need to query with the Profile ID.
If you login to Google Analytics using the web interface you'll see the following pattern in URL of the browser's address bar:
/a12345w654321p9876543/
The number following the p is the profile ID, so 9876543 in the example above. Make sure you're using that and actually you should be using the table id which would be ga:9876543.
If it isn't an ID issue then instead query the Management API to list accounts and see what you have access to and to verify auth is working correctly.
This can help : https://developers.google.com/analytics/devguides/reporting/core/v3/coreErrors, look error 403.
//Thanks for this post. The required profile id can be read from the account summaries.
Dictionary profiles = new Dictionary();
var accounts = service.Management.AccountSummaries.List().Execute();
foreach (var account in accounts.Items)
{
var profileId = account.WebProperties[0].Profiles[0].Id;
profiles.Add("ga:" + profileId, account.Name);
}

Categories

Resources