OpenStack Swift Application Credentials

Nextcloud version (eg, 20.0.5): 23.0.3
Operating system and version (eg, Ubuntu 20.04): Debian 11
Apache or nginx version (eg, Apache 2.4.25): Apache 2.4.53
PHP version (eg, 7.4): 8.0.17

The issue you are facing:
I am trying to setup OpenStack Swift as External Storage, but I am met with:
Storage unauthorized. Authentication failed, verify the username, password and possibly tenant. Code 4

The credentials are working from the same server if I try them via the swiftclient. I think the issue might be that I have setup Application Credentials instead of a normal user in OpenStack, but I am not sure.

Is this the first time you’ve seen this error? (Y/N): Y

Steps to replicate it:

  1. Enable External Storage
  2. Add the External Storage via the GUI → Settings → Administration → External Storage
    This is where the error is thrown and I am not able to add it.

Output errors in nextcloud.log in /var/www/ or as admin user in top right menu, filtering for errors. Use a pastebin service if necessary.

{"reqId":"zrx21myANGEbLTWFomuX","level":2,"time":"2022-04-08T07:10:54+00:00","remoteAddr":"78.82.117.161","user":"admin","app":"no app in context","method":"GET","url":"/apps/files_external/globalstorages/1?testOnly=true","message":"Sto
rage unauthorized. Authentication failed, verify the username, password and possibly tenant","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36","version":"23.0.3.2","e
xception":{"Exception":"OCP\\Files\\StorageAuthException","Message":"Storage unauthorized. Authentication failed, verify the username, password and possibly tenant","Code":4,"Trace":[{"file":"/var/www/html/lib/private/Files/ObjectStore/
SwiftFactory.php","line":165,"function":"auth","class":"OC\\Files\\ObjectStore\\SwiftFactory","type":"->","args":[{"__class__":"OpenStack\\Identity\\v3\\Service"},"my-app-cred-id@myswifthost:5000/v3/nextcloud"]},{"file":"/var/www/html/lib/private/Files/ObjectStore/SwiftFactory.php","line":259,"function":"getClient","class":"OC\\Files\\ObjectStore\\SwiftFactory","type":"->","args":[]},{"file":"/var/www/html/lib/private/Files/ObjectStore/S
wiftFactory.php","line":247,"function":"createContainer","class":"OC\\Files\\ObjectStore\\SwiftFactory","type":"->","args":[]},{"file":"/var/www/html/apps/files_external/lib/Lib/Storage/Swift.php","line":584,"function":"getContainer","c
lass":"OC\\Files\\ObjectStore\\SwiftFactory","type":"->","args":[]},{"file":"/var/www/html/apps/files_external/lib/Lib/Storage/Swift.php","line":134,"function":"getContainer","class":"OCA\\Files_External\\Lib\\Storage\\Swift","type":"->
","args":[]},{"file":"/var/www/html/apps/files_external/lib/Lib/Storage/Swift.php","line":335,"function":"fetchObject","class":"OCA\\Files_External\\Lib\\Storage\\Swift","type":"->","args":[""]},{"file":"/var/www/html/lib/private/Files/
Storage/Common.php","line":458,"function":"stat","class":"OCA\\Files_External\\Lib\\Storage\\Swift","type":"->","args":[""]},{"file":"/var/www/html/apps/files_external/lib/MountConfig.php","line":130,"function":"test","class":"OC\\Files
\\Storage\\Common","type":"->","args":["*** sensitive parameter replaced ***","*** sensitive parameter replaced ***"]},{"file":"/var/www/html/apps/files_external/lib/Controller/StoragesController.php","line":288,"function":"getBackendSt
atus","class":"OCA\\Files_External\\MountConfig","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/apps/files_external/lib/Controller/StoragesController.php","line":363,"function":"updateStorageStatus"
,"class":"OCA\\Files_External\\Controller\\StoragesController","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":217,"function":"show","class":"OCA\
\Files_External\\Controller\\StoragesController","type":"->","args":[1,"*** sensitive parameter replaced ***"]},{"file":"/var/www/html/lib/private/AppFramework/Http/Dispatcher.php","line":126,"function":"executeController","class":"OC\\
AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Files_External\\Controller\\GlobalStoragesController"},"show"]},{"file":"/var/www/html/lib/private/AppFramework/App.php","line":157,"function":"dispatch","class":"OC
\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Files_External\\Controller\\GlobalStoragesController"},"show"]},{"file":"/var/www/html/lib/private/Route/Router.php","line":302,"function":"main","class":"OC\\AppF
ramework\\App","type":"::","args":["OCA\\Files_External\\Controller\\GlobalStoragesController","show",{"__class__":"OC\\AppFramework\\DependencyInjection\\DIContainer"},{"id":"1","_route":"files_external.global_storages.show"}]},{"file"
:"/var/www/html/lib/base.php","line":1006,"function":"match","class":"OC\\Route\\Router","type":"->","args":["/apps/files_external/globalstorages/1"]},{"file":"/var/www/html/index.php","line":36,"function":"handleRequest","class":"OC","
type":"::","args":[]}],"File":"/var/www/html/lib/private/Files/ObjectStore/SwiftFactory.php","Line":225,"Hint":"Storage is temporarily not available","Previous":{"Exception":"GuzzleHttp\\Exception\\ClientException","Message":"Client err
or: `POST https://myswifthost:5000/v3/auth/tokens` resulted in a `401 Unauthorized` response:\n{\"error\": {\"message\": \"The request you have made requires authentication.\", \"code\": 401, \"title\": \"Unauthorized\"}}\n","Code"
:401,"Trace":[{"file":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Middleware.php","line":69,"function":"create","class":"GuzzleHttp\\Exception\\RequestException","type":"::","args":[{"__class__":"GuzzleHttp\\Psr7\\Request"},"*** sensi
tive parameter replaced ***",null,[],null]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":204,"function":"GuzzleHttp\\{closure}","class":"GuzzleHttp\\Middleware","type":"::","args":["*** sensitive parameter
s replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":153,"function":"callHandler","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":[1,"*** sensitive parameter replaced ***",null]},{"file
":"/var/www/html/3rdparty/guzzlehttp/promises/src/TaskQueue.php","line":48,"function":"GuzzleHttp\\Promise\\{closure}","class":"GuzzleHttp\\Promise\\Promise","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/ww
w/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":248,"function":"run","class":"GuzzleHttp\\Promise\\TaskQueue","type":"->","args":["*** sensitive parameter replaced ***"]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/s
rc/Promise.php","line":224,"function":"invokeWaitFn","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":269,"function":"waitIfPending","class":"Guzzl
eHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/promises/src/Promise.php","line":226,"function":"invokeWaitList","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/
html/3rdparty/guzzlehttp/promises/src/Promise.php","line":62,"function":"waitIfPending","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Client.php","line":187,"function
":"wait","class":"GuzzleHttp\\Promise\\Promise","type":"->","args":[]},{"file":"/var/www/html/3rdparty/php-opencloud/openstack/src/Common/Api/OperatorTrait.php","line":115,"function":"request","class":"GuzzleHttp\\Client","type":"->","a
rgs":["POST","auth/tokens",{"headers":[],"json":{"auth":{"identity":{"password":{"user":["Encoding skipped as the maximum nesting level was reached"]},"methods":["password"]},"scope":{"project":{"name":"mytenantname","domain":["Encoding sk
ipped as the maximum nesting level was reached"]}}}},"synchronous":"*** sensitive parameter replaced ***"}]},{"file":"/var/www/html/3rdparty/php-opencloud/openstack/src/Common/Api/OperatorTrait.php","line":123,"function":"sendRequest","
class":"OpenStack\\Common\\Resource\\OperatorResource","type":"->","args":[{"__class__":"OpenStack\\Common\\Api\\Operation"},{"user":{"name":"my-app-cred-id","password":"my-app-cred-secret","domain":{"name":"default"}},"scope":{"project":{"name":"mytenantname","domain":{"name":"default"}}},"methods":["password"]}]},{"file":"/var/www/html/3rdparty/php-opencloud/openstack/src/Identity/v3/
Models/Token.php","line":117,"function":"execute","class":"OpenStack\\Common\\Resource\\OperatorResource","type":"->","args":[{"method":"POST","path":"auth/tokens","params":{"methods":{"type":"array","path":"auth.identity","items":{"typ
e":"string"},"description":"An array of authentication methods (in string form) that the SDK will use to authenticate. The only acceptable methods\nare \"password\" or \"token\"."},"user":{"type":"object","path":"auth.identity.password"
,"properties":{"id":{"type":"string","description":["Encoding skipped as the maximum nesting level was reached"]},"name":{"type":"string","description":"The username of the user"},"password":{"type":"string","description":"The password 
of the user"},"domain":{"type":"object","properties":["Encoding skipped as the maximum nesting level was reached"]}}},"tokenId":{"path":"auth.identity.token","sentAs":"id","type":"string","description":{"description":"The unique ID, or 
identifier, for the token","type":"string","location":"json"}},"scope":{"type":"object","path":"auth","properties":{"project":{"type":"object","properties":["Encoding skipped as the maximum nesting level was reached"]},"domain":{"type":
"object","properties":["Encoding skipped as the maximum nesting level was reached"]}}}}},{"user":{"name":"my-app-cred-id","password":"my-app-cred-secret","domain":{"name":"default"}},"scope":{"project":{"name":"mytenantname","domain":{"name":"default"}}},"methods":["password"]}]},{"file":"/var/www/html/3rdparty/php-opencloud/openstack/src/Identity/v3/Service.php","line":74,"function":"c
reate","class":"OpenStack\\Identity\\v3\\Models\\Token","type":"->","args":[{"user":{"name":"my-app-cred-id","password":"my-app-cred-secret","domain":
{"name":"default"}},"scope":{"project":{"name":"mytenantname","domain":{"name":"default"}}},"methods":["password"]}]},{"file":"/var/www/html/3rdparty/php-opencloud/openstack/src/Identity/v3/Service.php","line":42,"function":"generateToken"
,"class":"OpenStack\\Identity\\v3\\Service","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/lib/private/Files/ObjectStore/SwiftFactory.php","line":214,"function":"authenticate","class":"OpenStack\\Id
entity\\v3\\Service","type":"->","args":[{"autocreate":"*** sensitive parameter replaced ***","urlType":"publicURL","catalogName":"swift","catalogType":"object-store","bucket":"nextcloud","0":"And 13 more entries, set log level to debug
 to see all entries"}]},{"file":"/var/www/html/lib/private/Files/ObjectStore/SwiftFactory.php","line":165,"function":"auth","class":"OC\\Files\\ObjectStore\\SwiftFactory","type":"->","args":[{"__class__":"OpenStack\\Identity\\v3\\Servic
e"},"my-app-cred-id@myswifthost:5000/v3/nextcloud"]},{"file":"/var/www/html/lib/private/Files/ObjectStore/SwiftFactory.php","line":259,"function":"getClient","class":"OC\\Files\\ObjectStore\\SwiftFactory",
"type":"->","args":[]},{"file":"/var/www/html/lib/private/Files/ObjectStore/SwiftFactory.php","line":247,"function":"createContainer","class":"OC\\Files\\ObjectStore\\SwiftFactory","type":"->","args":[]},{"file":"/var/www/html/apps/file
s_external/lib/Lib/Storage/Swift.php","line":584,"function":"getContainer","class":"OC\\Files\\ObjectStore\\SwiftFactory","type":"->","args":[]},{"file":"/var/www/html/apps/files_external/lib/Lib/Storage/Swift.php","line":134,"function"
:"getContainer","class":"OCA\\Files_External\\Lib\\Storage\\Swift","type":"->","args":[]},{"file":"/var/www/html/apps/files_external/lib/Lib/Storage/Swift.php","line":335,"function":"fetchObject","class":"OCA\\Files_External\\Lib\\Stora
ge\\Swift","type":"->","args":[""]},{"file":"/var/www/html/lib/private/Files/Storage/Common.php","line":458,"function":"stat","class":"OCA\\Files_External\\Lib\\Storage\\Swift","type":"->","args":[""]},{"file":"/var/www/html/apps/files_
external/lib/MountConfig.php","line":130,"function":"test","class":"OC\\Files\\Storage\\Common","type":"->","args":["*** sensitive parameter replaced ***","*** sensitive parameter replaced ***"]},{"file":"/var/www/html/apps/files_extern
al/lib/Controller/StoragesController.php","line":288,"function":"getBackendStatus","class":"OCA\\Files_External\\MountConfig","type":"::","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/apps/files_external/lib/C
ontroller/StoragesController.php","line":363,"function":"updateStorageStatus","class":"OCA\\Files_External\\Controller\\StoragesController","type":"->","args":["*** sensitive parameters replaced ***"]},{"file":"/var/www/html/lib/private
/AppFramework/Http/Dispatcher.php","line":217,"function":"show","class":"OCA\\Files_External\\Controller\\StoragesController","type":"->","args":[1,"*** sensitive parameter replaced ***"]},{"file":"/var/www/html/lib/private/AppFramework
/Http/Dispatcher.php","line":126,"function":"executeController","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Files_External\\Controller\\GlobalStoragesController"},"show"]},{"file":"/var/www/html/l
ib/private/AppFramework/App.php","line":157,"function":"dispatch","class":"OC\\AppFramework\\Http\\Dispatcher","type":"->","args":[{"__class__":"OCA\\Files_External\\Controller\\GlobalStoragesController"},"show"]},{"file":"/var/www/html
/lib/private/Route/Router.php","line":302,"function":"main","class":"OC\\AppFramework\\App","type":"::","args":["OCA\\Files_External\\Controller\\GlobalStoragesController","show",{"__class__":"OC\\AppFramework\\DependencyInjection\\DICo
ntainer"},{"id":"1","_route":"files_external.global_storages.show"}]},{"file":"/var/www/html/lib/base.php","line":1006,"function":"match","class":"OC\\Route\\Router","type":"->","args":["/apps/files_external/globalstorages/1"]},{"file":
"/var/www/html/index.php","line":36,"function":"handleRequest","class":"OC","type":"::","args":[]}],"File":"/var/www/html/3rdparty/guzzlehttp/guzzle/src/Exception/RequestException.php","Line":113},"CustomMessage":"--"}}

