The canonical examples for using CalDAV always use username/password authentication. However Nextcloud supports OAuth2, therefore I would like to use CalDAV via oauth.
I already have done the same with the Google calendar API, but just adapting the oauth2client
sample provided by Google:
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())
by replacing build_http()
by an instance of caldav.DAVClient
does not work. The internal request()
APIs are quite different and calling any method of the caldav client will miserably fail when wrapped by authorize()
. So, the question is: how to integrate caldav.DAVClient
with oauth2client
?
Also documentation on using OAuth with nextCloud is scarce. I have found this posting, but it is still not obvious what goes where.
Let's start with the configuration. In Nextcloud, go to the security settings (https://mycloud.example.com/settings/admin/security). There is a section OAuth 2.0 clients
. Add a client. You can use any name, e.g. calendar
, but it is important that the redirect URI is http://localhost:8080
. Why? tools.run_flow()
will instantiate an http server to receive the authentication call on this address by default. Click "add". You should now see a new client ID. Copy the client ID and the secret (click the eye icon to reveal) to client_secrets.json which should then look like this:
{
"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": []
}
}
When you now run the example from the question section, your browser should automatically be directed to the mycloud.example.com instance and there should be a message saying "You are about to grant calendar access to your mycloud.example.com account." Click "Grant access". After entering your username and password, the browser should now be redirected to http://localhost:8080 and you should see the message "The authentication flow has completed."
Notes:
client_secrets.json
starts with web
or with installed
. It must be one of these two, however.redirect_uris
can remain empty.Now the programming question (this is a programmer's forum after all...)
The constructor of caldav.DAVClient
allows for an auth
parameter, which should be an instance of requests.auth.AuthBase
. So let's create one:
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
Now instead of calling credentials.authorize(http=build_http())
as in the original example from Google, we write
caldav_client = caldav.DAVClient(
"https://mycloud.example.com/remote.php/dav/",
auth=OAuth(credentials))
That's it! We can now write
principal = caldav_client.principal()
calendars = principal.calendars()
as in the original example.