Oidc - refresh_token prompting user to re-authenticate

Expected behaviour

Using OpenId Connect plugins, ownCloud Desktop clients (both Linux and Windows versions) should not be prompting users to re-authenticate once token_refresh triggers (after ~3-4 hours of operations)

Actual behaviour

Using OpenId Connect plugins, ownCloud Desktop clients (both Linux and Windows versions) are prompting users to re-authenticate once token_refresh triggers

Steps to reproduce

  1. Login to ownCloud Desktop client via OpenId Connect Provider (Apereo CAS)

  2. Login into ownCloud Desktop client is successful

  3. After several hours of operations using the ownCloud Desktop, the login form from OpenId Connect Provider (Apereo CAS) with re-appear asking the user to re-authenticate.

  4. The previous version (v.2.8.?) of ownCloud Desktop clients (both Linux and Windows version) were working correctly prior to upgrading to 2.9.x versions (i.e. ownCloud desktop client were not requiring user to re-authenticate every few hours)

  5. It should be noted that the ownCloud Android mobile client works correctly using the same OpenId Connect Provider (Apereo CAS)

Server configuration

Operating system:
Linux 5.12.12-gentoo

Web server:
Apache/2.4.48 (Unix)

Database:
mysql-8.0.23

PHP version:
PHP/7.4.19

ownCloud version:
ownCloud 10.8.0 (stable)

Storage backend (external storage):
none

Client configuration

Client version:
ownCloud 2.9.1 (build 5500)

Operating system:
Ubuntu 20.04 LTS

OS language:

Qt version used by client package (Linux only, see also Settings dialog):
Libraries Qt 5.12.10

Client package (From ownCloud or distro) (Linux only):
ownCloud

Installation path of client:
/opt/ownCloud

OpenId Connect Provider (OP) configuration

Operating system:
Apereo CAS - Version 6.4.0

Operating system:
Linux 5.12.12-gentoo

Web server:
Apache/2.4.48 (Unix)

Database:
mysql-8.0.23

Logs

10-21 15:31:38:841 [ info sync.credentials.http ]:	Refreshing token
10-21 15:31:38:841 [ info sync.accessmanager ]:	2 "" "https://www.redacted.com/owncloud/status.php" has X-Request-ID "09b4718a-c5f6-4865-a788-a72b52476660"
10-21 15:31:38:841 [ debug sync.cookiejar ]	[ OCC::CookieJar::cookiesForUrl ]:	QUrl("https://www.redacted.com/owncloud/status.php") requests: (QNetworkCookie("oc_sessionPassphrase=mcuGdVH758WMSyL%2FUQo7SpVspDlhIbBUQXYPVTuJXbbd6XVYR2j0G3Ovgqzb0noI4mVFh6GRU4Zamd8iCMFz76kLNVxaPI%2BBKUsDYgibuoj1wIVfL11zfQaE9ezmMIJh; secure; HttpOnly; domain=www.redacted.com; path=/owncloud"), QNetworkCookie("oc6es4gu9jai=ca2b4irb0pbvji3047ogpls8a6; secure; HttpOnly; domain=www.redacted.com; path=/owncloud"))
10-21 15:31:38:841 [ info sync.httplogger ]:	"09b4718a-c5f6-4865-a788-a72b52476660: Request: GET https://www.redacted.com/owncloud/status.php Header: { OC-Connection-Validator: desktop, User-Agent: Mozilla/5.0 (Linux) mirall/2.9.1 (build 5500) (ownCloud, ubuntu-5.4.154-0504154-generic ClientArchitecture: x86_64 OsArchitecture: x86_64), Accept: */*, X-Request-ID: 09b4718a-c5f6-4865-a788-a72b52476660, Original-Request-ID: 09b4718a-c5f6-4865-a788-a72b52476660, Cookie: oc_sessionPassphrase=mcuGdVH758WMSyL%2FUQo7SpVspDlhIbBUQXYPVTuJXbbd6XVYR2j0G3Ovgqzb0noI4mVFh6GRU4Zamd8iCMFz76kLNVxaPI%2BBKUsDYgibuoj1wIVfL11zfQaE9ezmMIJh; oc6es4gu9jai=ca2b4irb0pbvji3047ogpls8a6, } Data: []"
10-21 15:31:38:841 [ info sync.networkjob ]:	Created OCC::CheckServerJob("https://www.redacted.com/owncloud/status.php", "09b4718a-c5f6-4865-a788-a72b52476660", "09b4718a-c5f6-4865-a788-a72b52476660") for OCC::OAuth(0x55e4c4d8cdd0)
10-21 15:31:38:841 [ debug sync.networkjob.jobqueue ]	[ OCC::JobQueue::block ]:	block: 1 "someone@www.redacted.com"
10-21 15:31:38:842 [ info sync.httplogger ]:	"15cf18ef-0a36-4418-a7e3-a2433d635bba: Response: PROPFIND 401 https://www.redacted.com/owncloud/remote.php/dav/files/someone/ Header: { Date: Thu, 21 Oct 2021 19:31:38 GMT, Server: Apache, Strict-Transport-Security: max-age=15552000; includeSubDomains, X-Content-Type-Options: nosniff, X-XSS-Protection: 0, X-Robots-Tag: none, X-Frame-Options: SAMEORIGIN, X-Download-Options: noopen, X-Permitted-Cross-Domain-Policies: none, X-Powered-By: PHP/7.4.19, Expires: Thu, 19 Nov 1981 08:52:00 GMT, Cache-Control: no-store, no-cache, must-revalidate, Pragma: no-cache, Content-Security-Policy: default-src 'none';, WWW-Authenticate: Bearer realm=\"ownCloud\", Basic realm=\"ownCloud\", charset=\"UTF-8\", Content-Length: 476, Content-Type: application/xml; charset=utf-8, Via: 1.1 www.redacted.com, Vary: User-Agent, Keep-Alive: timeout=100, max=118, Connection: Keep-Alive, } Data: [<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<d:error xmlns:d=\"DAV:\" xmlns:s=\"http://sabredav.org/ns\">\n  <s:exception>Sabre\\DAV\\Exception\\NotAuthenticated</s:exception>\n  <s:message>No public access to this resource., No 'Authorization: Basic' header found. Either the client didn't send one, or the server is misconfigured, Bearer token was incorrect, No 'Authorization: Basic' header found. Either the client didn't send one, or the server is misconfigured</s:message>\n</d:error>\n]"
10-21 15:31:38:842 [ debug sync.networkjob.jobqueue ]	[ OCC::JobQueue::retry ]:	Retry queued OCC::RequestEtagJob("https://www.redacted.com/owncloud/remote.php/dav/files/someone/", "15cf18ef-0a36-4418-a7e3-a2433d635bba", "15cf18ef-0a36-4418-a7e3-a2433d635bba", "Host requires authentication")