The output of your config.php file in /path/to/nextcloud (make sure you remove any identifiable information!):

<?php
$CONFIG = array (
  'htaccess.RewriteBase' => '/',
  'memcache.local' => '\\OC\\Memcache\\APCu',
  'apps_paths' => 
  array (
    0 => 
    array (
      'path' => '/var/www/html/apps',
      'url' => '/apps',
      'writable' => false,
    ),
    1 => 
    array (
      'path' => '/var/www/html/custom_apps',
      'url' => '/custom_apps',
      'writable' => true,
    ),
  ),
  'instanceid' => 'xxx',
  'passwordsalt' => 'xxx',
  'secret' => 'xxx',
  'trusted_domains' => 
  array (
    0 => 'mydomain',
  ),
  'datadirectory' => '/var/www/html/data',
  'dbtype' => 'pgsql',
  'version' => '23.0.3.2',
  'overwrite.cli.url' => 'http://myurl',
  'dbname' => 'nextclouddb',
  'dbhost' => 'sqldb',
  'dbport' => '',
  'dbtableprefix' => '',
  'dbuser' => 'xxx,
  'dbpassword' => 'xxx',
  'installed' => true,
  'log_type' => 'file',
  'logfile' => 'nextcloud.log',
  'loglevel' => 3,
  'logdateformat' => 'F d, Y H:i:s',
);

