C# GoogleWebAuthorizationBroker for OAuth2连接到GMail在Windows服务中不起作用。

huangapple go评论69阅读模式
英文:

C# GoogleWebAuthorizationBroker for OAuth2 connection to GMail not work in windows service

问题

我的应用程序是使用.NET Framework 4.7.2编写的。
我创建的应用程序必须与GMail邮箱接口,以通过POP3下载邮件。为了通过OAuth2将我的应用程序认证到邮箱,我在Google Cloud平台上创建了一个应用程序,并通过IdClient和IdSecret进行了认证。

我的应用程序由两个可执行程序组成:

  • 第一个可执行程序用于可视配置模式,用户可以在其中指定访问Google Cloud平台应用程序的参数。
  • 第二个可执行程序用于自动静默模式,可在Windows服务定期启动。

为了对云平台上创建的应用程序进行身份验证,我使用了Google.Apis.Auth NuGet包。
为了请求令牌,我使用以下代码。此代码由我应用程序中的两个可执行程序共享。

UserCredential credential;
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    new ClientSecrets
    {
        ClientId = "XXXXXXX",
        ClientSecret = "XXXXXXX"
    },
    new[] { "https://mail.google.com/" },
    "user",
    System.Threading.CancellationToken.None).Result;

if (credential.Token.IsExpired(credential.Flow.Clock))
{
    if (!(credential.RefreshTokenAsync(CancellationToken.None).Result))
    {
        CrmEMFunc.LogMessage("Access token can't be refreshed");
        return;
    }
}

在第一次安装期间,配置应用程序(可视化应用程序)将运行,以便用户可以输入进行身份验证所需的所有参数。
当首次保存连接配置时,将打开浏览器以请求访问应用程序在云平台上创建的邮件的同意。

成功完成此配置阶段后,将生成一个令牌,只要未被撤销,就可以使用或刷新。因此,对于所有未来的执行,将不再需要打开浏览器以请求同意。

重新打开可视化exe程序,它将根据需要多次要求凭据,而不再打开浏览器。

但是,当我从Windows服务运行非可视exe程序时,当我到达凭据请求代码(如上所示的代码)时,它会挂起。
令牌是有效且活动的,因此浏览器不应该打开,应用程序应该能够在无需用户交互的情况下继续执行。

我还尝试使用await或**GetAwaiter().GetResult()**方法:

credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(...)

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(...).GetAwaiter().GetResult()

但是没有任何变化,应用程序仍然挂起。

在Windows服务上,我已激活了“允许服务与桌面交互”的选项(服务 > 属性 > 连接 > 本地系统帐户),但问题仍然存在。

在非可视exe程序选项中,我尝试将应用程序的输出类型(应用程序 > 输出类型)设置为“控制台应用程序”或“Windows应用程序”,但在这两种情况下都没有改变情况。

有关如何解决此问题的任何想法吗?

英文:

My application is written with .NET Framework 4.7.2.
The application I created must interface with a GMail mailbox to download mail (via POP3). To authenticate my application to the mailbox via OAuth2, I created an application on Google Cloud Platform, and from the application I authenticate via IdClient and IdSecret.

My application consists of two executable programs:

  • First exe program for visual configuration mode, where the user can indicate the parameters to access the application on the google cloud platform
  • Secondo exe program for automatic silent mode, in which the executable will be launched on a schedual basis from a windows service

To authenticate to the application created on the cloud platform, I use the Google.Apis.Auth NuGet package.
To request the token I use the following code. This code is shared by both executable programs in my application

        UserCredential credential;
        credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
            new ClientSecrets
            {
                ClientId = "XXXXXXX",
                ClientSecret = "XXXXXXX"
            },
            new[] { "https://mail.google.com/" },
            "user",
            System.Threading.CancellationToken.None).Result;

        if (credential.Token.IsExpired(credential.Flow.Clock))
        {
            if (!(credential.RefreshTokenAsync(CancellationToken.None).Result))
            {
                CrmEMFunc.LogMessage("Access token can't be refreshed");
                return;
            }
        }