Please check for the POST request to the token endpoint, and the 401 before and post requests and responses. For request and response, there are always 2x [ info sync.httplogger ] that share the same X-Request-ID. (09b4718a-c5f6-4865-a788…)

Seeing “IO error opening file stream.”,“error”:“invalid_client_metadata” in the Desktop client log.

I’ve uploaded both the ownCloud Desktop client log (beyond the previously provided snippet) and the CAS log for the same timeframe.

In response to the token refresh, it appears the CAS is looking for a service configuration file named “/etc/cas/config/services/ownCloud2.9.1(build5500)-1634844699382.json” which does not exist while the correct service configuration file (used during the initial login) is named “/etc/cas/config/services/openid-10000025.json”. I’m unclear as to why/how CAS is being requested a service configuration file named as the ownCloud Desktop version?

ownCloud Desktop log

Apero CAS log

Desktop 2.9 supports OpenID Connect Dynamic Client Registration (DCR).

It’s announced in your .well-known/openid-configuration:

"registration_endpoint":"https://secure.redacted.com/cas/oidc/register"

So Desktop 2.9 sends a POST to this endpoint:

{
"application_type": "native",
"client_name": "ownCloud 2.9.1 (build 5500)",
"redirect_uris": [
    "http://127.0.0.1"
],
"token_endpoint_auth_method": "client_secret_basic"
}

Seems your IdP can’t deal with it. Response is:

"error_description":"IO error opening file stream.","error":"invalid_client_metadata"

This works fine with Keycloak and other IdP’s, but I’m not an expert for Apereo CAS.

Apereo CAS docs about DCR are not that detailed:
CAS - OpenID Connect Authentication

Thanks Michael for the additional details. I think I have enough insight now to pursue the issue which seems clearly on my IdP’s side. I agree Apereo CAS is rather terse on its support of DCR. Feel free to close this ticket.

Let me know if you have insights about Apereo CAS. It might be useful for others as well. And maybe there’s anything the ownCloud Desktop app can improve…

Sorry for the delay. I was waiting for the Apereo CAS developers to release 6.5.RC2 which included a bug fix regarding OIDC Dynamic Client Registration.

Now the ownCloud client (linux, windows) initial authorization requests are generating JSON service definition on the CAS server but I’m struggling a bit with callback being sent by the ownCloud desktop app and its later validation by the CAS server. Ultimately, I’m able to get the ownCloud client and the CAS server to work together (i.e. persistant authentication) after I modify manually the CAS JSON file by updating the serviceId (i.e. callback url) from http://127.0.0.1
to http://127.0.0.1:.* (i.e. match to all port numbers). As documented below, initially the ownCloud client registers itself with a callback-uri (serviceId) of http://127.0.0.1 for which CAS creates a JSON file for this service. Unfortunately later, the ownCloud client (as I understand), attempts some validation using http://127.0.0.1:40709. The CAS server expect an exact match on serviceId (without any port number) and hence does not recognize the call from the ownCloud client. I’m still unclear whether the issue (or solution) is linked to the ownCloud client or the CAS server.

