【问题标题】:Python client to access CalDAV via OAuth2 on NextcloudPython 客户端通过 Nextcloud 上的 OAuth2 访问 CalDAV
【发布时间】:2021-08-12 15:48:04
【问题描述】:

用于使用 CalDAV 的规范 examples 始终使用用户名/密码身份验证。但是 Nextcloud 支持 OAuth2,因此我想通过 oauth 使用 CalDAV。

我已经对 Google 日历 API 做了同样的事情,但只是改编了 Google 提供的 oauth2client 示例:

client_secrets = 'client_secrets.json'
flow = client.flow_from_clientsecrets(client_secrets, scope="",
                                      message=tools.message_if_missing(client_secrets))
storage = file.Storage('calendar_credentials.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
    credentials = tools.run_flow(flow, storage)

http = credentials.authorize(http=build_http())

build_http() 替换为caldav.DAVClient 的实例不起作用。内部的request() API 完全不同,调用 caldav 客户端的任何方法都会在被authorize() 包裹时惨遭失败。那么,问题来了:如何将caldav.DAVClientoauth2client 结合起来?

关于在 nextCloud 中使用 OAuth 的文档也很少。我找到了this posting,但仍然不清楚具体去哪里了。

【问题讨论】:

    标签: python oauth-2.0 caldav nextcloud


    【解决方案1】:

    让我们从配置开始。在 Nextcloud 中,转到安全设置 (https://mycloud.example.com/settings/admin/security)。有一个部分OAuth 2.0 clients。添加客户端。您可以使用任何名称,例如calendar,但重要的是重定向 URI 是 http://localhost:8080。为什么? tools.run_flow() 默认会实例化一个 http 服务器来接收这个地址上的认证调用。点击“添加”。您现在应该会看到一个新的客户端 ID。将客户端 ID 和密码(单击眼睛图标以显示)复制到 client_secrets.json,然后应该如下所示:

    {
      "web": {
        "client_id": "stuff copied from Client Identifier",
        "client_secret": "stuff copied from secret",
        "auth_uri": "https://mycloud.example.com/index.php/apps/oauth2/authorize",
        "token_uri": "https://mycloud.example.com/index.php/apps/oauth2/api/v1/token",
        "redirect_uris": []
      }
    }
    

    当您现在从问题部分运行示例时,您的浏览器应该会自动定向到 mycloud.example.com 实例,并且应该有一条消息说“您即将授予 日历 访问权限到您的 mycloud.example.com 帐户。”单击“授予访问权限”。输入您的用户名和密码后,浏览器现在应该重定向到 http://localhost:8080,您应该会看到消息“身份验证流程已完成。”

    注意事项:

    • 我发现client_secrets.json是否以web开头没有区别 或installed。但是,它必须是这两者之一。
    • 显然,redirect_uris 可以保持为空。

    现在是编程问题(毕竟这是一个程序员的论坛......)

    caldav.DAVClient 的构造函数允许一个auth 参数,它应该是requests.auth.AuthBase 的一个实例。所以让我们创建一个:

    from requests.auth import AuthBase
    
    
    class OAuth(AuthBase):
        def __init__(self, credentials):
            self.credentials = credentials
    
        def __call__(self, r):
            self.credentials.apply(r.headers)
            return r
    

    现在,我们不再像 Google 的原始示例那样调用 credentials.authorize(http=build_http()),而是编写

    caldav_client = caldav.DAVClient(
        "https://mycloud.example.com/remote.php/dav/",
        auth=OAuth(credentials))
    

    就是这样!我们现在可以写

    principal = caldav_client.principal()
    calendars = principal.calendars()
    

    original example

    【讨论】:

    • 谢谢你的例子。我可以知道我们是否可以在服务器端构建该 oauth 过程,而无需通过前端的身份验证过程?