I have also tried adding the authentication straight into the config.php as shown in your example config, but that makes Nextcloud not start with the same error as above (authentication error):

  'objectstore' => array(
         'class' => 'OC\\Files\\ObjectStore\\Swift',
         'arguments' => array(
                 'autocreate' => true,
                 'user' => [
                         'name' => 'my-app-cred-id',
                         'password' => 'my-app-cred-secret',
                         'domain' => [
                                 'name' => 'Default'
                         ]
                 ],
                 'scope' => [
                         'project' => [
                                 'name' => 'mytenantname',
                                 'domain' => [
                                         'name' => 'Default',
                                 ],
                         ],
                 ],
                 'serviceName' => 'swift',
                 'region' => 'se-sto',
                 'url' => "myswifthost:5000/v3",
                 'bucket' => 'nextcloud'
         )
   ),

Doest Nextcloud support authetication via application credentials or is creating a separate account for it the only way to make it work? Atm we have mandatory 2FA on all accounts which makes this impossible, and why we generally use application credentials for these kinds of things instead.

Any help or pointers in the right direction is appreciated!

Best regards

thank you for filling up the support form, it’s appreciate !!!

Usually, with data container is link to an user-account.
Also, the virtualization means you can use encryption for the data, but i don’t see the 'use_ssl' => true, who might be needed to establish the connection to s3…