ownCloud desktop client (2.9.1) - Linux:
11-22 14:17:39:755 [ info sync.httplogger ]:	"3f00fc7c-b5d6-4d52-b929-4094a9b9ae19: Response: POST 201 https://secure.redacted.com/cas/oidc/register Header: { Date: Mon, 22 Nov 2021 19:17:38 GMT, Server: Apache, requestId: 764571c9-a254-4bcc-8799-2b0537afbd52, Cache-Control: no-cache, no-store, max-age=0, must-revalidate, Pragma: no-cache, Expires: 0, Strict-Transport-Security: max-age=15768000 ; includeSubDomains, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, X-XSS-Protection: 1; mode=block, Vary: User-Agent, Keep-Alive: timeout=100, max=200, Connection: Keep-Alive, Transfer-Encoding: chunked, Content-Type: application/json;charset=UTF-8, } Data: [{\"client_id\":\"EypOKlThOZe6FDMnXPvwnefV5vwaEg2fO2Y9\",\"client_secret\":\"bC24YKkyjm36zlL66DWgzvZOuf5zZc4SHNT1\",\"client_name\":\"ownCloud 2.9.1 (build 5500)\",\"application_type\":\"web\",\"subject_type\":\"public\",\"grant_types\":[\"none\",\"authorization_code\",\"password\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:uma-ticket\"],\"response_types\":[\"code\",\"none\",\"token\",\"device_code\",\"id_token token\",\"id_token\"],\"redirect_uris\":[\"http://127.0.0.1\"],\"contacts\":[],\"token_endpoint_auth_method\":\"client_secret_basic\",\"registration_access_token\":\"AT-1-K7tCjVccP5SOYtQtVtIoCi11a9Iykgzu\",\"registration_client_uri\":\"https://secure.redacted.com/cas/oidc/clientConfig?clientId=EypOKlThOZe6FDMnXPvwnefV5vwaEg2fO2Y9\",\"client_secret_expires_at\":0}]"
> 11-22 14:17:39:757 [ info sync.credentials.manager ]:	set "ownCloud_credentials:www.redacted.com:10839d0e-e0d4-487a-a280-91bab78a3e35:http/clientSecret"
> 11-22 14:17:39:760 [ debug sync.credentials.oauth ]	[ isUrlValid ]:	Checking URL for validity: QUrl("https://secure.redacted.com/cas/oidc/oidcAuthorize?response_type=code&client_id=EypOKlThOZe6FDMnXPvwnefV5vwaEg2fO2Y9&redirect_uri=http://127.0.0.1:40709&code_challenge=wvuB6-9PdhJaV-vwsig3ILw2BWAigICoQ6MtnaughEE&code_challenge_method=S256&scope=openid offline_access email profile&prompt=select_account consent&state=IXo0lpOqYtZMLLB_DVBTrvAyElWR5xZyFwsH5-2WhZA%3D&login_hint=someone&user=someone")
> 
> CAS server log:
> 2021-11-22 14:17:42,568 ERROR [org.apereo.cas.support.oauth.util.OAuth20Utils] - <Unsupported [redirect_uri]: [http://127.0.0.1:40709] does not match what is defined for registered service: [http://127.0.0.1]. Service is considered unauthorized. Verify the service matching strategy used in theservice definition is correct and does in fact match the client [http://127.0. 0.1:40709]>
>  2021-11-22 14:17:42,571 WARN [org.apereo.cas.support.oauth.validator.authorization.BaseOAuth20AuthorizationRequestValidator] - <Callback URL [http://127.0.0.1:40709] is not authorized for registered service [http://127.0.0.1].>
>  
>  CAS service definition (JSON) generated by ownCloud OIDC Dynamic Client registration
>  {
>  @class: org.apereo.cas.services.OidcRegisteredService
>  serviceId: http://127.0.0.1
>  name: ownCloud 2.9.1 (build 5500)
>  id: 1637610348738
>  description: Registered service ownCloud 2.9.1 (build 5500)
>  logoutUrl: ""
>  clientSecret: ygi5ALxHQLOoJfuzpyMciv1XX4CqEujleBSH
>  clientId: kRgLKkNsBBDf0nAEQoacRCo0hU4lk4M6kUXT
>  dynamicallyRegistered: true
>  dynamicRegistrationDateTime: 2021-11-22T19:45:48.738746Z
>  scopes:
>  [
>  java.util.HashSet
>  [
>  address
>  phone
>  openid
>  email
>  profile
>  offline_access
>  ]
>  ]
>  }

Check RFC 8252 for mored details about the loopback interface with a randomly assigned port:

Thank you for the pointer. It does clarify the situation. I’ll follow-up on the CAS server side.