The credentials authorizing access to gmailAPI seem to be cached somehow. When I change the client ID and secret, and email address, nothing changes. Also, when I change the scope, nothing changes. I am trying to force the credentials to refresh. Is there a way to force the credentials to reprompt the user?
Is there a file I can find and delete?
I am using c# and the gmail api package from nuget. The code for credentialing is:
_emailAddress = Settings.EmailAddress;
string clientSecret = Settings.ClientSecret;
string clientId = Settings.ClientId;
ClientSecrets clientSecrets = new ClientSecrets {ClientId = clientId, ClientSecret = clientSecret};
UserCredential credential;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
clientSecrets,
Scopes,
"user",
CancellationToken.None).Result;
_service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Draft Sender", //applicationName,
});
Also, once I credential, my scope is set: cannot change scope until I delete and renew the secret and clientID. Also, it takes two run-throughs in order to generate the credentials, the first resulting in an error, but the second resulting in a prompt to authorize.
I'm pretty sure that this is happening because the credentials are cached somewhere, somehow, even though I haven't specified any storage...but I don't know for sure. Either way, the issue seems to depend on code inside the gmail api package, but I am having trouble finding a solution for what I am dealing with.
Ultimately, I need to deploy a functionality to an ASP website that will access the gmail account as a repository for emails, so I need to have this authorization thing figured out so that the server will have access to the email account upon deployment (or easily after). It is not really necessary (but desired) that it happen without the intermediate error.
Is there a solution (easy or hard) to control this caching process and handle the permissions?
If you don't specify where you are storing the response token, it's stored on:
C:\Users\'Your user'\AppData\Roaming\Google.Apis.Auth
If you want to specify the directory look at this:
(note: you just need to create the directory App_Data in your project)
string path = HostingEnvironment.MapPath("~/App_Data");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
new ClientSecrets
{
ClientId = "Client_ID",
ClientSecret = "Secret_Client"
},
Scopes,
"me",
CancellationToken.None,
new FileDataStore(path)
).Result;
Related
We have different projects on GCP we use them to access different Google APIs. Most of them for internal use only.
In this particular case, we have 2 projects, both use Service Account and both are allowed on Workspace Domain-wide Delegation on the same scopes. They are almost clones of each other.
I execute a simple request with the same code (Spreadsheet.Get()) with project 1 credentials it works. I execute the same request with project 2 credentials it doesn't work.
Since Workspace Domain-wide Delegation it's activated the spreadsheet its shared to my email and I connect to the API with my email too (works with project 1 so this is not the problem) (impersonating a user)
The only difference it's that one project has OAuth Consent Screen on external (only 100 users cause we use it internally only, anyways..) and the other one it's internal but this has nothing to do with this right?
Where the problem could come from? Do I need to recreate the project that doesn't work?
Here is the error message :
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested
Edit to answer the comments but this code works depending on the service account we use
Generating the credentials:
internal static ServiceCredential GetApiCredentialsFromJson(string jsonCredentialsPath, string mailToMimic)
{
string jsonCertificate = File.ReadAllText(jsonCredentialsPath);
string privateKey = Regex.Match(jsonCertificate, #"(?<=""private_key"": "")(.*)(?="")").Value.Replace(#"\n", "");
string accountEmail = Regex.Match(jsonCertificate, #"(?<=""client_email"": "")(.*)(?="")").Value;
ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
{
Scopes = _scopes,
User = mailToMimic
}.FromPrivateKey(privateKey);
return new ServiceAccountCredential(credentials);
}
Using the credentials:
internal GoogleSheetService(ServiceCredential credentials)
{
SheetsService = new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer = credentials
});
SheetsService.HttpClient.Timeout = TimeSpan.FromSeconds(100);
}
Client ID is allowed on the Drive, Ads and Spreadsheets scopes on the Workspace console.
The answer was simple, but we had to figure it out by ourselves.
The scopes you add in your app when you initialize the client need to be exactly the same scopes you added in the Google Admin wide-delegation page. Even if your app or part of your app don't need them all.
C# example:
private static readonly string[] _scopes = { DriveService.Scope.Drive, SheetsService.Scope.Spreadsheets, SlidesService.Scope.Presentations };
ServiceAccountCredential.Initializer credentials = new ServiceAccountCredential.Initializer(accountEmail)
{
Scopes = _scopes,
User = mailToMimic
}.FromPrivateKey(privateKey);
return new ServiceAccountCredential(credentials);
Here my app only needs SheetsService.Scope.Spreadsheets but I had to add DriveService.Scope.Drive and SlidesService.Scope.Presentations because the same client its used for other apps that need them.
I currently have a Bot for MS Teams that I have created in Visual studio. I need a way of retrieving the information of the user of the Bot in MS Teams when they have used it such as the email address, full name, etc... This Bot is hosted in Azure as an app service. I have followed this link https://learn.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-auth-aad and have configured Azure Active Directory authentication on my app service. I have the following code to try and retrieve an access token from Azure that I will then use to get information from https://graph.microsoft.com/v1.0/me.
static async Task<AuthenticationResult> GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = await context.AcquireTokenAsync(
resource, // the resource (app) we are going to access with the token
clientCredential); // the client credentials
return authenticationResult;
}
I've taken this code from elsewhere but the problem I'm having is that I can't see a client secret for the Azure Active Directory application and I don't know what "authority" should be. This is my first time working with anything like this so would appreciate any pointers or tips in this area.
static string authority = "https://login.microsoftonline.com/common";
static string clientId = "my_azure-ad_app_id";
static string resource = "https://<app_name>.azurewebsites.net/";
static string clientSecret = "azure-ad_app_secret";
static public async Task<AuthenticationResult> GetS2SAccessTokenForProdMSAAsync()
{
return await GetS2SAccessToken(authority, resource, clientId, clientSecret);
}
static async Task<AuthenticationResult> GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = await context.AcquireTokenAsync(
resource, // the resource (app) we are going to access with the token
clientCredential); // the client credentials
return authenticationResult;
}
This is the code I've got to retrieve the access token so far but it doesn't return anything.
The secret (or key) you can get by generating one in its App Registration blade. You should go to Azure Active Directory -> App registrations -> Find your app -> Keys -> Enter a name, select duration and Save.
You should now have your key.
The authority is basically "the identity provider we will authenticate against".
So if your app is only meant for use in your organization,
you'll want to specify that AAD tenant.
In that case the authority would be:
https://login.microsoftonline.com/your-tenant.onmicrosoft.com
Replace your-tenant.onmicrosoft.com with any verified domain name you have in AAD (one in this format is included always), or use your AAD tenant id instead (you can find it from AAD -> Properties).
In case your app should support any organization (i.e. it's a multi-tenant app),
then the authority is always:
https://login.microsoftonline.com/common
Adding to Junnas Answer. You can try this reference doc
Client Id:
Client Secret :
Note: The Client Secret is visible only at the time of creation. After that, it won't appear again. So make sure you copy on the first time itself
We develop application in C# which need to transfer ownership of all Google Drive documents related to the curtain domain to a single certain user without permission of original owner. We are using trial version of Google Apps business account.
In principal, we need to do this: http://screencast.com/t/effVWLxL0Mr4 but in C# code.
Accourding to the documentation, it is implemented in OAuth2 as superadmin functionality. https://support.google.com/a/answer/1247799?hl=en (1).
But document was deprecated and more over we did not find any API call to do that.
Using account of project creator, it is appeared, that he can not access to all files and can not see files is not shared with him.
In Google Admin Console in manage API client access we added access rights to him to access files without permission to files without permission. Link: screencast.com/t/zU9cc6Psyb. We added routes access routes there according to that document link: trovepromo-tf.trove-stg.com/0m1-sds/support.google.com/a/answer/162106?hl=en and tried again.
It did not work out...
Also, We found out that we need to use service account to have access to all data of all users of the domain, therefore we generated API keys for service account link: screencast.com/t/rNKuz6zchwV in the created project and got authenticated in the application using the following code:
var certificate = new X509Certificate2(#"C:\Temp\key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("Service account email")
{
User= "admin#domain.com",
Scopes = new[] { DriveService.Scope.Drive }
}.FromCertificate(certificate));
but when we try to get list of folders, we get error :"access_denied", Description:"Requested client not authorized.", Uri:""
Please help us to transfer ownership of one user to another by service account!
Update from 13-08-2014:
Dear, It seems I have problem with user impersonalization.
1) When I use api to connect on behalf of user. During the authentication it redirects to browser and ask permisstion. After that all is completely fine, I can manimulate with folders except one one thing: I can not transfer ownership to him
2) When I use service account without impersonalization, authentication looks like the following:
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountId)
{
Scopes = new[] { DriveService.Scope.Drive,
DriveService.Scope.DriveAppdata,
DriveService.Scope.DriveFile,
DriveService.Scope.DriveScripts,
DirectoryService.Scope.AdminDirectoryUser,
DirectoryService.Scope.AdminDirectoryGroup,
DirectoryService.Scope.AdminDirectoryOrgunit,
DirectoryService.Scope.AdminDirectoryUserReadonly
},
}.FromCertificate(certificate));
Then I can access to all files shared to service account but (again) I can not transfer the rights.
3) Then I try impersonalize Service account by adding sypeadministrator email account to the user User = myaddress#mydomain.com
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(ServiceAccountId)
{
Scopes = new[] { DriveService.Scope.Drive,
DriveService.Scope.DriveAppdata,
DriveService.Scope.DriveFile,
DriveService.Scope.DriveScripts,
DirectoryService.Scope.AdminDirectoryUser,
DirectoryService.Scope.AdminDirectoryGroup,
DirectoryService.Scope.AdminDirectoryOrgunit,
DirectoryService.Scope.AdminDirectoryUserReadonly
},
User = AdminEmail,
}.FromCertificate(certificate));
Then I have Error:"access_denied", Description:"Requested client not authorized.", Uri:""
How can I impersonalize service account correctly?
Updated 13-08-2014
We found out that basic api for authentication is here: https://developers.google.com/accounts/docs/OAuth2ServiceAccount#creatingjwt
Normally, all what I showed before is an .net implementation of the protocol.
How can we please do impersonalization of of user in .net code. We did not find any working .net implementation of it.
I finally found an answer.
I could impersonalize an account by using the following construction:
Thanks to all, who tried to help me!
var initializer = new ServiceAccountCredential.Initializer(ServiceAccountId)
{
Scopes = scope,
User = AdminEmail1
};
var credential = new ServiceAccountCredential(initializer.FromCertificate(certificate));
var driveService = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName
});
I have been writing a desktop application that uses the Google Drive API v2. I have the following code:
var credential = GoogleWebAuthorizationBroker.AuthorizeAsync
(
new ClientSecrets { ClientId = ClientID, ClientSecret = ClientSecret },
new[] { DriveService.Scope.Drive }, "user", CancellationToken.None
)
.Result;
this.Service = new DriveService( new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "My Test App",
} );
var request = this.Service.Files.List();
request.Q = "title = 'foo' and trashed = false";
var result = request.Execute();
The first time I ran this code it opened a browser and asked me to grant permissions to the App, which I did. Everything worked successfully until I realized I was using the wrong Google account. At that point I logged into the wrong Google account and revoked access to my App. Now, whenever I run the same code it throws an exception:
Error:"invalid_grant", Description:"", Uri:""
When I examine the service and request objects, it looks like the oauth_token isn't getting created any more.
I know what I did to mess things up, but I can't figure out how to correct it so I can use a different Google account for testing. What do I need to do?
The stored credentials are persisted using StorageDataStore. You can either call the DeleteAsync() method or manually find and delete the file.
I am retriving a token through an website, then I save the token, and refresh token in a database. Then I am writing .net program that uploads video's located on our server to youtube. My problem is to get the program I am making, use the stored token. I am using one of google's examples to upload the video. But the program should use the already saved token, instead os asking for new credentials.
The current code is this.
UserCredential credential;
//credential.UserCredential(,"",)
using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
Console.WriteLine("HER");
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
So instead I would like to use an already saved token.
Any help, or a pointer in the right direction would be great. Thanks.
You would use GoogleAuthorizationCodeFlow instead of GoogleWebAuthorizationBroker in order to use the refresh token. See the answer here.