Adding 'use_ssl' => true to the config did not help. I am still first met with:

Storage unauthorized. Authentication failed, verify the username, password and possibly tenant Storage is temporarily not available

And on subsequent attempts only:

Storage is temporarily not available

There’s not much in terms of errors to go on but I found this in the nextcloud.log:

{"reqId":"FOF4LN0zoeBz5lwFEUcG","level":3,"time":"May 27, 2022 07:40:56","remoteAddr":"xx.xx.xx.xx","user":"--","app":"core","method":"GET","url":"/login","message":"","userAgent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36","version":"23.0.3.2","exception":{"Exception":"OCP\\Files\\StorageNotAvailableException","Message":"","Code":1,"Trace":[{"file":"/var/www/html/lib/private/Files/Storage/Wrapper/Availability.php","line":242,"function":"checkAvailability","class":"OC\\Files\\Storage\\Wrapper\\Availability","type":"->","args":[]},{"file":"/var/www/html/lib/private/Files/Storage/Wrapper/Wrapper.php","line":227,"function":"file_exists","class":"OC\\Files\\Storage\\Wrapper\\Availability","type":"->","args":["appdata_ocif5mryemvh/js/core/merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Files/View.php","line":1344,"function":"file_exists","class":"OC\\Files\\Storage\\Wrapper\\Wrapper","type":"->","args":["appdata_ocif5mryemvh/js/core/merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Files/View.php","line":1393,"function":"getCacheEntry","class":"OC\\Files\\View","type":"->","args":[{"__class__":"OCA\\Files_Trashbin\\Storage","cache":{"__class__":"OC\\Files\\Cache\\Cache"},"scanner":null,"watcher":null,"propagator":null,"updater":null},"appdata_ocif5mryemvh/js/core/merged-template-prepend.js","/appdata_ocif5mryemvh/js/core/merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Files/Node/Root.php","line":200,"function":"getFileInfo","class":"OC\\Files\\View","type":"->","args":["/appdata_ocif5mryemvh/js/core/merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Files/Node/Folder.php","line":146,"function":"get","class":"OC\\Files\\Node\\Root","type":"->","args":["/appdata_ocif5mryemvh/js/core/merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Files/Node/Folder.php","line":155,"function":"get","class":"OC\\Files\\Node\\Folder","type":"->","args":["merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Files/SimpleFS/SimpleFolder.php","line":71,"function":"nodeExists","class":"OC\\Files\\Node\\Folder","type":"->","args":["merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Template/JSCombiner.php","line":116,"function":"fileExists","class":"OC\\Files\\SimpleFS\\SimpleFolder","type":"->","args":["merged-template-prepend.js"]},{"file":"/var/www/html/lib/private/Template/JSCombiner.php","line":102,"function":"isCached","class":"OC\\Template\\JSCombiner","type":"->","args":["merged-template-prepend.js",{"__class__":"OC\\Files\\SimpleFS\\SimpleFolder"}]},{"file":"/var/www/html/lib/private/Template/JSResourceLocator.php","line":115,"function":"process","class":"OC\\Template\\JSCombiner","type":"->","args":["/var/www/html","core/js/merged-template-prepend.json","core"]},{"file":"/var/www/html/lib/private/Template/JSResourceLocator.php","line":71,"function":"cacheAndAppendCombineJsonIfExist","class":"OC\\Template\\JSResourceLocator","type":"->","args":["/var/www/html","core/js/merged-template-prepend.json"]},{"file":"/var/www/html/lib/private/Template/ResourceLocator.php","line":78,"function":"doFind","class":"OC\\Template\\JSResourceLocator","type":"->","args":["js/merged-template-prepend"]},{"file":"/var/www/html/lib/private/TemplateLayout.php","line":388,"function":"find","class":"OC\\Template\\ResourceLocator","type":"->","args":[["core/js/dist/main","js/dist/files_fileinfo","js/dist/files_client","js/merged-template-prepend","core/l10n/en","And 13 more entries, set log level to debug to see all entries"]]},{"file":"/var/www/html/lib/private/TemplateLayout.php","line":216,"function":"findJavascriptFiles","class":"OC\\TemplateLayout","type":"::","args":[["core/js/dist/main","js/dist/files_fileinfo","js/dist/files_client","js/merged-template-prepend","core/l10n/en","And 13 more entries, set log level to debug to see all entries"]]},{"file":"/var/www/html/lib/private/legacy/OC_Template.php","line":182,"function":"__construct","class":"OC\\TemplateLayout","type":"->","args":["error",""]},{"file":"/var/www/html/lib/private/Template/Base.php","line":132,"function":"fetchPage","class":"OC_Template","type":"->","args":[]},{"file":"/var/www/html/lib/private/legacy/OC_Template.php","line":298,"function":"printPage","class":"OC\\Template\\Base","type":"->","args":[]},{"file":"/var/www/html/index.php","line":44,"function":"printErrorPage","class":"OC_Template","type":"::","args":["","Storage is temporarily not available",503]}],"File":"/var/www/html/lib/private/Files/Storage/Wrapper/Availability.php","Line":93,"Hint":"Storage is temporarily not available","CustomMessage":"--"}}