During the first installation, the configuration application (visual application) will run, so that the user can enter all the parameters necessary for authentication.
When the connection configuration is saved for the first time, the browser will be opened to request consent to access emails via the application created on the cloud platform.

Once this configuration phase has been successfully completed, a token will be generated which can be used or refreshed as long as it is not revoked. For all future executions it will therefore no longer be necessary to view the browser to request consent.

Reopen visual exe program, it will ask for credentials as many times as necessary, without opening the browser anymore.

But when I run the non visual exe program from the windows service, when I get to the credential request code (code shown above), it hangs.
The token is valid and active, so the browser shouldn't open, and the application should be able to proceed without user iteration.

I also tried using the await or methods GetAwaiter().GetResult() methods

credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(...)

or

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(...).GetAwaiter().GetResult()

But nothing has changed, the application keeps hanging.

On the windows service I activated the option "Allow the service to interact with the desktop" (service > properties > connection > Local system account), but the problem continues to occur.

In the non-visual exe program options, I have tried setting the output type for the application (Application > Output Type) as either "Console Application" or "Windows Application," but in both cases it does not change the situation

Any ideas on how to solve this problem?

答案1

得分: 2

首先,您需要了解如何GoogleWebAuthorizationBroker存储用户凭据。

让我们来看一个比您目前的版本更高级的示例。

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
    GoogleClientSecrets.Load(stream).Secrets,
    new[] { DriveService.Scope.Drive,  DriveService.Scope.DriveFile },
    "LookIAmAUniqueUser",
    CancellationToken.None,
    new FileDataStore("Drive.Auth.Store")
).Result;

默认情况下,FileDataStore会将用户的授权凭据存储在%AppData%中。每个用户的App数据都不同。因此,当您的应用程序首次运行并进行授权时,凭据将存储在上面的示例中的%AppData%\Drive.Auth.Store中。

当您以Windows服务方式运行代码时,代码将以服务用户身份而不是最初授权您的用户身份运行。因此,服务无法找到授权,并尝试再次打开同意屏幕,但由于它是一个服务,无法执行此操作。

解决方案是为FileDataStore提供一个Windows服务将具有访问权限的目录。然后重新进行授权,文件将以凭据创建,然后当服务运行时,它将具有访问权限。

new FileDataStore(@"c:\datastore", true)

刷新令牌注意:

如果您的应用程序仍处于测试阶段,那么您的刷新令牌将在七天后过期。

英文:

Well what a great question.

First off you need to understand how GoogleWebAuthorizationBroker stores the user credentials.

Lets look at a little more advanced version then what you have.

credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
      GoogleClientSecrets.Load(stream).Secrets,
      new[] { DriveService.Scope.Drive,  DriveService.Scope.DriveFile },
      "LookIAmAUniqueUser",
       CancellationToken.None,
      new FileDataStore("Drive.Auth.Store")                               
      ).Result;

By default FileDataStore stores the users authorization credentials %AppData% App data is different for each user. So when your app runs the first time and you authorize it the credentials are stored in the example above %AppDatat%\Drive.Auth.Store

When you run as a windows service the code is run as a service user and not the user who originally authorized your code. So the service cant find the authorization and it will try to open the consent screen again which it cant as its a service.

Solution is to supply FileDataStore with a directory that the windows service will have access to. Then authorize it again the file will be created with the credentials, then when the service runs it will have access.

new FileDataStore(@"c:\datastore",true)   

refresh token note

If your app is still in testing phase then your refresh token is going to expire in seven days.

答案2

得分: 0

检查您的设置,使用正确的URI在https://console.cloud.google.com/上,URI是"xx/signin-google"。

英文:

review your settings on https://console.cloud.google.com/ with correct URI " xx/signin-google "

huangapple
  • 本文由 发表于 2023年6月22日 16:44:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76530068.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定