Repository: owncloud/ocis
Pull request #6427 (2023-06-01 11:59:24)

[full-ci] Service Accounts
Introduces service-accounts

kobergj: Introduces `auth-service` service
- [ ] Provide a README.md for that service in the root folder of that service.
  - Use CamelCase for section headers.
- [ ] For images and example files used in README.md:
  - Create a folder named `md-sources` on the same level where README.md is located. Put all the images and example files referenced by README.md into this folder.
  - Use absolute references like `https://raw.githubusercontent.com/owncloud/ocis/master/services/<service-name>/md-sources/file` to make the content accessible for both README.md and owncloud.dev
    bad `<img src="https://github.com/owncloud/ocis/blob/master/services/graph/images/mermaid-graph.svg" width="500" />`  
    good `<img src="https://raw.githubusercontent.com/owncloud/ocis/master/services/graph/images/mermaid-graph.svg" width="500" />`
- [ ] If new CLI command are introduced, that command must be described in readme.md.
- [ ] If new global envvar is introduced, the name must start with `OCIS_`.
- [ ] Add the service to the makefile in the ocis repo root.
- [ ] Make the service startable for binary and individual startup:
  - For single binary add service to `ocis/pkg/runtime`
  - For individual startup add service to `ocis/pkg/commands`
- [ ] Add the service to `.drone.star` to enable CI.
- [ ] Inform doc team in an _early stage_ to review the readme AND the environment variables created.
  - The description must reflect the behaviour AND usually has a positive code quality impact.
- [ ] Create proper description strings for envvars - see other services for examples, especially when it comes to multiple values. This must include:
  - base description, set of available values, description of each value.
- [ ] When suggestable commits are created for text changes and you agree, collect them to a batch and commit them. Do not forget to rebase locally to avoid overwriting the changes made.
- [ ] If new envvars are introduced which serve the same purpose but in multiple services, an additional envvar must be added at the beginning of the list starting with `OCIS_` (global envvar).
- [ ] Ensure that a service has a debug port
- [ ] If the new service introduces a new port:
  - the port must be added to [port-ranges.md](https://github.com/owncloud/ocis/blob/master/docs/services/port-ranges.md) and to the readme.md file.
- [ ] Make sure to have a function `FullDefaultConfig()` in `pkg/config/defaults/defaultconfig.go` of your service. It is needed to create the documentation.
ownclouders: :boom: Acceptance test [<strong>cs3ApiTests-ocis</strong>](https://drone.owncloud.com/owncloud/ocis/22864/47/1) failed. Further test are cancelled...
<!-- id: pr-6427 -->

sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![0.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [0.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![10.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20-16px.png '10.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [10.3% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![0.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [0.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![10.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20-16px.png '10.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [10.3% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [2 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![3.8%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '3.8%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [3.8% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![10.2%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20-16px.png '10.2%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [10.2% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [5 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![23.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '23.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [23.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![12.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20-16px.png '12.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [12.5% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: SonarCloud Quality Gate failed.&nbsp; &nbsp; [![Quality Gate failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![C](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/C-16px.png 'C')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [2 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [5 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![28.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '28.1%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [28.1% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![20.4%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '20.4%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [20.4% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)



![idea](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png 'idea') Catch issues before they fail your Quality Gate with our IDE extension ![sonarlint](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png 'sonarlint') [SonarLint](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=sonarcloud-welcome)
sonarcloud[bot]: SonarCloud Quality Gate failed.&nbsp; &nbsp; [![Quality Gate failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![C](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/C-16px.png 'C')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [1 Bug](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [5 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![28.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '28.1%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [28.1% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![20.4%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '20.4%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [20.4% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)



![idea](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png 'idea') Catch issues before they fail your Quality Gate with our IDE extension ![sonarlint](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png 'sonarlint') [SonarLint](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=sonarcloud-welcome)
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [11 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![28.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '28.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [28.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![21.2%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '21.2%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [21.2% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [11 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![28.3%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '28.3%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [28.3% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![21.2%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '21.2%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [21.2% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [11 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![26.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '26.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [26.5% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![21.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '21.1%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [21.1% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [6 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![29.6%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '29.6%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [29.6% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![21.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '21.1%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [21.1% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [11 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![0.0%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/0-16px.png '0.0%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [0.0% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![21.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '21.1%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [21.1% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: SonarCloud Quality Gate failed.&nbsp; &nbsp; [![Quality Gate failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/failed-16px.png 'Quality Gate failed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![C](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/C-16px.png 'C')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [1 Bug](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [11 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![29.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '29.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [29.5% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![20.9%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '20.9%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [20.9% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)



![idea](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/light_bulb-16px.png 'idea') Catch issues before they fail your Quality Gate with our IDE extension ![sonarlint](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/sonarlint-16px.png 'sonarlint') [SonarLint](https://www.sonarsource.com/products/sonarlint/features/connected-mode/?referrer=sonarcloud-welcome)
rhafer: @kobergj  Btw, in order to try this out I started to work on using the service account for user autoprovisioning and role-assignement: https://github.com/rhafer/ocis/tree/proxy-service-account
It mostly work, and gets us rid of some pretty ugly code. But still needs a few tweaks and cleanup.
sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [6 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![29.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '29.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [29.5% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![20.9%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20plus-16px.png '20.9%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [20.9% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [6 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![29.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '29.1%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [29.1% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![17.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20-16px.png '17.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [17.5% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=owncloud_ocis&pullRequest=6427)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=owncloud_ocis&pullRequest=6427&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL) [6 Code Smells](https://sonarcloud.io/project/issues?id=owncloud_ocis&pullRequest=6427&resolved=false&types=CODE_SMELL)

[![29.1%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/25-16px.png '29.1%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list) [29.1% Coverage](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_coverage&view=list)  
[![17.5%](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/20-16px.png '17.5%')](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list) [17.5% Duplication](https://sonarcloud.io/component_measures?id=owncloud_ocis&pullRequest=6427&metric=new_duplicated_lines_density&view=list)



Patch:
diff --git a/.drone.star b/.drone.star
index 0d14257a46e..63fdd685239 100644
--- a/.drone.star
+++ b/.drone.star
@@ -61,6 +61,7 @@ config = {
         "services/auth-basic",
         "services/auth-bearer",
         "services/auth-machine",
+        "services/auth-service",
         "services/eventhistory",
         "services/frontend",
         "services/gateway",
diff --git a/Makefile b/Makefile
index 8e648ffc237..f45ce7f1758 100644
--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,7 @@ OCIS_MODULES = \
 	services/auth-basic \
 	services/auth-bearer \
 	services/auth-machine \
+	services/auth-service \
 	services/eventhistory \
 	services/frontend \
 	services/gateway \
diff --git a/changelog/unreleased/bump-reva.md b/changelog/unreleased/bump-reva.md
index 36011d7ffe2..6f39bacc369 100644
--- a/changelog/unreleased/bump-reva.md
+++ b/changelog/unreleased/bump-reva.md
@@ -3,3 +3,4 @@ Enhancement: Bump Reva
 bumps reva version
 
 https://github.com/owncloud/ocis/pull/7138
+https://github.com/owncloud/ocis/pull/6427
diff --git a/changelog/unreleased/service-accounts.md b/changelog/unreleased/service-accounts.md
new file mode 100644
index 00000000000..9042c9cf5a1
--- /dev/null
+++ b/changelog/unreleased/service-accounts.md
@@ -0,0 +1,5 @@
+Enhancement: Introduce service accounts
+
+Introduces service accounts to avoid impersonating users in async processes
+
+https://github.com/owncloud/ocis/pull/6427
diff --git a/go.mod b/go.mod
index 285f8730055..b5615d4a738 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,7 @@ require (
 	github.com/coreos/go-oidc v2.2.1+incompatible
 	github.com/coreos/go-oidc/v3 v3.6.0
 	github.com/cs3org/go-cs3apis v0.0.0-20230516150832-730ac860c71d
-	github.com/cs3org/reva/v2 v2.16.1-0.20230828111521-594d4e103741
+	github.com/cs3org/reva/v2 v2.16.1-0.20230829124655-8ba013d7a129
 	github.com/disintegration/imaging v1.6.2
 	github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
 	github.com/egirna/icap-client v0.1.1
diff --git a/go.sum b/go.sum
index e797d7913c9..ba9abd5d870 100644
--- a/go.sum
+++ b/go.sum
@@ -858,8 +858,8 @@ github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo
 github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
 github.com/crewjam/saml v0.4.13 h1:TYHggH/hwP7eArqiXSJUvtOPNzQDyQ7vwmwEqlFWhMc=
 github.com/crewjam/saml v0.4.13/go.mod h1:igEejV+fihTIlHXYP8zOec3V5A8y3lws5bQBFsTm4gA=
-github.com/cs3org/reva/v2 v2.16.1-0.20230828111521-594d4e103741 h1:y3Tw/ZVGPSDRiCslFUESomgSUOa3SAguOJKpiSk9pls=
-github.com/cs3org/reva/v2 v2.16.1-0.20230828111521-594d4e103741/go.mod h1:RvhuweTFqzezjUFU0SIdTXakrEx9vJlMvQ7znPXSP1g=
+github.com/cs3org/reva/v2 v2.16.1-0.20230829124655-8ba013d7a129 h1:259bY0/RA/xOxN+7SnRryP5MXbj/GmXgNRqv4LYb8Co=
+github.com/cs3org/reva/v2 v2.16.1-0.20230829124655-8ba013d7a129/go.mod h1:RvhuweTFqzezjUFU0SIdTXakrEx9vJlMvQ7znPXSP1g=
 github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
diff --git a/ocis-pkg/config/config.go b/ocis-pkg/config/config.go
index bf16fd67174..77fec4f6fa8 100644
--- a/ocis-pkg/config/config.go
+++ b/ocis-pkg/config/config.go
@@ -9,6 +9,7 @@ import (
 	authbasic "github.com/owncloud/ocis/v2/services/auth-basic/pkg/config"
 	authbearer "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/config"
 	authmachine "github.com/owncloud/ocis/v2/services/auth-machine/pkg/config"
+	authservice "github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
 	eventhistory "github.com/owncloud/ocis/v2/services/eventhistory/pkg/config"
 	frontend "github.com/owncloud/ocis/v2/services/frontend/pkg/config"
 	gateway "github.com/owncloud/ocis/v2/services/gateway/pkg/config"
@@ -83,6 +84,7 @@ type Config struct {
 	AuthBasic         *authbasic.Config      `yaml:"auth_basic"`
 	AuthBearer        *authbearer.Config     `yaml:"auth_bearer"`
 	AuthMachine       *authmachine.Config    `yaml:"auth_machine"`
+	AuthService       *authservice.Config    `yaml:"auth_service"`
 	EventHistory      *eventhistory.Config   `yaml:"eventhistory"`
 	Frontend          *frontend.Config       `yaml:"frontend"`
 	Gateway           *gateway.Config        `yaml:"gateway"`
diff --git a/ocis-pkg/config/defaultconfig.go b/ocis-pkg/config/defaultconfig.go
index bfa03aee634..8639821bc76 100644
--- a/ocis-pkg/config/defaultconfig.go
+++ b/ocis-pkg/config/defaultconfig.go
@@ -8,6 +8,7 @@ import (
 	authbasic "github.com/owncloud/ocis/v2/services/auth-basic/pkg/config/defaults"
 	authbearer "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/config/defaults"
 	authmachine "github.com/owncloud/ocis/v2/services/auth-machine/pkg/config/defaults"
+	authservice "github.com/owncloud/ocis/v2/services/auth-service/pkg/config/defaults"
 	eventhistory "github.com/owncloud/ocis/v2/services/eventhistory/pkg/config/defaults"
 	frontend "github.com/owncloud/ocis/v2/services/frontend/pkg/config/defaults"
 	gateway "github.com/owncloud/ocis/v2/services/gateway/pkg/config/defaults"
@@ -55,6 +56,7 @@ func DefaultConfig() *Config {
 		AuthBasic:         authbasic.DefaultConfig(),
 		AuthBearer:        authbearer.DefaultConfig(),
 		AuthMachine:       authmachine.DefaultConfig(),
+		AuthService:       authservice.DefaultConfig(),
 		EventHistory:      eventhistory.DefaultConfig(),
 		Frontend:          frontend.DefaultConfig(),
 		Gateway:           gateway.DefaultConfig(),
diff --git a/ocis/pkg/command/auth-service.go b/ocis/pkg/command/auth-service.go
new file mode 100644
index 00000000000..e0796e9b61a
--- /dev/null
+++ b/ocis/pkg/command/auth-service.go
@@ -0,0 +1,30 @@
+package command
+
+import (
+	"github.com/owncloud/ocis/v2/ocis-pkg/config"
+	"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
+	"github.com/owncloud/ocis/v2/ocis-pkg/config/parser"
+	"github.com/owncloud/ocis/v2/ocis/pkg/command/helper"
+	"github.com/owncloud/ocis/v2/ocis/pkg/register"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/command"
+	"github.com/urfave/cli/v2"
+)
+
+// AuthServiceCommand is the entrypoint for the AuthService command.
+func AuthServiceCommand(cfg *config.Config) *cli.Command {
+	return &cli.Command{
+		Name:     cfg.AuthService.Service.Name,
+		Usage:    helper.SubcommandDescription(cfg.AuthService.Service.Name),
+		Category: "services",
+		Before: func(c *cli.Context) error {
+			configlog.Error(parser.ParseConfig(cfg, true))
+			cfg.AuthService.Commons = cfg.Commons
+			return nil
+		},
+		Subcommands: command.GetCommands(cfg.AuthService),
+	}
+}
+
+func init() {
+	register.AddCommand(AuthServiceCommand)
+}
diff --git a/ocis/pkg/command/services.go b/ocis/pkg/command/services.go
index df143b623e6..da634e2dfa0 100644
--- a/ocis/pkg/command/services.go
+++ b/ocis/pkg/command/services.go
@@ -15,6 +15,7 @@ import (
 	authbasic "github.com/owncloud/ocis/v2/services/auth-basic/pkg/command"
 	authbearer "github.com/owncloud/ocis/v2/services/auth-bearer/pkg/command"
 	authmachine "github.com/owncloud/ocis/v2/services/auth-machine/pkg/command"
+	authservice "github.com/owncloud/ocis/v2/services/auth-service/pkg/command"
 	eventhistory "github.com/owncloud/ocis/v2/services/eventhistory/pkg/command"
 	frontend "github.com/owncloud/ocis/v2/services/frontend/pkg/command"
 	gateway "github.com/owncloud/ocis/v2/services/gateway/pkg/command"
@@ -83,6 +84,11 @@ var svccmds = []register.Command{
 			cfg.AuthMachine.Commons = cfg.Commons
 		})
 	},
+	func(cfg *config.Config) *cli.Command {
+		return ServiceCommand(cfg, cfg.AuthService.Service.Name, authservice.GetCommands(cfg.AuthService), func(c *config.Config) {
+			cfg.AuthService.Commons = cfg.Commons
+		})
+	},
 	func(cfg *config.Config) *cli.Command {
 		return ServiceCommand(cfg, cfg.EventHistory.Service.Name, eventhistory.GetCommands(cfg.EventHistory), func(c *config.Config) {
 			cfg.EventHistory.Commons = cfg.Commons
diff --git a/ocis/pkg/init/init.go b/ocis/pkg/init/init.go
index 560af63144d..01aef0ceab1 100644
--- a/ocis/pkg/init/init.go
+++ b/ocis/pkg/init/init.go
@@ -55,10 +55,11 @@ type GraphApplication struct {
 }
 
 type GraphService struct {
-	Application GraphApplication
-	Events      Events
-	Spaces      InsecureService
-	Identity    LdapBasedService
+	Application    GraphApplication
+	Events         Events
+	Spaces         InsecureService
+	Identity       LdapBasedService
+	ServiceAccount ServiceAccount `yaml:"service_account"`
 }
 
 type ServiceUserPasswordsSettings struct {
@@ -101,7 +102,8 @@ type ThumbnailService struct {
 }
 
 type Search struct {
-	Events Events
+	Events         Events
+	ServiceAccount ServiceAccount `yaml:"service_account"`
 }
 
 type Audit struct {
@@ -113,8 +115,9 @@ type Sharing struct {
 }
 
 type StorageUsers struct {
-	Events  Events
-	MountID string `yaml:"mount_id"`
+	Events         Events
+	MountID        string         `yaml:"mount_id"`
+	ServiceAccount ServiceAccount `yaml:"service_account"`
 }
 
 type Gateway struct {
@@ -126,7 +129,16 @@ type StorageRegistry struct {
 }
 
 type Notifications struct {
-	Notifications struct{ Events Events } // The notifications config has a field called notifications
+	Notifications  struct{ Events Events } // The notifications config has a field called notifications
+	ServiceAccount ServiceAccount          `yaml:"service_account"`
+}
+
+type Userlog struct {
+	ServiceAccount ServiceAccount `yaml:"service_account"`
+}
+
+type AuthService struct {
+	ServiceAccount ServiceAccount `yaml:"service_account"`
 }
 
 type Nats struct {
@@ -136,6 +148,12 @@ type Nats struct {
 	}
 }
 
+// ServiceAccount is the configuration for the used service account
+type ServiceAccount struct {
+	ServiceAccountID     string `yaml:"service_account_id"`
+	ServiceAccountSecret string `yaml:"service_account_secret"`
+}
+
 // TODO: use the oCIS config struct instead of this custom struct
 // We can't use it right now, because it would need  "omitempty" on
 // all elements, in order to produce a slim config file with `ocis init`.
@@ -173,6 +191,8 @@ type OcisConfig struct {
 	Notifications     Notifications
 	Nats              Nats
 	Gateway           Gateway
+	Userlog           Userlog
+	AuthService       AuthService `yaml:"auth_service"`
 }
 
 func checkConfigPath(configPath string) error {
@@ -225,6 +245,7 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 	adminUserID := uuid.Must(uuid.NewV4()).String()
 	graphApplicationID := uuid.Must(uuid.NewV4()).String()
 	storageUsersMountID := uuid.Must(uuid.NewV4()).String()
+	serviceAccountID := uuid.Must(uuid.NewV4()).String()
 
 	idmServicePassword, err := generators.GenerateRandomPassword(passwordLength)
 	if err != nil {
@@ -266,6 +287,15 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 	if err != nil {
 		return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err)
 	}
+	serviceAccountSecret, err := generators.GenerateRandomPassword(passwordLength)
+	if err != nil {
+		return fmt.Errorf("could not generate random password for thumbnailsTransferSecret: %s", err)
+	}
+
+	serviceAccount := ServiceAccount{
+		ServiceAccountID:     serviceAccountID,
+		ServiceAccountSecret: serviceAccountSecret,
+	}
 
 	cfg := OcisConfig{
 		TokenManager: TokenManager{
@@ -319,6 +349,7 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 					BindPassword: idmServicePassword,
 				},
 			},
+			ServiceAccount: serviceAccount,
 		},
 		Thumbnails: ThumbnailService{
 			Thumbnail: ThumbnailSettings{
@@ -331,7 +362,20 @@ func CreateConfig(insecure, forceOverwrite bool, configPath, adminPassword strin
 			},
 		},
 		StorageUsers: StorageUsers{
-			MountID: storageUsersMountID,
+			MountID:        storageUsersMountID,
+			ServiceAccount: serviceAccount,
+		},
+		Userlog: Userlog{
+			ServiceAccount: serviceAccount,
+		},
+		AuthService: AuthService{
+			ServiceAccount: serviceAccount,
+		},
+		Search: Search{
+			ServiceAccount: serviceAccount,
+		},
+		Notifications: Notifications{
+			ServiceAccount: serviceAccount,
 		},
 	}
 
diff --git a/ocis/pkg/runtime/service/service.go b/ocis/pkg/runtime/service/service.go
index 027ef7e5514..1f43db921eb 100644
--- a/ocis/pkg/runtime/service/service.go
+++ b/ocis/pkg/runtime/service/service.go
@@ -26,6 +26,7 @@ import (
 	audit "github.com/owncloud/ocis/v2/services/audit/pkg/command"
 	authbasic "github.com/owncloud/ocis/v2/services/auth-basic/pkg/command"
 	authmachine "github.com/owncloud/ocis/v2/services/auth-machine/pkg/command"
+	authservice "github.com/owncloud/ocis/v2/services/auth-service/pkg/command"
 	eventhistory "github.com/owncloud/ocis/v2/services/eventhistory/pkg/command"
 	frontend "github.com/owncloud/ocis/v2/services/frontend/pkg/command"
 	gateway "github.com/owncloud/ocis/v2/services/gateway/pkg/command"
@@ -135,6 +136,11 @@ func NewService(options ...Option) (*Service, error) {
 		cfg.AuthMachine.Commons = cfg.Commons
 		return authmachine.Execute(cfg.AuthMachine)
 	})
+	reg(opts.Config.AuthService.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error {
+		cfg.AuthService.Context = ctx
+		cfg.AuthService.Commons = cfg.Commons
+		return authservice.Execute(cfg.AuthService)
+	})
 	reg(opts.Config.EventHistory.Service.Name, func(ctx context.Context, cfg *ociscfg.Config) error {
 		cfg.EventHistory.Context = ctx
 		cfg.EventHistory.Commons = cfg.Commons
diff --git a/services/auth-basic/README.md b/services/auth-basic/README.md
index c1b1db30bc9..c5afe10633c 100644
--- a/services/auth-basic/README.md
+++ b/services/auth-basic/README.md
@@ -6,6 +6,14 @@ The `auth-basic` service is responsible for validating authentication of incomin
 
 To enable `auth-basic`, you first must set `PROXY_ENABLE_BASIC_AUTH` to `true`.
 
+## The `auth` Service Family
+
+ocis uses serveral authentication services for different use cases. All services that start with `auth-` are part of the authentication service family. Each member authenticates requests with different scopes. As of now, these services exist:
+  -   `auth-basic` handles basic authentication
+  -   `auth-bearer` handles oidc authentication
+  -   `auth-machine` handles interservice authentication when a user is impersonated
+  -   `auth-service` handles interservice authentication when using service accounts
+
 ## Auth Managers
 
 Since the `auth-basic` service does not do any validation itself, it needs to be configured with an authentication manager. One can use the `AUTH_BASIC_AUTH_MANAGER` environment variable to configure this. Currently only one auth manager is supported: `"ldap"`
diff --git a/services/auth-bearer/README.md b/services/auth-bearer/README.md
index 6ada9310e31..024f6caa138 100644
--- a/services/auth-bearer/README.md
+++ b/services/auth-bearer/README.md
@@ -2,7 +2,15 @@
 
 The oCIS Auth Bearer service communicates with the configured OpenID Connect identity provider to authenticate requests. OpenID Connect is the default authentication mechanism for all clients: web, desktop and mobile. Basic auth is only used for testing and has to be explicity enabled.
 
-## Built in OpenID Connect identity provider
+## The `auth` Service Family
+
+ocis uses serveral authentication services for different use cases. All services that start with `auth-` are part of the authentication service family. Each member authenticates requests with different scopes. As of now, these services exist:
+  -   `auth-basic` handles basic authentication
+  -   `auth-bearer` handles oidc authentication
+  -   `auth-machine` handles interservice authentication when a user is impersonated
+  -   `auth-service` handles interservice authentication when using service accounts
+
+## Built in OpenID Connect Identity Provider
 
 A default oCIS deployment will start a [built in OpenID Connect identity provider](https://github.com/owncloud/ocis/tree/master/services/idp) but can be configured to use an external one as well.
 
diff --git a/services/auth-machine/README.md b/services/auth-machine/README.md
new file mode 100644
index 00000000000..b06664054d5
--- /dev/null
+++ b/services/auth-machine/README.md
@@ -0,0 +1,17 @@
+# Auth-Machine
+
+The oCIS Auth Machine is used for interservice communication when using user impersonation.
+
+ocis uses serveral authentication services for different use cases. All services that start with `auth-` are part of the authentication service family. Each member authenticates requests with different scopes. As of now, these services exist:
+  -   `auth-basic` handles basic authentication
+  -   `auth-bearer` handles oidc authentication
+  -   `auth-machine` handles interservice authentication when a user is impersonated
+  -   `auth-service` handles interservice authentication when using service accounts
+
+## User Impersonation
+
+When one ocis service is trying to talk to other ocis services, it needs to authenticate itself. To do so, it will impersonate a user using the `auth-machine` service. It will then act on behalf of this user. Any action will show up as action of this specific user, which gets visible when e.g. logged in the audit log.
+
+## Deprecation
+
+With the upcoming `auth-service` service, the `auth-machine` service will be used less frequently and is probably a candidate for deprecation.
diff --git a/services/auth-service/Makefile b/services/auth-service/Makefile
new file mode 100644
index 00000000000..bab3eec5894
--- /dev/null
+++ b/services/auth-service/Makefile
@@ -0,0 +1,37 @@
+SHELL := bash
+NAME := auth-service
+
+include ../../.make/recursion.mk
+
+############ tooling ############
+ifneq (, $(shell command -v go 2> /dev/null)) # suppress `command not found warnings` for non go targets in CI
+include ../../.bingo/Variables.mk
+endif
+
+############ go tooling ############
+include ../../.make/go.mk
+
+############ release ############
+include ../../.make/release.mk
+
+############ docs generate ############
+include ../../.make/docs.mk
+
+.PHONY: docs-generate
+docs-generate: config-docs-generate
+
+############ generate ############
+include ../../.make/generate.mk
+
+.PHONY: ci-go-generate
+ci-go-generate: # CI runs ci-node-generate automatically before this target
+
+.PHONY: ci-node-generate
+ci-node-generate:
+
+############ licenses ############
+.PHONY: ci-node-check-licenses
+ci-node-check-licenses:
+
+.PHONY: ci-node-save-licenses
+ci-node-save-licenses:
diff --git a/services/auth-service/README.md b/services/auth-service/README.md
new file mode 100644
index 00000000000..b34057d14c9
--- /dev/null
+++ b/services/auth-service/README.md
@@ -0,0 +1,19 @@
+# Auth-Service
+
+The ocis Auth Service is used to authenticate service accounts. Compared to normal accounts, service accounts are ocis internal only and not available as ordinary users like via LDAP.
+
+## The `auth` Service Family
+
+ocis uses serveral authentication services for different use cases. All services that start with `auth-` are part of the authentication service family. Each member authenticates requests with different scopes. As of now, these services exist:
+  -   `auth-basic` handles basic authentication
+  -   `auth-bearer` handles oidc authentication
+  -   `auth-machine` handles interservice authentication when a user is impersonated
+  -   `auth-service` handles interservice authentication when using service accounts
+
+## Service Accounts
+
+Service accounts are user accounts that are only used for inter service communication. The users have no personal space, do not show up in user lists and cannot login via the UI. Service accounts can be configured in the settings service. Only the `admin` service user is available for now. Additionally to the actions it can do via its role, all service users can stat all files on all spaces.
+
+## Configuring Service Accounts
+
+By using the envvars `OCIS_SERVICE_ACCOUNT_ID` and `OCIS_SERVICE_ACCOUNT_SECRET`, one can configure the ID and the secret of the service user. The secret can be rotated regulary to increase security. For activating a new secret, all services where the envvars are used need to be restarted. The secret is always and only stored in memory and never written into any persistant store. Though you can use any string for the service account, it is recommmended to use a UUIDv4 string.
diff --git a/services/auth-service/cmd/auth-service/main.go b/services/auth-service/cmd/auth-service/main.go
new file mode 100644
index 00000000000..bcc7a625ec7
--- /dev/null
+++ b/services/auth-service/cmd/auth-service/main.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"os"
+
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/command"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config/defaults"
+)
+
+func main() {
+	if err := command.Execute(defaults.DefaultConfig()); err != nil {
+		os.Exit(1)
+	}
+}
diff --git a/services/auth-service/pkg/command/health.go b/services/auth-service/pkg/command/health.go
new file mode 100644
index 00000000000..7470b581db3
--- /dev/null
+++ b/services/auth-service/pkg/command/health.go
@@ -0,0 +1,54 @@
+package command
+
+import (
+	"fmt"
+	"net/http"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config/parser"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/logging"
+	"github.com/urfave/cli/v2"
+)
+
+// Health is the entrypoint for the health command.
+func Health(cfg *config.Config) *cli.Command {
+	return &cli.Command{
+		Name:     "health",
+		Usage:    "check health status",
+		Category: "info",
+		Before: func(c *cli.Context) error {
+			return configlog.ReturnError(parser.ParseConfig(cfg))
+		},
+		Action: func(c *cli.Context) error {
+			logger := logging.Configure(cfg.Service.Name, cfg.Log)
+
+			resp, err := http.Get(
+				fmt.Sprintf(
+					"http://%s/healthz",
+					cfg.Debug.Addr,
+				),
+			)
+
+			if err != nil {
+				logger.Fatal().
+					Err(err).
+					Msg("Failed to request health check")
+			}
+
+			defer resp.Body.Close()
+
+			if resp.StatusCode != http.StatusOK {
+				logger.Fatal().
+					Int("code", resp.StatusCode).
+					Msg("Health seems to be in bad state")
+			}
+
+			logger.Debug().
+				Int("code", resp.StatusCode).
+				Msg("Health got a good state")
+
+			return nil
+		},
+	}
+}
diff --git a/services/auth-service/pkg/command/root.go b/services/auth-service/pkg/command/root.go
new file mode 100644
index 00000000000..2fd57dd7adb
--- /dev/null
+++ b/services/auth-service/pkg/command/root.go
@@ -0,0 +1,34 @@
+package command
+
+import (
+	"os"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/clihelper"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+	"github.com/urfave/cli/v2"
+)
+
+// GetCommands provides all commands for this service
+func GetCommands(cfg *config.Config) cli.Commands {
+	return []*cli.Command{
+		// start this service
+		Server(cfg),
+
+		// interaction with this service
+
+		// infos about this service
+		Health(cfg),
+		Version(cfg),
+	}
+}
+
+// Execute is the entry point for the ocis-auth-service command.
+func Execute(cfg *config.Config) error {
+	app := clihelper.DefaultApp(&cli.App{
+		Name:     "auth-service",
+		Usage:    "Provide service authentication for oCIS",
+		Commands: GetCommands(cfg),
+	})
+
+	return app.Run(os.Args)
+}
diff --git a/services/auth-service/pkg/command/server.go b/services/auth-service/pkg/command/server.go
new file mode 100644
index 00000000000..21bdeee3b4a
--- /dev/null
+++ b/services/auth-service/pkg/command/server.go
@@ -0,0 +1,99 @@
+package command
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path"
+
+	"github.com/cs3org/reva/v2/cmd/revad/runtime"
+	"github.com/gofrs/uuid"
+	"github.com/oklog/run"
+	"github.com/owncloud/ocis/v2/ocis-pkg/config/configlog"
+	"github.com/owncloud/ocis/v2/ocis-pkg/registry"
+	"github.com/owncloud/ocis/v2/ocis-pkg/sync"
+	"github.com/owncloud/ocis/v2/ocis-pkg/version"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config/parser"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/logging"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/revaconfig"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/server/debug"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/tracing"
+	"github.com/urfave/cli/v2"
+)
+
+// Server is the entry point for the server command.
+func Server(cfg *config.Config) *cli.Command {
+	return &cli.Command{
+		Name:     "server",
+		Usage:    fmt.Sprintf("start the %s service without runtime (unsupervised mode)", cfg.Service.Name),
+		Category: "server",
+		Before: func(c *cli.Context) error {
+			return configlog.ReturnFatal(parser.ParseConfig(cfg))
+		},
+		Action: func(c *cli.Context) error {
+			logger := logging.Configure(cfg.Service.Name, cfg.Log)
+			err := tracing.Configure(cfg, logger)
+			if err != nil {
+				return err
+			}
+			gr := run.Group{}
+			ctx, cancel := defineContext(cfg)
+
+			defer cancel()
+
+			pidFile := path.Join(os.TempDir(), "revad-"+cfg.Service.Name+"-"+uuid.Must(uuid.NewV4()).String()+".pid")
+
+			rcfg := revaconfig.AuthMachineConfigFromStruct(cfg)
+
+			gr.Add(func() error {
+				runtime.RunWithOptions(rcfg, pidFile, runtime.WithLogger(&logger.Logger))
+				return nil
+			}, func(err error) {
+				logger.Error().
+					Err(err).
+					Str("server", cfg.Service.Name).
+					Msg("Shutting down server")
+
+				cancel()
+			})
+
+			debugServer, err := debug.Server(
+				debug.Logger(logger),
+				debug.Context(ctx),
+				debug.Config(cfg),
+			)
+
+			if err != nil {
+				logger.Info().Err(err).Str("server", "debug").Msg("Failed to initialize server")
+				return err
+			}
+
+			gr.Add(debugServer.ListenAndServe, func(_ error) {
+				cancel()
+			})
+
+			if !cfg.Supervised {
+				sync.Trap(&gr, cancel)
+			}
+
+			grpcSvc := registry.BuildGRPCService(cfg.GRPC.Namespace+"."+cfg.Service.Name, uuid.Must(uuid.NewV4()).String(), cfg.GRPC.Addr, version.GetString())
+			if err := registry.RegisterService(ctx, grpcSvc, logger); err != nil {
+				logger.Fatal().Err(err).Msg("failed to register the grpc service")
+			}
+
+			return gr.Run()
+		},
+	}
+}
+
+// defineContext sets the context for the service. If there is a context configured it will create a new child from it,
+// if not, it will create a root context that can be cancelled.
+func defineContext(cfg *config.Config) (context.Context, context.CancelFunc) {
+	return func() (context.Context, context.CancelFunc) {
+		if cfg.Context == nil {
+			return context.WithCancel(context.Background())
+		}
+		return context.WithCancel(cfg.Context)
+	}()
+}
diff --git a/services/auth-service/pkg/command/version.go b/services/auth-service/pkg/command/version.go
new file mode 100644
index 00000000000..8bd660262d5
--- /dev/null
+++ b/services/auth-service/pkg/command/version.go
@@ -0,0 +1,50 @@
+package command
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/registry"
+	"github.com/owncloud/ocis/v2/ocis-pkg/version"
+
+	tw "github.com/olekukonko/tablewriter"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+	"github.com/urfave/cli/v2"
+)
+
+// Version prints the service versions of all running instances.
+func Version(cfg *config.Config) *cli.Command {
+	return &cli.Command{
+		Name:     "version",
+		Usage:    "print the version of this binary and the running service instances",
+		Category: "info",
+		Action: func(c *cli.Context) error {
+			fmt.Println("Version: " + version.GetString())
+			fmt.Printf("Compiled: %s\n", version.Compiled())
+			fmt.Println("")
+
+			reg := registry.GetRegistry()
+			services, err := reg.GetService(cfg.GRPC.Namespace + "." + cfg.Service.Name)
+			if err != nil {
+				fmt.Println(fmt.Errorf("could not get %s services from the registry: %v", cfg.Service.Name, err))
+				return err
+			}
+
+			if len(services) == 0 {
+				fmt.Println("No running " + cfg.Service.Name + " service found.")
+				return nil
+			}
+
+			table := tw.NewWriter(os.Stdout)
+			table.SetHeader([]string{"Version", "Address", "Id"})
+			table.SetAutoFormatHeaders(false)
+			for _, s := range services {
+				for _, n := range s.Nodes {
+					table.Append([]string{s.Version, n.Address, n.Id})
+				}
+			}
+			table.Render()
+			return nil
+		},
+	}
+}
diff --git a/services/auth-service/pkg/config/config.go b/services/auth-service/pkg/config/config.go
new file mode 100644
index 00000000000..0314497006c
--- /dev/null
+++ b/services/auth-service/pkg/config/config.go
@@ -0,0 +1,63 @@
+package config
+
+import (
+	"context"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
+)
+
+type Config struct {
+	Commons *shared.Commons `yaml:"-"` // don't use this directly as configuration for a service
+	Service Service         `yaml:"-"`
+	Tracing *Tracing        `yaml:"tracing"`
+	Log     *Log            `yaml:"log"`
+	Debug   Debug           `yaml:"debug"`
+
+	GRPC GRPCConfig `yaml:"grpc"`
+
+	TokenManager *TokenManager `yaml:"token_manager"`
+	Reva         *shared.Reva  `yaml:"reva"`
+
+	// TODO: when using multiple service accounts we need to find a way to configure them
+	ServiceAccount ServiceAccount `yaml:"service_account"`
+
+	Supervised bool            `yaml:"-"`
+	Context    context.Context `yaml:"-"`
+}
+type Tracing struct {
+	Enabled   bool   `yaml:"enabled" env:"OCIS_TRACING_ENABLED;AUTH_SERVICE_TRACING_ENABLED" desc:"Activates tracing."`
+	Type      string `yaml:"type" env:"OCIS_TRACING_TYPE;AUTH_SERVICE_TRACING_TYPE" desc:"The type of tracing. Defaults to '', which is the same as 'jaeger'. Allowed tracing types are 'jaeger' and '' as of now."`
+	Endpoint  string `yaml:"endpoint" env:"OCIS_TRACING_ENDPOINT;AUTH_SERVICE_TRACING_ENDPOINT" desc:"The endpoint of the tracing agent."`
+	Collector string `yaml:"collector" env:"OCIS_TRACING_COLLECTOR;AUTH_SERVICE_TRACING_COLLECTOR" desc:"The HTTP endpoint for sending spans directly to a collector, i.e. http://jaeger-collector:14268/api/traces. Only used if the tracing endpoint is unset."`
+}
+
+type Log struct {
+	Level  string `yaml:"level" env:"OCIS_LOG_LEVEL;AUTH_SERVICE_LOG_LEVEL" desc:"The log level. Valid values are: 'panic', 'fatal', 'error', 'warn', 'info', 'debug', 'trace'."`
+	Pretty bool   `yaml:"pretty" env:"OCIS_LOG_PRETTY;AUTH_SERVICE_LOG_PRETTY" desc:"Activates pretty log output."`
+	Color  bool   `yaml:"color" env:"OCIS_LOG_COLOR;AUTH_SERVICE_LOG_COLOR" desc:"Activates colorized log output."`
+	File   string `yaml:"file" env:"OCIS_LOG_FILE;AUTH_SERVICE_LOG_FILE" desc:"The path to the log file. Activates logging to this file if set."`
+}
+
+type Service struct {
+	Name string `yaml:"-"`
+}
+
+type Debug struct {
+	Addr   string `yaml:"addr" env:"AUTH_SERVICE_DEBUG_ADDR" desc:"Bind address of the debug server, where metrics, health, config and debug endpoints will be exposed."`
+	Token  string `yaml:"token" env:"AUTH_SERVICE_DEBUG_TOKEN" desc:"Token to secure the metrics endpoint."`
+	Pprof  bool   `yaml:"pprof" env:"AUTH_SERVICE_DEBUG_PPROF" desc:"Enables pprof, which can be used for profiling."`
+	Zpages bool   `yaml:"zpages" env:"AUTH_SERVICE_DEBUG_ZPAGES" desc:"Enables zpages, which can be used for collecting and viewing in-memory traces."`
+}
+
+type GRPCConfig struct {
+	Addr      string                 `yaml:"addr" env:"AUTH_SERVICE_GRPC_ADDR" desc:"The bind address of the GRPC service."`
+	TLS       *shared.GRPCServiceTLS `yaml:"tls"`
+	Namespace string                 `yaml:"-"`
+	Protocol  string                 `yaml:"protocol" env:"AUTH_SERVICE_GRPC_PROTOCOL" desc:"The transport protocol of the GRPC service."`
+}
+
+// ServiceAccount is the configuration for the used service account
+type ServiceAccount struct {
+	ServiceAccountID     string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;AUTH_SERVICE_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."`
+	ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;AUTH_SERVICE_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."`
+}
diff --git a/services/auth-service/pkg/config/defaults/defaultconfig.go b/services/auth-service/pkg/config/defaults/defaultconfig.go
new file mode 100644
index 00000000000..9f05e4fd233
--- /dev/null
+++ b/services/auth-service/pkg/config/defaults/defaultconfig.go
@@ -0,0 +1,83 @@
+package defaults
+
+import (
+	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
+	"github.com/owncloud/ocis/v2/ocis-pkg/structs"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+)
+
+// FullDefaultConfig returns a fully initialized default configuration
+func FullDefaultConfig() *config.Config {
+	cfg := DefaultConfig()
+	EnsureDefaults(cfg)
+	Sanitize(cfg)
+	return cfg
+}
+
+// DefaultConfig returns a basic default configuration
+func DefaultConfig() *config.Config {
+	return &config.Config{
+		Debug: config.Debug{
+			Addr:   "127.0.0.1:9169",
+			Token:  "",
+			Pprof:  false,
+			Zpages: false,
+		},
+		GRPC: config.GRPCConfig{
+			Addr:      "127.0.0.1:9199",
+			Namespace: "com.owncloud.api",
+			Protocol:  "tcp",
+		},
+		Service: config.Service{
+			Name: "auth-service",
+		},
+		Reva: shared.DefaultRevaConfig(),
+	}
+}
+
+// EnsureDefaults adds default values to the configuration if they are not set yet
+func EnsureDefaults(cfg *config.Config) {
+	// provide with defaults for shared logging, since we need a valid destination address for "envdecode".
+	if cfg.Log == nil && cfg.Commons != nil && cfg.Commons.Log != nil {
+		cfg.Log = &config.Log{
+			Level:  cfg.Commons.Log.Level,
+			Pretty: cfg.Commons.Log.Pretty,
+			Color:  cfg.Commons.Log.Color,
+			File:   cfg.Commons.Log.File,
+		}
+	} else if cfg.Log == nil {
+		cfg.Log = &config.Log{}
+	}
+	// provide with defaults for shared tracing, since we need a valid destination address for "envdecode".
+	if cfg.Tracing == nil && cfg.Commons != nil && cfg.Commons.Tracing != nil {
+		cfg.Tracing = &config.Tracing{
+			Enabled:   cfg.Commons.Tracing.Enabled,
+			Type:      cfg.Commons.Tracing.Type,
+			Endpoint:  cfg.Commons.Tracing.Endpoint,
+			Collector: cfg.Commons.Tracing.Collector,
+		}
+	} else if cfg.Tracing == nil {
+		cfg.Tracing = &config.Tracing{}
+	}
+
+	if cfg.Reva == nil && cfg.Commons != nil {
+		cfg.Reva = structs.CopyOrZeroValue(cfg.Commons.Reva)
+	}
+
+	if cfg.TokenManager == nil && cfg.Commons != nil && cfg.Commons.TokenManager != nil {
+		cfg.TokenManager = &config.TokenManager{
+			JWTSecret: cfg.Commons.TokenManager.JWTSecret,
+		}
+	} else if cfg.TokenManager == nil {
+		cfg.TokenManager = &config.TokenManager{}
+	}
+
+	if cfg.GRPC.TLS == nil && cfg.Commons != nil {
+		cfg.GRPC.TLS = structs.CopyOrZeroValue(cfg.Commons.GRPCServiceTLS)
+	}
+}
+
+// Sanitize sanitized the configuration
+func Sanitize(cfg *config.Config) {
+	// nothing to sanitize here atm
+}
diff --git a/services/auth-service/pkg/config/parser/parse.go b/services/auth-service/pkg/config/parser/parse.go
new file mode 100644
index 00000000000..2bb6b66305a
--- /dev/null
+++ b/services/auth-service/pkg/config/parser/parse.go
@@ -0,0 +1,42 @@
+package parser
+
+import (
+	"errors"
+
+	ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
+	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config/defaults"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/config/envdecode"
+)
+
+// ParseConfig loads configuration from known paths.
+func ParseConfig(cfg *config.Config) error {
+	_, err := ociscfg.BindSourcesToStructs(cfg.Service.Name, cfg)
+	if err != nil {
+		return err
+	}
+
+	defaults.EnsureDefaults(cfg)
+
+	// load all env variables relevant to the config in the current context.
+	if err := envdecode.Decode(cfg); err != nil {
+		// no environment variable set for this config is an expected "error"
+		if !errors.Is(err, envdecode.ErrNoTargetFieldsAreSet) {
+			return err
+		}
+	}
+
+	defaults.Sanitize(cfg)
+
+	return Validate(cfg)
+}
+
+func Validate(cfg *config.Config) error {
+	if cfg.TokenManager.JWTSecret == "" {
+		return shared.MissingJWTTokenError(cfg.Service.Name)
+	}
+
+	return nil
+}
diff --git a/services/auth-service/pkg/config/reva.go b/services/auth-service/pkg/config/reva.go
new file mode 100644
index 00000000000..14cb00d0892
--- /dev/null
+++ b/services/auth-service/pkg/config/reva.go
@@ -0,0 +1,6 @@
+package config
+
+// TokenManager is the config for using the reva token manager
+type TokenManager struct {
+	JWTSecret string `yaml:"jwt_secret" env:"OCIS_JWT_SECRET;AUTH_MACHINE_JWT_SECRET" desc:"The secret to mint and validate jwt tokens."`
+}
diff --git a/services/auth-service/pkg/logging/logging.go b/services/auth-service/pkg/logging/logging.go
new file mode 100644
index 00000000000..79b966fd223
--- /dev/null
+++ b/services/auth-service/pkg/logging/logging.go
@@ -0,0 +1,17 @@
+package logging
+
+import (
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+)
+
+// LoggerFromConfig initializes a service-specific logger instance.
+func Configure(name string, cfg *config.Log) log.Logger {
+	return log.NewLogger(
+		log.Name(name),
+		log.Level(cfg.Level),
+		log.Pretty(cfg.Pretty),
+		log.Color(cfg.Color),
+		log.File(cfg.File),
+	)
+}
diff --git a/services/auth-service/pkg/revaconfig/config.go b/services/auth-service/pkg/revaconfig/config.go
new file mode 100644
index 00000000000..4baaff7949b
--- /dev/null
+++ b/services/auth-service/pkg/revaconfig/config.go
@@ -0,0 +1,53 @@
+package revaconfig
+
+import (
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+)
+
+// AuthMachineConfigFromStruct will adapt an oCIS config struct into a reva mapstructure to start a reva service.
+func AuthMachineConfigFromStruct(cfg *config.Config) map[string]interface{} {
+	return map[string]interface{}{
+		"core": map[string]interface{}{
+			"tracing_enabled":      cfg.Tracing.Enabled,
+			"tracing_exporter":     cfg.Tracing.Type,
+			"tracing_endpoint":     cfg.Tracing.Endpoint,
+			"tracing_collector":    cfg.Tracing.Collector,
+			"tracing_service_name": cfg.Service.Name,
+		},
+		"shared": map[string]interface{}{
+			"jwt_secret":          cfg.TokenManager.JWTSecret,
+			"gatewaysvc":          cfg.Reva.Address,
+			"grpc_client_options": cfg.Reva.GetGRPCClientConfig(),
+		},
+		"grpc": map[string]interface{}{
+			"network": cfg.GRPC.Protocol,
+			"address": cfg.GRPC.Addr,
+			"tls_settings": map[string]interface{}{
+				"enabled":     cfg.GRPC.TLS.Enabled,
+				"certificate": cfg.GRPC.TLS.Cert,
+				"key":         cfg.GRPC.TLS.Key,
+			},
+			"services": map[string]interface{}{
+				"authprovider": map[string]interface{}{
+					"auth_manager": "serviceaccounts",
+					"auth_managers": map[string]interface{}{
+						"serviceaccounts": map[string]interface{}{
+							"service_accounts": []map[string]interface{}{
+								{
+									"id":     cfg.ServiceAccount.ServiceAccountID,
+									"secret": cfg.ServiceAccount.ServiceAccountSecret,
+								},
+							},
+						},
+					},
+				},
+			},
+			"interceptors": map[string]interface{}{
+				"prometheus": map[string]interface{}{
+					"namespace": "ocis",
+					"subsystem": "auth_service",
+				},
+			},
+		},
+	}
+}
diff --git a/services/auth-service/pkg/server/debug/option.go b/services/auth-service/pkg/server/debug/option.go
new file mode 100644
index 00000000000..b11a774e6df
--- /dev/null
+++ b/services/auth-service/pkg/server/debug/option.go
@@ -0,0 +1,50 @@
+package debug
+
+import (
+	"context"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+)
+
+// Option defines a single option function.
+type Option func(o *Options)
+
+// Options defines the available options for this package.
+type Options struct {
+	Logger  log.Logger
+	Context context.Context
+	Config  *config.Config
+}
+
+// newOptions initializes the available default options.
+func newOptions(opts ...Option) Options {
+	opt := Options{}
+
+	for _, o := range opts {
+		o(&opt)
+	}
+
+	return opt
+}
+
+// Logger provides a function to set the logger option.
+func Logger(val log.Logger) Option {
+	return func(o *Options) {
+		o.Logger = val
+	}
+}
+
+// Context provides a function to set the context option.
+func Context(val context.Context) Option {
+	return func(o *Options) {
+		o.Context = val
+	}
+}
+
+// Config provides a function to set the config option.
+func Config(val *config.Config) Option {
+	return func(o *Options) {
+		o.Config = val
+	}
+}
diff --git a/services/auth-service/pkg/server/debug/server.go b/services/auth-service/pkg/server/debug/server.go
new file mode 100644
index 00000000000..faedc717bb7
--- /dev/null
+++ b/services/auth-service/pkg/server/debug/server.go
@@ -0,0 +1,63 @@
+package debug
+
+import (
+	"io"
+	"net/http"
+
+	"github.com/owncloud/ocis/v2/ocis-pkg/service/debug"
+	"github.com/owncloud/ocis/v2/ocis-pkg/version"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+)
+
+// Server initializes the debug service and server.
+func Server(opts ...Option) (*http.Server, error) {
+	options := newOptions(opts...)
+
+	return debug.NewService(
+		debug.Logger(options.Logger),
+		debug.Name(options.Config.Service.Name),
+		debug.Version(version.GetString()),
+		debug.Address(options.Config.Debug.Addr),
+		debug.Token(options.Config.Debug.Token),
+		debug.Pprof(options.Config.Debug.Pprof),
+		debug.Zpages(options.Config.Debug.Zpages),
+		debug.Health(health(options.Config)),
+		debug.Ready(ready(options.Config)),
+		//debug.CorsAllowedOrigins(options.Config.HTTP.CORS.AllowedOrigins),
+		//debug.CorsAllowedMethods(options.Config.HTTP.CORS.AllowedMethods),
+		//debug.CorsAllowedHeaders(options.Config.HTTP.CORS.AllowedHeaders),
+		//debug.CorsAllowCredentials(options.Config.HTTP.CORS.AllowCredentials),
+	), nil
+}
+
+// health implements the health check.
+func health(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "text/plain")
+		w.WriteHeader(http.StatusOK)
+
+		// TODO: check if services are up and running
+
+		_, err := io.WriteString(w, http.StatusText(http.StatusOK))
+		// io.WriteString should not fail but if it does we want to know.
+		if err != nil {
+			panic(err)
+		}
+	}
+}
+
+// ready implements the ready check.
+func ready(cfg *config.Config) func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Set("Content-Type", "text/plain")
+		w.WriteHeader(http.StatusOK)
+
+		// TODO: check if services are up and running
+
+		_, err := io.WriteString(w, http.StatusText(http.StatusOK))
+		// io.WriteString should not fail but if it does we want to know.
+		if err != nil {
+			panic(err)
+		}
+	}
+}
diff --git a/services/auth-service/pkg/tracing/tracing.go b/services/auth-service/pkg/tracing/tracing.go
new file mode 100644
index 00000000000..357ca4ebb6d
--- /dev/null
+++ b/services/auth-service/pkg/tracing/tracing.go
@@ -0,0 +1,25 @@
+package tracing
+
+import (
+	"github.com/owncloud/ocis/v2/ocis-pkg/log"
+	"github.com/owncloud/ocis/v2/services/auth-service/pkg/config"
+	"go.opentelemetry.io/otel/trace"
+
+	pkgtrace "github.com/owncloud/ocis/v2/ocis-pkg/tracing"
+)
+
+var (
+	// TraceProvider is the global trace provider for the service.
+	TraceProvider = trace.NewNoopTracerProvider()
+)
+
+func Configure(cfg *config.Config, logger log.Logger) error {
+	var err error
+	if cfg.Tracing.Enabled {
+		if TraceProvider, err = pkgtrace.GetTraceProvider(cfg.Tracing.Endpoint, cfg.Tracing.Collector, cfg.Service.Name, cfg.Tracing.Type); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
diff --git a/services/gateway/pkg/config/config.go b/services/gateway/pkg/config/config.go
index 791064a67e4..c19f88785df 100644
--- a/services/gateway/pkg/config/config.go
+++ b/services/gateway/pkg/config/config.go
@@ -38,6 +38,7 @@ type Config struct {
 	AuthBasicEndpoint         string `yaml:"-"`
 	AuthBearerEndpoint        string `yaml:"-"`
 	AuthMachineEndpoint       string `yaml:"-"`
+	AuthServiceEndpoint       string `yaml:"-"`
 	StoragePublicLinkEndpoint string `yaml:"-"`
 	StorageUsersEndpoint      string `yaml:"-"`
 	StorageSharesEndpoint     string `yaml:"-"`
diff --git a/services/gateway/pkg/config/defaults/defaultconfig.go b/services/gateway/pkg/config/defaults/defaultconfig.go
index 3e0fd026e53..f66332a6792 100644
--- a/services/gateway/pkg/config/defaults/defaultconfig.go
+++ b/services/gateway/pkg/config/defaults/defaultconfig.go
@@ -55,6 +55,7 @@ func DefaultConfig() *config.Config {
 		AppRegistryEndpoint:       "com.owncloud.api.app-registry",
 		AuthBasicEndpoint:         "com.owncloud.api.auth-basic",
 		AuthMachineEndpoint:       "com.owncloud.api.auth-machine",
+		AuthServiceEndpoint:       "com.owncloud.api.auth-service",
 		GroupsEndpoint:            "com.owncloud.api.groups",
 		PermissionsEndpoint:       "com.owncloud.api.settings",
 		SharingEndpoint:           "com.owncloud.api.sharing",
diff --git a/services/gateway/pkg/revaconfig/config.go b/services/gateway/pkg/revaconfig/config.go
index d9c5df0d9eb..97ef2b44df6 100644
--- a/services/gateway/pkg/revaconfig/config.go
+++ b/services/gateway/pkg/revaconfig/config.go
@@ -80,9 +80,10 @@ func GatewayConfigFromStruct(cfg *config.Config, logger log.Logger) map[string]i
 					"drivers": map[string]interface{}{
 						"static": map[string]interface{}{
 							"rules": map[string]interface{}{
-								"basic":        cfg.AuthBasicEndpoint,
-								"machine":      cfg.AuthMachineEndpoint,
-								"publicshares": cfg.StoragePublicLinkEndpoint,
+								"basic":           cfg.AuthBasicEndpoint,
+								"machine":         cfg.AuthMachineEndpoint,
+								"publicshares":    cfg.StoragePublicLinkEndpoint,
+								"serviceaccounts": cfg.AuthServiceEndpoint,
 							},
 						},
 					},
diff --git a/services/graph/pkg/config/config.go b/services/graph/pkg/config/config.go
index 884cb0d2661..5c54def3bf6 100644
--- a/services/graph/pkg/config/config.go
+++ b/services/graph/pkg/config/config.go
@@ -30,8 +30,8 @@ type Config struct {
 	Identity    Identity    `yaml:"identity"`
 	Events      Events      `yaml:"events"`
 
-	MachineAuthAPIKey string   `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;USERLOG_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
-	Keycloak          Keycloak `yaml:"keycloak"`
+	Keycloak       Keycloak       `yaml:"keycloak"`
+	ServiceAccount ServiceAccount `yaml:"service_account"`
 
 	Context context.Context `yaml:"-"`
 }
@@ -137,3 +137,9 @@ type Keycloak struct {
 	UserRealm          string `yaml:"user_realm" env:"OCIS_KEYCLOAK_USER_REALM;GRAPH_KEYCLOAK_USER_REALM" desc:"The realm users are defined."`
 	InsecureSkipVerify bool   `yaml:"insecure_skip_verify" env:"OCIS_KEYCLOAK_INSECURE_SKIP_VERIFY;GRAPH_KEYCLOAK_INSECURE_SKIP_VERIFY" desc:"Disable TLS certificate validation for Keycloak connections. Do not set this in production environments."`
 }
+
+// ServiceAccount is the configuration for the used service account
+type ServiceAccount struct {
+	ServiceAccountID     string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;GRAPH_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."`
+	ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;GRAPH_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."`
+}
diff --git a/services/graph/pkg/config/defaults/defaultconfig.go b/services/graph/pkg/config/defaults/defaultconfig.go
index b12410a8694..004a031b5cd 100644
--- a/services/graph/pkg/config/defaults/defaultconfig.go
+++ b/services/graph/pkg/config/defaults/defaultconfig.go
@@ -159,10 +159,6 @@ func EnsureDefaults(cfg *config.Config) {
 		cfg.HTTP.TLS = cfg.Commons.HTTPServiceTLS
 	}
 
-	if cfg.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
-		cfg.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
-	}
-
 	if cfg.Identity.LDAP.GroupCreateBaseDN == "" {
 		cfg.Identity.LDAP.GroupCreateBaseDN = cfg.Identity.LDAP.GroupBaseDN
 	}
diff --git a/services/graph/pkg/service/v0/personaldata.go b/services/graph/pkg/service/v0/personaldata.go
index 435dc417c0e..ac1098a30ef 100644
--- a/services/graph/pkg/service/v0/personaldata.go
+++ b/services/graph/pkg/service/v0/personaldata.go
@@ -99,7 +99,7 @@ func (g Graph) GatherPersonalData(usr *user.User, ref *provider.Reference, token
 	}
 
 	// the context might already be cancelled. We need to impersonate the acting user again
-	ctx, err := utils.ImpersonateUser(usr, gatewayClient, g.config.MachineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(g.config.ServiceAccount.ServiceAccountID, gatewayClient, g.config.ServiceAccount.ServiceAccountSecret)
 	if err != nil {
 		g.logger.Error().Err(err).Str("userID", usr.GetId().GetOpaqueId()).Msg("cannot impersonate user")
 	}
@@ -162,7 +162,7 @@ func (g Graph) upload(u *user.User, data []byte, ref *provider.Reference, th str
 		return err
 	}
 
-	ctx, err := utils.ImpersonateUser(u, gatewayClient, g.config.MachineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(g.config.ServiceAccount.ServiceAccountID, gatewayClient, g.config.ServiceAccount.ServiceAccountSecret)
 	if err != nil {
 		return err
 	}
diff --git a/services/notifications/pkg/command/server.go b/services/notifications/pkg/command/server.go
index 49466fdf3df..8887bf1ff61 100644
--- a/services/notifications/pkg/command/server.go
+++ b/services/notifications/pkg/command/server.go
@@ -116,7 +116,7 @@ func Server(cfg *config.Config) *cli.Command {
 				logger.Fatal().Err(err).Str("addr", cfg.Notifications.RevaGateway).Msg("could not get reva gateway selector")
 			}
 			valueService := settingssvc.NewValueService("com.owncloud.api.settings", grpcClient)
-			svc := service.NewEventsNotifier(evts, channel, logger, gatewaySelector, valueService, cfg.Notifications.MachineAuthAPIKey, cfg.Notifications.EmailTemplatePath, cfg.WebUIURL)
+			svc := service.NewEventsNotifier(evts, channel, logger, gatewaySelector, valueService, cfg.ServiceAccount.ServiceAccountID, cfg.ServiceAccount.ServiceAccountSecret, cfg.Notifications.EmailTemplatePath, cfg.WebUIURL)
 
 			gr.Add(svc.Run, func(error) {
 				cancel()
diff --git a/services/notifications/pkg/config/config.go b/services/notifications/pkg/config/config.go
index 06419795862..170a6c94ccb 100644
--- a/services/notifications/pkg/config/config.go
+++ b/services/notifications/pkg/config/config.go
@@ -18,8 +18,9 @@ type Config struct {
 
 	WebUIURL string `yaml:"ocis_url" env:"OCIS_URL;NOTIFICATIONS_WEB_UI_URL" desc:"The public facing URL of the oCIS Web UI, used e.g. when sending notification eMails"`
 
-	Notifications Notifications        `yaml:"notifications"`
-	GRPCClientTLS shared.GRPCClientTLS `yaml:"grpc_client_tls"`
+	Notifications  Notifications        `yaml:"notifications"`
+	GRPCClientTLS  shared.GRPCClientTLS `yaml:"grpc_client_tls"`
+	ServiceAccount ServiceAccount       `yaml:"service_account"`
 
 	Context context.Context `yaml:"-"`
 }
@@ -28,7 +29,6 @@ type Config struct {
 type Notifications struct {
 	SMTP              SMTP                  `yaml:"SMTP"`
 	Events            Events                `yaml:"events"`
-	MachineAuthAPIKey string                `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;NOTIFICATIONS_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary to access resources from other services."`
 	EmailTemplatePath string                `yaml:"email_template_path" env:"OCIS_EMAIL_TEMPLATE_PATH;NOTIFICATIONS_EMAIL_TEMPLATE_PATH" desc:"Path to Email notification templates overriding embedded ones."`
 	TranslationPath   string                `yaml:"translation_path" env:"OCIS_TRANSLATION_PATH,NOTIFICATIONS_TRANSLATION_PATH" desc:"(optional) Set this to a path with custom translations to overwrite the builtin translations. Note that file and folder naming rules apply, see the documentation for more details."`
 	RevaGateway       string                `yaml:"reva_gateway" env:"OCIS_REVA_GATEWAY" desc:"CS3 gateway used to look up user metadata"`
@@ -55,3 +55,9 @@ type Events struct {
 	TLSRootCACertificate string `yaml:"tls_root_ca_certificate" env:"OCIS_EVENTS_TLS_ROOT_CA_CERTIFICATE;NOTIFICATIONS_EVENTS_TLS_ROOT_CA_CERTIFICATE" desc:"The root CA certificate used to validate the server's TLS certificate. If provided NOTIFICATIONS_EVENTS_TLS_INSECURE will be seen as false."`
 	EnableTLS            bool   `yaml:"enable_tls" env:"OCIS_EVENTS_ENABLE_TLS;NOTIFICATIONS_EVENTS_ENABLE_TLS" desc:"Enable TLS for the connection to the events broker. The events broker is the ocis service which receives and delivers events between the services.."`
 }
+
+// ServiceAccount is the configuration for the used service account
+type ServiceAccount struct {
+	ServiceAccountID     string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;NOTIFICATIONS_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."`
+	ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;NOTIFICATIONS_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."`
+}
diff --git a/services/notifications/pkg/config/defaults/defaultconfig.go b/services/notifications/pkg/config/defaults/defaultconfig.go
index f643baaa004..ab8e2eca7d8 100644
--- a/services/notifications/pkg/config/defaults/defaultconfig.go
+++ b/services/notifications/pkg/config/defaults/defaultconfig.go
@@ -73,9 +73,6 @@ func EnsureDefaults(cfg *config.Config) {
 		cfg.Tracing = &config.Tracing{}
 	}
 
-	if cfg.Notifications.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
-		cfg.Notifications.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
-	}
 	if cfg.Notifications.GRPCClientTLS == nil && cfg.Commons != nil {
 		cfg.Notifications.GRPCClientTLS = structs.CopyOrZeroValue(cfg.Commons.GRPCClientTLS)
 	}
diff --git a/services/notifications/pkg/config/parser/parse.go b/services/notifications/pkg/config/parser/parse.go
index 72ccfb22f99..af45c272621 100644
--- a/services/notifications/pkg/config/parser/parse.go
+++ b/services/notifications/pkg/config/parser/parse.go
@@ -4,7 +4,6 @@ import (
 	"errors"
 
 	ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
-	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/owncloud/ocis/v2/services/notifications/pkg/config"
 	"github.com/owncloud/ocis/v2/services/notifications/pkg/config/defaults"
 
@@ -34,9 +33,5 @@ func ParseConfig(cfg *config.Config) error {
 }
 
 func Validate(cfg *config.Config) error {
-	if cfg.Notifications.MachineAuthAPIKey == "" {
-		return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
-	}
-
 	return nil
 }
diff --git a/services/notifications/pkg/service/service.go b/services/notifications/pkg/service/service.go
index 9ac5ea291ec..a8a3bf0e7ae 100644
--- a/services/notifications/pkg/service/service.go
+++ b/services/notifications/pkg/service/service.go
@@ -42,32 +42,34 @@ func NewEventsNotifier(
 	logger log.Logger,
 	gatewaySelector pool.Selectable[gateway.GatewayAPIClient],
 	valueService settingssvc.ValueService,
-	machineAuthAPIKey, emailTemplatePath, ocisURL string) Service {
+	serviceAccountID, serviceAccountSecret, emailTemplatePath, ocisURL string) Service {
 
 	return eventsNotifier{
-		logger:            logger,
-		channel:           channel,
-		events:            events,
-		signals:           make(chan os.Signal, 1),
-		gatewaySelector:   gatewaySelector,
-		valueService:      valueService,
-		machineAuthAPIKey: machineAuthAPIKey,
-		emailTemplatePath: emailTemplatePath,
-		ocisURL:           ocisURL,
+		logger:               logger,
+		channel:              channel,
+		events:               events,
+		signals:              make(chan os.Signal, 1),
+		gatewaySelector:      gatewaySelector,
+		valueService:         valueService,
+		serviceAccountID:     serviceAccountID,
+		serviceAccountSecret: serviceAccountSecret,
+		emailTemplatePath:    emailTemplatePath,
+		ocisURL:              ocisURL,
 	}
 }
 
 type eventsNotifier struct {
-	logger            log.Logger
-	channel           channels.Channel
-	events            <-chan events.Event
-	signals           chan os.Signal
-	gatewaySelector   pool.Selectable[gateway.GatewayAPIClient]
-	valueService      settingssvc.ValueService
-	machineAuthAPIKey string
-	emailTemplatePath string
-	translationPath   string
-	ocisURL           string
+	logger               log.Logger
+	channel              channels.Channel
+	events               <-chan events.Event
+	signals              chan os.Signal
+	gatewaySelector      pool.Selectable[gateway.GatewayAPIClient]
+	valueService         settingssvc.ValueService
+	emailTemplatePath    string
+	translationPath      string
+	ocisURL              string
+	serviceAccountID     string
+	serviceAccountSecret string
 }
 
 func (s eventsNotifier) Run() error {
diff --git a/services/notifications/pkg/service/shares.go b/services/notifications/pkg/service/shares.go
index b6d7d844b93..58ec8bec54f 100644
--- a/services/notifications/pkg/service/shares.go
+++ b/services/notifications/pkg/service/shares.go
@@ -19,13 +19,13 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
 		return
 	}
 
-	ownerCtx, owner, err := utils.Impersonate(e.Sharer, gatewayClient, s.machineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
 	if err != nil {
-		logger.Error().Err(err).Msg("Could not impersonate sharer")
+		logger.Error().Err(err).Msg("Could not impersonate service user")
 		return
 	}
 
-	resourceInfo, err := s.getResourceInfo(ownerCtx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
+	resourceInfo, err := s.getResourceInfo(ctx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
 	if err != nil {
 		logger.Error().
 			Err(err).
@@ -41,13 +41,19 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
 		return
 	}
 
-	granteeList := s.ensureGranteeList(ownerCtx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
+	owner, err := utils.GetUser(e.Sharer, gatewayClient)
+	if err != nil {
+		logger.Error().Err(err).Msg("Could not get user")
+		return
+	}
+
+	granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
 	if granteeList == nil {
 		return
 	}
 
 	sharerDisplayName := owner.GetDisplayName()
-	recipientList, err := s.render(ownerCtx, email.ShareCreated,
+	recipientList, err := s.render(ctx, email.ShareCreated,
 		"ShareGrantee",
 		map[string]string{
 			"ShareSharer": sharerDisplayName,
@@ -58,7 +64,7 @@ func (s eventsNotifier) handleShareCreated(e events.ShareCreated) {
 		s.logger.Error().Err(err).Str("event", "ShareCreated").Msg("could not get render the email")
 		return
 	}
-	s.send(ownerCtx, recipientList)
+	s.send(ctx, recipientList)
 }
 
 func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
@@ -73,13 +79,13 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
 		return
 	}
 
-	ownerCtx, owner, err := utils.Impersonate(e.ShareOwner, gatewayClient, s.machineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
 	if err != nil {
 		logger.Error().Err(err).Msg("Could not impersonate sharer")
 		return
 	}
 
-	resourceInfo, err := s.getResourceInfo(ownerCtx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
+	resourceInfo, err := s.getResourceInfo(ctx, e.ItemID, &fieldmaskpb.FieldMask{Paths: []string{"name"}})
 	if err != nil {
 		logger.Error().
 			Err(err).
@@ -87,12 +93,18 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
 		return
 	}
 
-	granteeList := s.ensureGranteeList(ownerCtx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
+	owner, err := utils.GetUser(e.ShareOwner, gatewayClient)
+	if err != nil {
+		logger.Error().Err(err).Msg("Could not get user")
+		return
+	}
+
+	granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
 	if granteeList == nil {
 		return
 	}
 
-	recipientList, err := s.render(ownerCtx, email.ShareExpired,
+	recipientList, err := s.render(ctx, email.ShareExpired,
 		"ShareGrantee",
 		map[string]string{
 			"ShareFolder": resourceInfo.GetName(),
@@ -102,5 +114,5 @@ func (s eventsNotifier) handleShareExpired(e events.ShareExpired) {
 		s.logger.Error().Err(err).Str("event", "ShareExpired").Msg("could not get render the email")
 		return
 	}
-	s.send(ownerCtx, recipientList)
+	s.send(ctx, recipientList)
 }
diff --git a/services/notifications/pkg/service/spaces.go b/services/notifications/pkg/service/spaces.go
index 3922c81badc..32ed5bacc07 100644
--- a/services/notifications/pkg/service/spaces.go
+++ b/services/notifications/pkg/service/spaces.go
@@ -19,7 +19,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
 		return
 	}
 
-	executantCtx, executant, err := utils.Impersonate(e.Executant, gatewayClient, s.machineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
 	if err != nil {
 		logger.Error().
 			Err(err).
@@ -35,7 +35,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
 		return
 	}
 
-	resourceInfo, err := s.getResourceInfo(executantCtx, &resourceID, nil)
+	resourceInfo, err := s.getResourceInfo(ctx, &resourceID, nil)
 	if err != nil {
 		logger.Error().
 			Err(err).
@@ -51,16 +51,24 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
 		return
 	}
 
+	executant, err := utils.GetUser(e.Executant, gatewayClient)
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Msg("could not get user")
+		return
+	}
+
 	// Note: We're using the 'executantCtx' (authenticated as the share executant) here for requesting
 	// the Grantees of the shares. Ideally the notfication service would use some kind of service
 	// user for this.
-	granteeList := s.ensureGranteeList(executantCtx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
+	granteeList := s.ensureGranteeList(ctx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
 	if granteeList == nil {
 		return
 	}
 
 	sharerDisplayName := executant.GetDisplayName()
-	recipientList, err := s.render(executantCtx, email.SharedSpace,
+	recipientList, err := s.render(ctx, email.SharedSpace,
 		"SpaceGrantee",
 		map[string]string{
 			"SpaceSharer": sharerDisplayName,
@@ -71,7 +79,7 @@ func (s eventsNotifier) handleSpaceShared(e events.SpaceShared) {
 		s.logger.Error().Err(err).Str("event", "SharedSpace").Msg("could not get render the email")
 		return
 	}
-	s.send(executantCtx, recipientList)
+	s.send(ctx, recipientList)
 }
 
 func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
@@ -86,7 +94,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
 		return
 	}
 
-	executantCtx, executant, err := utils.Impersonate(e.Executant, gatewayClient, s.machineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
 	if err != nil {
 		logger.Error().Err(err).Msg("could not handle space unshared event")
 		return
@@ -100,7 +108,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
 		return
 	}
 
-	resourceInfo, err := s.getResourceInfo(executantCtx, &resourceID, nil)
+	resourceInfo, err := s.getResourceInfo(ctx, &resourceID, nil)
 	if err != nil {
 		logger.Error().
 			Err(err).
@@ -116,16 +124,24 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
 		return
 	}
 
+	executant, err := utils.GetUser(e.Executant, gatewayClient)
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Msg("could not get user")
+		return
+	}
+
 	// Note: We're using the 'executantCtx' (authenticated as the share executant) here for requesting
 	// the Grantees of the shares. Ideally the notfication service would use some kind of service
 	// user for this.
-	granteeList := s.ensureGranteeList(executantCtx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
+	granteeList := s.ensureGranteeList(ctx, executant.GetId(), e.GranteeUserID, e.GranteeGroupID)
 	if granteeList == nil {
 		return
 	}
 
 	sharerDisplayName := executant.GetDisplayName()
-	recipientList, err := s.render(executantCtx, email.UnsharedSpace,
+	recipientList, err := s.render(ctx, email.UnsharedSpace,
 		"SpaceGrantee",
 		map[string]string{
 			"SpaceSharer": sharerDisplayName,
@@ -136,7 +152,7 @@ func (s eventsNotifier) handleSpaceUnshared(e events.SpaceUnshared) {
 		s.logger.Error().Err(err).Str("event", "UnsharedSpace").Msg("Could not get render the email")
 		return
 	}
-	s.send(executantCtx, recipientList)
+	s.send(ctx, recipientList)
 }
 
 func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExpired) {
@@ -151,18 +167,26 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp
 		return
 	}
 
-	ownerCtx, owner, err := utils.Impersonate(e.SpaceOwner, gatewayClient, s.machineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(s.serviceAccountID, gatewayClient, s.serviceAccountSecret)
 	if err != nil {
 		logger.Error().Err(err).Msg("Could not impersonate sharer")
 		return
 	}
 
-	granteeList := s.ensureGranteeList(ownerCtx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
+	owner, err := utils.GetUser(e.SpaceOwner, gatewayClient)
+	if err != nil {
+		logger.Error().
+			Err(err).
+			Msg("could not get user")
+		return
+	}
+
+	granteeList := s.ensureGranteeList(ctx, owner.GetId(), e.GranteeUserID, e.GranteeGroupID)
 	if granteeList == nil {
 		return
 	}
 
-	recipientList, err := s.render(ownerCtx, email.MembershipExpired,
+	recipientList, err := s.render(ctx, email.MembershipExpired,
 		"SpaceGrantee",
 		map[string]string{
 			"SpaceName": e.SpaceName,
@@ -172,5 +196,5 @@ func (s eventsNotifier) handleSpaceMembershipExpired(e events.SpaceMembershipExp
 		s.logger.Error().Err(err).Str("event", "SpaceUnshared").Msg("could not get render the email")
 		return
 	}
-	s.send(ownerCtx, recipientList)
+	s.send(ctx, recipientList)
 }
diff --git a/services/search/pkg/config/config.go b/services/search/pkg/config/config.go
index 9b66cd80874..6b2399959f1 100644
--- a/services/search/pkg/config/config.go
+++ b/services/search/pkg/config/config.go
@@ -29,7 +29,13 @@ type Config struct {
 	Extractor                  Extractor             `yaml:"extractor"`
 	ContentExtractionSizeLimit uint64                `yaml:"content_extraction_size_limit" env:"SEARCH_CONTENT_EXTRACTION_SIZE_LIMIT" desc:"Maximum file size in bytes that is allowed for content extraction."`
 
-	MachineAuthAPIKey string `yaml:"machine_auth_api_key" env:"OCIS_MACHINE_AUTH_API_KEY;SEARCH_MACHINE_AUTH_API_KEY" desc:"Machine auth API key used to validate internal requests necessary for the access to resources from other services."`
+	ServiceAccount ServiceAccount `yaml:"service_account"`
 
 	Context context.Context `yaml:"-"`
 }
+
+// ServiceAccount is the configuration for the used service account
+type ServiceAccount struct {
+	ServiceAccountID     string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;SEARCH_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."`
+	ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;SEARCH_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."`
+}
diff --git a/services/search/pkg/config/defaults/defaultconfig.go b/services/search/pkg/config/defaults/defaultconfig.go
index 50fe01b12c0..c8009d30b4d 100644
--- a/services/search/pkg/config/defaults/defaultconfig.go
+++ b/services/search/pkg/config/defaults/defaultconfig.go
@@ -54,7 +54,6 @@ func DefaultConfig() *config.Config {
 			EnableTLS:        false,
 		},
 		ContentExtractionSizeLimit: 20 * 1024 * 1024, // Limit content extraction to <20MB files by default
-		MachineAuthAPIKey:          "",
 	}
 }
 
@@ -91,10 +90,6 @@ func EnsureDefaults(cfg *config.Config) {
 		cfg.TokenManager = &config.TokenManager{}
 	}
 
-	if cfg.MachineAuthAPIKey == "" && cfg.Commons != nil && cfg.Commons.MachineAuthAPIKey != "" {
-		cfg.MachineAuthAPIKey = cfg.Commons.MachineAuthAPIKey
-	}
-
 	if cfg.Reva == nil && cfg.Commons != nil {
 		cfg.Reva = structs.CopyOrZeroValue(cfg.Commons.Reva)
 	}
diff --git a/services/search/pkg/config/parser/parse.go b/services/search/pkg/config/parser/parse.go
index fafad8f8482..9b3c124ae91 100644
--- a/services/search/pkg/config/parser/parse.go
+++ b/services/search/pkg/config/parser/parse.go
@@ -4,7 +4,6 @@ import (
 	"errors"
 
 	ociscfg "github.com/owncloud/ocis/v2/ocis-pkg/config"
-	"github.com/owncloud/ocis/v2/ocis-pkg/shared"
 	"github.com/owncloud/ocis/v2/services/search/pkg/config"
 	"github.com/owncloud/ocis/v2/services/search/pkg/config/defaults"
 
@@ -34,8 +33,5 @@ func ParseConfig(cfg *config.Config) error {
 }
 
 func Validate(cfg *config.Config) error {
-	if cfg.MachineAuthAPIKey == "" {
-		return shared.MissingMachineAuthApiKeyError(cfg.Service.Name)
-	}
 	return nil
 }
diff --git a/services/search/pkg/search/search.go b/services/search/pkg/search/search.go
index d8d595ea809..26213790ac8 100644
--- a/services/search/pkg/search/search.go
+++ b/services/search/pkg/search/search.go
@@ -8,18 +8,14 @@ import (
 	"strings"
 
 	gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
-	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
 	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
-	"github.com/cs3org/reva/v2/pkg/errtypes"
 	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
 	"github.com/cs3org/reva/v2/pkg/storagespace"
 	"github.com/cs3org/reva/v2/pkg/utils"
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
 	searchmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/search/v0"
 	"github.com/owncloud/ocis/v2/services/search/pkg/engine"
-	"google.golang.org/grpc/metadata"
 )
 
 var scopeRegex = regexp.MustCompile(`scope:\s*([^" "\n\r]*)`)
@@ -71,30 +67,14 @@ func logDocCount(engine engine.Engine, logger log.Logger) {
 	logger.Debug().Interface("count", c).Msg("new document count")
 }
 
-func getAuthContext(owner *user.User, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], secret string, logger log.Logger) (context.Context, error) {
+func getAuthContext(serviceAccountID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], secret string, logger log.Logger) (context.Context, error) {
 	gatewayClient, err := gatewaySelector.Next()
 	if err != nil {
 		logger.Error().Err(err).Msg("could not get reva gatewayClient")
 		return nil, err
 	}
 
-	ownerCtx := ctxpkg.ContextSetUser(context.Background(), owner)
-	authRes, err := gatewayClient.Authenticate(ownerCtx, &gateway.AuthenticateRequest{
-		Type:         "machine",
-		ClientId:     "userid:" + owner.GetId().GetOpaqueId(),
-		ClientSecret: secret,
-	})
-
-	if err == nil && authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
-		err = errtypes.NewErrtypeFromStatus(authRes.Status)
-	}
-
-	if err != nil {
-		logger.Error().Err(err).Interface("owner", owner).Interface("authRes", authRes).Msg("error using machine auth")
-		return nil, err
-	}
-
-	return metadata.AppendToOutgoingContext(ownerCtx, ctxpkg.TokenHeader, authRes.Token), nil
+	return utils.GetServiceUserContext(serviceAccountID, gatewayClient, secret)
 }
 
 func statResource(ctx context.Context, ref *provider.Reference, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], logger log.Logger) (*provider.StatResponse, error) {
diff --git a/services/search/pkg/search/service.go b/services/search/pkg/search/service.go
index 43707a07fe1..1ce6a9c32ae 100644
--- a/services/search/pkg/search/service.go
+++ b/services/search/pkg/search/service.go
@@ -57,7 +57,9 @@ type Service struct {
 	gatewaySelector pool.Selectable[gateway.GatewayAPIClient]
 	engine          engine.Engine
 	extractor       content.Extractor
-	secret          string
+
+	serviceAccountID     string
+	serviceAccountSecret string
 }
 
 var errSkipSpace error
@@ -67,9 +69,11 @@ func NewService(gatewaySelector pool.Selectable[gateway.GatewayAPIClient], eng e
 	var s = &Service{
 		gatewaySelector: gatewaySelector,
 		engine:          eng,
-		secret:          cfg.MachineAuthAPIKey,
 		logger:          logger,
 		extractor:       extractor,
+
+		serviceAccountID:     cfg.ServiceAccount.ServiceAccountID,
+		serviceAccountSecret: cfg.ServiceAccount.ServiceAccountSecret,
 	}
 
 	return s
@@ -291,21 +295,12 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest,
 			return nil, err
 		}
 
-		var ownerCtx context.Context
-		if space.Owner.Id.Type == user.UserType_USER_TYPE_SPACE_OWNER {
-			// We can't impersonate SPACE_OWNER users and have to fall back to using the user auth instead,
-			// which will not resolve the absolute path of the share in the space but only the part the user
-			// is allowed to see.
-			// In the future this problem can be solved using service accounts.
-			ownerCtx = ctx
-		} else {
-			ownerCtx, err = getAuthContext(&user.User{Id: space.Owner.Id}, s.gatewaySelector, s.secret, s.logger)
-			if err != nil {
-				return nil, err
-			}
+		serviceCtx, err := getAuthContext(s.serviceAccountID, s.gatewaySelector, s.serviceAccountSecret, s.logger)
+		if err != nil {
+			return nil, err
 		}
 
-		gpRes, err := gatewayClient.GetPath(ownerCtx, &provider.GetPathRequest{
+		gpRes, err := gatewayClient.GetPath(serviceCtx, &provider.GetPathRequest{
 			ResourceId: space.Root,
 		})
 		if err != nil {
@@ -380,7 +375,7 @@ func (s *Service) searchIndex(ctx context.Context, req *searchsvc.SearchRequest,
 
 // IndexSpace (re)indexes all resources of a given space.
 func (s *Service) IndexSpace(spaceID *provider.StorageSpaceId, uID *user.UserId) error {
-	ownerCtx, err := getAuthContext(&user.User{Id: uID}, s.gatewaySelector, s.secret, s.logger)
+	ownerCtx, err := getAuthContext(s.serviceAccountID, s.gatewaySelector, s.serviceAccountSecret, s.logger)
 	if err != nil {
 		return err
 	}
@@ -510,7 +505,7 @@ func (s *Service) MoveItem(ref *provider.Reference, uID *user.UserId) {
 }
 
 func (s *Service) resInfo(uID *user.UserId, ref *provider.Reference) (context.Context, *provider.StatResponse, string) {
-	ownerCtx, err := getAuthContext(&user.User{Id: uID}, s.gatewaySelector, s.secret, s.logger)
+	ownerCtx, err := getAuthContext(s.serviceAccountID, s.gatewaySelector, s.serviceAccountSecret, s.logger)
 	if err != nil {
 		return nil, nil, ""
 	}
diff --git a/services/settings/README.md b/services/settings/README.md
index 7a8008c917f..256cf4b290a 100644
--- a/services/settings/README.md
+++ b/services/settings/README.md
@@ -8,7 +8,7 @@ The settings service is currently used for managing the:
 *   possible user roles and their respective permissions,
 *   assignment of roles to users.
 
-As an example, user profile settings that can be changed in the Web UI must be persistent.  
+As an example, user profile settings that can be changed in the Web UI must be persistent.
 
 The settings service supports two different backends for persisting the data. The backend can be set via the `SETTINGS_STORE_TYPE` environment variable. Supported values are:
 
@@ -67,3 +67,7 @@ Infinite Scale services can register *settings bundles* with the settings servic
 ## Settings Usage
 
 Services can set or query ocis *setting values* of a user from settings bundles.
+
+## Service Accounts
+
+The settings service needs to know the ID's of service accounts but it doesn't need their secrets. Currently only one service account can be configured which has the admin role. This can be set with the `SETTINGS_SERVICE_ACCOUNT_ID_ADMIN` envvar, but it will also pick up the global `OCIS_SERVICE_ACCOUNT_ID` envvar. Also see the 'auth-service' service description for additional details.
diff --git a/services/settings/pkg/config/config.go b/services/settings/pkg/config/config.go
index e606b18656b..2ca13dff155 100644
--- a/services/settings/pkg/config/config.go
+++ b/services/settings/pkg/config/config.go
@@ -37,6 +37,8 @@ type Config struct {
 
 	SetupDefaultAssignments bool `yaml:"set_default_assignments" env:"SETTINGS_SETUP_DEFAULT_ASSIGNMENTS;IDM_CREATE_DEMO_USERS" desc:"The default role assignments the demo users should be setup."`
 
+	ServiceAccountIDAdmin string `yaml:"service_account_id_admin" env:"OCIS_SERVICE_ACCOUNT_ID;SETTINGS_SERVICE_ACCOUNT_ID_ADMIN" desc:"The ID of the service account having the admin role. See the 'auth-service' service description for more details."`
+
 	Context context.Context `yaml:"-"`
 }
 
diff --git a/services/settings/pkg/config/defaults/defaultconfig.go b/services/settings/pkg/config/defaults/defaultconfig.go
index 64866ae3f8e..8bd1126fe8c 100644
--- a/services/settings/pkg/config/defaults/defaultconfig.go
+++ b/services/settings/pkg/config/defaults/defaultconfig.go
@@ -64,8 +64,9 @@ func DefaultConfig() *config.Config {
 				TTL:            time.Minute * 10,
 			},
 		},
-		BundlesPath: "",
-		Bundles:     nil,
+		BundlesPath:           "",
+		Bundles:               nil,
+		ServiceAccountIDAdmin: "service-user-id",
 	}
 }
 
diff --git a/services/settings/pkg/store/defaults/defaults.go b/services/settings/pkg/store/defaults/defaults.go
index f3de7739420..96ca44acfd8 100644
--- a/services/settings/pkg/store/defaults/defaults.go
+++ b/services/settings/pkg/store/defaults/defaults.go
@@ -822,6 +822,11 @@ func DefaultRoleAssignments(cfg *config.Config) []*settingsmsg.UserRoleAssignmen
 				AccountUuid: "534bb038-6f9d-4093-946f-133be61fa4e7",
 				RoleId:      BundleUUIDRoleSpaceAdmin,
 			},
+			{
+				// service user
+				AccountUuid: "service-user-id",
+				RoleId:      BundleUUIDRoleAdmin,
+			},
 		}
 	}
 
@@ -833,5 +838,12 @@ func DefaultRoleAssignments(cfg *config.Config) []*settingsmsg.UserRoleAssignmen
 		})
 	}
 
+	if cfg.ServiceAccountIDAdmin != "" {
+		assignments = append(assignments, &settingsmsg.UserRoleAssignment{
+			AccountUuid: cfg.ServiceAccountIDAdmin,
+			RoleId:      BundleUUIDRoleAdmin,
+		})
+	}
+
 	return assignments
 }
diff --git a/services/storage-users/pkg/config/config.go b/services/storage-users/pkg/config/config.go
index 5423259a0f4..88e326f42cf 100644
--- a/services/storage-users/pkg/config/config.go
+++ b/services/storage-users/pkg/config/config.go
@@ -38,6 +38,7 @@ type Config struct {
 	ReadOnly          bool              `yaml:"readonly" env:"STORAGE_USERS_READ_ONLY" desc:"Set this storage to be read-only."`
 	UploadExpiration  int64             `yaml:"upload_expiration" env:"STORAGE_USERS_UPLOAD_EXPIRATION" desc:"Duration in seconds after which uploads will expire. Note that when setting this to a low number, uploads could be cancelled before they are finished and return a 403 to the user."`
 	Tasks             Tasks             `yaml:"tasks"`
+	ServiceAccount    ServiceAccount    `yaml:"service_account"`
 
 	Supervised bool            `yaml:"-"`
 	Context    context.Context `yaml:"-"`
@@ -278,3 +279,9 @@ type PurgeTrashBin struct {
 	PersonalDeleteBefore time.Duration `yaml:"personal_delete_before" env:"STORAGE_USERS_PURGE_TRASH_BIN_PERSONAL_DELETE_BEFORE" desc:"Specifies the period of time in which items that have been in the personal trash-bin for longer than this value should be deleted. A value of 0 means no automatic deletion. The value is human-readable, valid values are '24h', '60m', '60s' etc."`
 	ProjectDeleteBefore  time.Duration `yaml:"project_delete_before" env:"STORAGE_USERS_PURGE_TRASH_BIN_PROJECT_DELETE_BEFORE" desc:"Specifies the period of time in which items that have been in the project trash-bin for longer than this value should be deleted. A value of 0 means no automatic deletion. The value is human-readable, valid values are '24h', '60m', '60s' etc."`
 }
+
+// ServiceAccount is the configuration for the used service account
+type ServiceAccount struct {
+	ServiceAccountID     string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;STORAGE_USERS_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."`
+	ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;STORAGE_USERS_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."`
+}
diff --git a/services/storage-users/pkg/event/service.go b/services/storage-users/pkg/event/service.go
index 91384f75d59..cd2e9b96738 100644
--- a/services/storage-users/pkg/event/service.go
+++ b/services/storage-users/pkg/event/service.go
@@ -4,7 +4,6 @@ import (
 	"time"
 
 	apiGateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
-	apiUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	"github.com/cs3org/reva/v2/pkg/events"
 	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
 	"github.com/owncloud/ocis/v2/ocis-pkg/log"
@@ -53,11 +52,6 @@ func (s Service) Run() error {
 				executionTime = time.Now()
 			}
 
-			executantID := ev.ExecutantID
-			if executantID == nil {
-				executantID = &apiUser.UserId{OpaqueId: s.config.Tasks.PurgeTrashBin.UserID}
-			}
-
 			tasks := map[task.SpaceType]time.Time{
 				task.Project:  executionTime.Add(-s.config.Tasks.PurgeTrashBin.ProjectDeleteBefore),
 				task.Personal: executionTime.Add(-s.config.Tasks.PurgeTrashBin.PersonalDeleteBefore),
@@ -70,7 +64,7 @@ func (s Service) Run() error {
 					continue
 				}
 
-				if err = task.PurgeTrashBin(executantID, deleteBefore, spaceType, s.gatewaySelector, s.config.Commons.MachineAuthAPIKey); err != nil {
+				if err = task.PurgeTrashBin(s.config.ServiceAccount.ServiceAccountID, deleteBefore, spaceType, s.gatewaySelector, s.config.ServiceAccount.ServiceAccountSecret); err != nil {
 					errs = append(errs, err)
 				}
 			}
diff --git a/services/storage-users/pkg/task/trash_bin.go b/services/storage-users/pkg/task/trash_bin.go
index daf49fad1ed..e7bb2cb63d4 100644
--- a/services/storage-users/pkg/task/trash_bin.go
+++ b/services/storage-users/pkg/task/trash_bin.go
@@ -1,11 +1,9 @@
 package task
 
 import (
-	"fmt"
 	"time"
 
 	apiGateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
-	apiUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	apiRpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
 	apiProvider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
 	"github.com/cs3org/reva/v2/pkg/errtypes"
@@ -17,18 +15,18 @@ import (
 // the provided executantID must have space access.
 // removeBefore specifies how long an item must be in the trash-bin to be deleted,
 // items that stay there for a shorter time are ignored and kept in place.
-func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceType SpaceType, gatewaySelector pool.Selectable[apiGateway.GatewayAPIClient], machineAuthAPIKey string) error {
+func PurgeTrashBin(serviceAccountID string, deleteBefore time.Time, spaceType SpaceType, gatewaySelector pool.Selectable[apiGateway.GatewayAPIClient], serviceAccountSecret string) error {
 	gatewayClient, err := gatewaySelector.Next()
 	if err != nil {
 		return err
 	}
 
-	executantCtx, _, err := utils.Impersonate(executantID, gatewayClient, machineAuthAPIKey)
+	ctx, err := utils.GetServiceUserContext(serviceAccountID, gatewayClient, serviceAccountSecret)
 	if err != nil {
 		return err
 	}
 
-	listStorageSpacesResponse, err := gatewayClient.ListStorageSpaces(executantCtx, &apiProvider.ListStorageSpacesRequest{
+	listStorageSpacesResponse, err := gatewayClient.ListStorageSpaces(ctx, &apiProvider.ListStorageSpacesRequest{
 		Filters: []*apiProvider.ListStorageSpacesRequest_Filter{
 			{
 				Type: apiProvider.ListStorageSpacesRequest_Filter_TYPE_SPACE_TYPE,
@@ -43,52 +41,15 @@ func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceTyp
 	}
 
 	for _, storageSpace := range listStorageSpacesResponse.StorageSpaces {
-		var (
-			err                   error
-			impersonationID       *apiUser.UserId
-			storageSpaceReference = &apiProvider.Reference{
-				ResourceId: storageSpace.GetRoot(),
-			}
-		)
-
-		switch SpaceType(storageSpace.GetSpaceType()) {
-		case Personal:
-			impersonationID = storageSpace.GetOwner().GetId()
-		case Project:
-			var permissionsMap map[string]*apiProvider.ResourcePermissions
-			err := utils.ReadJSONFromOpaque(storageSpace.GetOpaque(), "grants", &permissionsMap)
-			if err != nil {
-				break
-			}
-
-			for id, permissions := range permissionsMap {
-				if !permissions.Delete {
-					continue
-				}
-
-				impersonationID = &apiUser.UserId{
-					OpaqueId: id,
-				}
-				break
-			}
-		default:
+		if typ := storageSpace.GetSpaceType(); typ != "personal" && typ != "project" {
+			// ignore spaces that are neither personal nor project
 			continue
 		}
-
-		if err != nil {
-			return err
-		}
-
-		if impersonationID == nil {
-			return fmt.Errorf("can't impersonate space user for space: %s", storageSpace.GetId().GetOpaqueId())
-		}
-
-		impersonatedCtx, _, err := utils.Impersonate(impersonationID, gatewayClient, machineAuthAPIKey)
-		if err != nil {
-			return err
+		storageSpaceReference := &apiProvider.Reference{
+			ResourceId: storageSpace.GetRoot(),
 		}
 
-		listRecycleResponse, err := gatewayClient.ListRecycle(impersonatedCtx, &apiProvider.ListRecycleRequest{Ref: storageSpaceReference})
+		listRecycleResponse, err := gatewayClient.ListRecycle(ctx, &apiProvider.ListRecycleRequest{Ref: storageSpaceReference})
 		if err != nil {
 			return err
 		}
@@ -99,7 +60,7 @@ func PurgeTrashBin(executantID *apiUser.UserId, deleteBefore time.Time, spaceTyp
 				continue
 			}
 
-			purgeRecycleResponse, err := gatewayClient.PurgeRecycle(impersonatedCtx, &apiProvider.PurgeRecycleRequest{
+			purgeRecycleResponse, err := gatewayClient.PurgeRecycle(ctx, &apiProvider.PurgeRecycleRequest{
 				Ref: storageSpaceReference,
 				Key: recycleItem.Key,
 			})
diff --git a/services/userlog/pkg/config/config.go b/services/userlog/pkg/config/config.go
index 782fb079172..0779c259a88 100644
--- a/services/userlog/pkg/config/config.go
+++ b/services/userlog/pkg/config/config.go
@@ -32,6 +32,8 @@ type Config struct {
 
 	GlobalNotificationsSecret string `yaml:"global_notifications_secret" env:"USERLOG_GLOBAL_NOTIFICATIONS_SECRET" desc:"The secret to secure the global notifications endpoint. Only system admins and users knowing that secret can call the global notifications POST/DELETE endpoints."`
 
+	ServiceAccount ServiceAccount `yaml:"service_account"`
+
 	Context context.Context `yaml:"-"`
 }
 
@@ -75,3 +77,9 @@ type HTTP struct {
 type TokenManager struct {
 	JWTSecret string `yaml:"jwt_secret" env:"OCIS_JWT_SECRET;USERLOG_JWT_SECRET" desc:"The secret to mint and validate jwt tokens."`
 }
+
+// ServiceAccount is the configuration for the used service account
+type ServiceAccount struct {
+	ServiceAccountID     string `yaml:"service_account_id" env:"OCIS_SERVICE_ACCOUNT_ID;USERLOG_SERVICE_ACCOUNT_ID" desc:"The ID of the service account the service should use. See the 'auth-service' service description for more details."`
+	ServiceAccountSecret string `yaml:"service_account_secret" env:"OCIS_SERVICE_ACCOUNT_SECRET;USERLOG_SERVICE_ACCOUNT_SECRET" desc:"The service account secret."`
+}
diff --git a/services/userlog/pkg/service/conversion.go b/services/userlog/pkg/service/conversion.go
index df472094a84..c589753fc85 100644
--- a/services/userlog/pkg/service/conversion.go
+++ b/services/userlog/pkg/service/conversion.go
@@ -52,31 +52,32 @@ type OC10Notification struct {
 
 // Converter is responsible for converting eventhistory events to OC10Notifications
 type Converter struct {
-	locale            string
-	gatewaySelector   pool.Selectable[gateway.GatewayAPIClient]
-	machineAuthAPIKey string
-	serviceName       string
-	translationPath   string
+	locale               string
+	gatewaySelector      pool.Selectable[gateway.GatewayAPIClient]
+	serviceAccountID     string
+	serviceAccountSecret string
+	serviceName          string
+	translationPath      string
 
 	// cached within one request not to query other service too much
-	spaces    map[string]*storageprovider.StorageSpace
-	users     map[string]*user.User
-	resources map[string]*storageprovider.ResourceInfo
-	contexts  map[string]context.Context
+	spaces                map[string]*storageprovider.StorageSpace
+	users                 map[string]*user.User
+	resources             map[string]*storageprovider.ResourceInfo
+	serviceAccountContext context.Context
 }
 
 // NewConverter returns a new Converter
-func NewConverter(loc string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string, name string, translationPath string) *Converter {
+func NewConverter(loc string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string, name string, translationPath string, serviceAccountID string, serviceAccountSecret string) *Converter {
 	return &Converter{
-		locale:            loc,
-		gatewaySelector:   gatewaySelector,
-		machineAuthAPIKey: machineAuthAPIKey,
-		serviceName:       name,
-		translationPath:   translationPath,
-		spaces:            make(map[string]*storageprovider.StorageSpace),
-		users:             make(map[string]*user.User),
-		resources:         make(map[string]*storageprovider.ResourceInfo),
-		contexts:          make(map[string]context.Context),
+		locale:               loc,
+		gatewaySelector:      gatewaySelector,
+		serviceAccountID:     serviceAccountID,
+		serviceAccountSecret: serviceAccountSecret,
+		serviceName:          name,
+		translationPath:      translationPath,
+		spaces:               make(map[string]*storageprovider.StorageSpace),
+		users:                make(map[string]*user.User),
+		resources:            make(map[string]*storageprovider.ResourceInfo),
 	}
 }
 
@@ -171,7 +172,7 @@ func (c *Converter) spaceMessage(eventid string, nt NotificationTemplate, execut
 		return OC10Notification{}, err
 	}
 
-	ctx, err := c.authenticate(usr)
+	ctx, err := c.authenticate()
 	if err != nil {
 		return OC10Notification{}, err
 	}
@@ -210,7 +211,7 @@ func (c *Converter) shareMessage(eventid string, nt NotificationTemplate, execut
 		return OC10Notification{}, err
 	}
 
-	ctx, err := c.authenticate(usr)
+	ctx, err := c.authenticate()
 	if err != nil {
 		return OC10Notification{}, err
 	}
@@ -327,13 +328,18 @@ func (c *Converter) deprovisionMessage(nt NotificationTemplate, deproDate string
 	}, nil
 }
 
-func (c *Converter) authenticate(usr *user.User) (context.Context, error) {
-	if ctx, ok := c.contexts[usr.GetId().GetOpaqueId()]; ok {
-		return ctx, nil
+func (c *Converter) authenticate() (context.Context, error) {
+	if c.serviceAccountContext != nil {
+		return c.serviceAccountContext, nil
 	}
-	ctx, err := authenticate(usr, c.gatewaySelector, c.machineAuthAPIKey)
+
+	gatewayClient, err := c.gatewaySelector.Next()
+	if err != nil {
+		return nil, err
+	}
+	ctx, err := utils.GetServiceUserContext(c.serviceAccountID, gatewayClient, c.serviceAccountSecret)
 	if err == nil {
-		c.contexts[usr.GetId().GetOpaqueId()] = ctx
+		c.serviceAccountContext = ctx
 	}
 	return ctx, err
 }
diff --git a/services/userlog/pkg/service/service.go b/services/userlog/pkg/service/service.go
index 7ebee777684..a9d18ce44c9 100644
--- a/services/userlog/pkg/service/service.go
+++ b/services/userlog/pkg/service/service.go
@@ -13,7 +13,6 @@ import (
 	user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
 	storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
-	revactx "github.com/cs3org/reva/v2/pkg/ctx"
 	"github.com/cs3org/reva/v2/pkg/events"
 	"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
 	"github.com/cs3org/reva/v2/pkg/utils"
@@ -29,7 +28,6 @@ import (
 	micrometadata "go-micro.dev/v4/metadata"
 	"go-micro.dev/v4/store"
 	"go.opentelemetry.io/otel/trace"
-	"google.golang.org/grpc/metadata"
 )
 
 // UserlogService is the service responsible for user activities
@@ -137,12 +135,12 @@ func (ul *UserlogService) processEvent(event events.Event) {
 			users = append(users, e.ExecutingUser.GetId().GetOpaqueId())
 		default:
 			return
-
 		}
+
 	// space related // TODO: how to find spaceadmins?
 	case events.SpaceDisabled:
 		executant = e.Executant
-		users, err = ul.findSpaceMembers(ul.impersonate(e.Executant), e.ID.GetOpaqueId(), viewer)
+		users, err = ul.findSpaceMembers(ul.mustAuthenticate(), e.ID.GetOpaqueId(), viewer)
 	case events.SpaceDeleted:
 		executant = e.Executant
 		for u := range e.FinalMembers {
@@ -150,22 +148,22 @@ func (ul *UserlogService) processEvent(event events.Event) {
 		}
 	case events.SpaceShared:
 		executant = e.Executant
-		users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
+		users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID)
 	case events.SpaceUnshared:
 		executant = e.Executant
-		users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
+		users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID)
 	case events.SpaceMembershipExpired:
-		users, err = ul.resolveID(ul.impersonate(e.SpaceOwner), e.GranteeUserID, e.GranteeGroupID)
+		users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID)
 
 	// share related
 	case events.ShareCreated:
 		executant = e.Executant
-		users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
+		users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID)
 	case events.ShareRemoved:
 		executant = e.Executant
-		users, err = ul.resolveID(ul.impersonate(e.Executant), e.GranteeUserID, e.GranteeGroupID)
+		users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID)
 	case events.ShareExpired:
-		users, err = ul.resolveID(ul.impersonate(e.ShareOwner), e.GranteeUserID, e.GranteeGroupID)
+		users, err = ul.resolveID(ul.mustAuthenticate(), e.GranteeUserID, e.GranteeGroupID)
 	}
 
 	if err != nil {
@@ -520,26 +518,6 @@ func (ul *UserlogService) resolveGroup(ctx context.Context, groupID string) ([]s
 	return userIDs, nil
 }
 
-func (ul *UserlogService) impersonate(uid *user.UserId) context.Context {
-	if uid == nil {
-		ul.log.Error().Msg("cannot impersonate nil user")
-		return nil
-	}
-
-	u, err := getUser(context.Background(), uid, ul.gatewaySelector)
-	if err != nil {
-		ul.log.Error().Err(err).Msg("cannot get user")
-		return nil
-	}
-
-	ctx, err := authenticate(u, ul.gatewaySelector, ul.cfg.MachineAuthAPIKey)
-	if err != nil {
-		ul.log.Error().Err(err).Str("userid", u.GetId().GetOpaqueId()).Msg("failed to impersonate user")
-		return nil
-	}
-	return ctx
-}
-
 func (ul *UserlogService) getUserLocale(userid string) string {
 	resp, err := ul.valueClient.GetValueByUniqueIdentifiers(
 		micrometadata.Set(context.Background(), middleware.AccountID, userid),
@@ -560,29 +538,25 @@ func (ul *UserlogService) getUserLocale(userid string) string {
 }
 
 func (ul *UserlogService) getConverter(locale string) *Converter {
-	return NewConverter(locale, ul.gatewaySelector, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath)
+	return NewConverter(locale, ul.gatewaySelector, ul.cfg.MachineAuthAPIKey, ul.cfg.Service.Name, ul.cfg.TranslationPath, ul.cfg.ServiceAccount.ServiceAccountID, ul.cfg.ServiceAccount.ServiceAccountSecret)
 }
 
-func authenticate(usr *user.User, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], machineAuthAPIKey string) (context.Context, error) {
-	gatewayClient, err := gatewaySelector.Next()
+func (ul *UserlogService) mustAuthenticate() context.Context {
+	ctx, err := authenticate(ul.cfg.ServiceAccount.ServiceAccountID, ul.gatewaySelector, ul.cfg.ServiceAccount.ServiceAccountSecret)
 	if err != nil {
-		return nil, err
+		ul.log.Error().Err(err).Str("accountid", ul.cfg.ServiceAccount.ServiceAccountID).Msg("failed to impersonate service account")
+		return nil
 	}
+	return ctx
+}
 
-	ctx := revactx.ContextSetUser(context.Background(), usr)
-	authRes, err := gatewayClient.Authenticate(ctx, &gateway.AuthenticateRequest{
-		Type:         "machine",
-		ClientId:     "userid:" + usr.GetId().GetOpaqueId(),
-		ClientSecret: machineAuthAPIKey,
-	})
+func authenticate(serviceAccountID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient], serviceAccountSecret string) (context.Context, error) {
+	gatewayClient, err := gatewaySelector.Next()
 	if err != nil {
 		return nil, err
 	}
-	if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
-		return nil, fmt.Errorf("error impersonating user: %s", authRes.Status.Message)
-	}
 
-	return metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, authRes.Token), nil
+	return utils.GetServiceUserContext(serviceAccountID, gatewayClient, serviceAccountSecret)
 }
 
 func getSpace(ctx context.Context, spaceID string, gatewaySelector pool.Selectable[gateway.GatewayAPIClient]) (*storageprovider.StorageSpace, error) {
@@ -665,6 +639,7 @@ func getResource(ctx context.Context, resourceid *storageprovider.ResourceId, ga
 
 func listStorageSpaceRequest(spaceID string) *storageprovider.ListStorageSpacesRequest {
 	return &storageprovider.ListStorageSpacesRequest{
+		Opaque: utils.AppendPlainToOpaque(nil, "unrestricted", "true"),
 		Filters: []*storageprovider.ListStorageSpacesRequest_Filter{
 			{
 				Type: storageprovider.ListStorageSpacesRequest_Filter_TYPE_ID,
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/loader/loader.go b/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/loader/loader.go
index 694cd98c29f..9fcba055412 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/loader/loader.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/loader/loader.go
@@ -30,5 +30,6 @@ import (
 	_ "github.com/cs3org/reva/v2/pkg/auth/manager/oidc"
 	_ "github.com/cs3org/reva/v2/pkg/auth/manager/owncloudsql"
 	_ "github.com/cs3org/reva/v2/pkg/auth/manager/publicshares"
+	_ "github.com/cs3org/reva/v2/pkg/auth/manager/serviceaccounts"
 	// Add your own here
 )
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/registry/registry.go b/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/registry/registry.go
index aea682f79d4..8d92a13e193 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/registry/registry.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/registry/registry.go
@@ -18,7 +18,9 @@
 
 package registry
 
-import "github.com/cs3org/reva/v2/pkg/auth"
+import (
+	"github.com/cs3org/reva/v2/pkg/auth"
+)
 
 // NewFunc is the function that auth implementations
 // should register to at init time.
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go b/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go
new file mode 100644
index 00000000000..dad1dcf72a8
--- /dev/null
+++ b/vendor/github.com/cs3org/reva/v2/pkg/auth/manager/serviceaccounts/serviceaccounts.go
@@ -0,0 +1,90 @@
+package serviceaccounts
+
+import (
+	"context"
+
+	authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
+	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
+
+	"github.com/cs3org/reva/v2/pkg/auth"
+	"github.com/cs3org/reva/v2/pkg/auth/manager/registry"
+	"github.com/cs3org/reva/v2/pkg/auth/scope"
+	"github.com/mitchellh/mapstructure"
+	"github.com/pkg/errors"
+)
+
+type conf struct {
+	ServiceUsers []serviceuser `mapstructure:"service_accounts"`
+}
+
+type serviceuser struct {
+	ID     string `mapstructure:"id"`
+	Secret string `mapstructure:"secret"`
+}
+
+type manager struct {
+	authenticate func(userID, secret string) error
+}
+
+func init() {
+	registry.Register("serviceaccounts", New)
+}
+
+// Configure parses the map conf
+func (m *manager) Configure(config map[string]interface{}) error {
+	c := &conf{}
+	if err := mapstructure.Decode(config, c); err != nil {
+		return errors.Wrap(err, "error decoding conf")
+	}
+	// only inmem authenticator for now
+	a := &inmemAuthenticator{make(map[string]string)}
+	for _, s := range c.ServiceUsers {
+		a.m[s.ID] = s.Secret
+	}
+	m.authenticate = a.Authenticate
+	return nil
+}
+
+// New creates a new manager for the 'service' authentication
+func New(conf map[string]interface{}) (auth.Manager, error) {
+	m := &manager{}
+	err := m.Configure(conf)
+	if err != nil {
+		return nil, err
+	}
+
+	return m, nil
+}
+
+// Authenticate authenticates the service account
+func (m *manager) Authenticate(ctx context.Context, userID string, secret string) (*userpb.User, map[string]*authpb.Scope, error) {
+	if err := m.authenticate(userID, secret); err != nil {
+		return nil, nil, err
+	}
+	scope, err := scope.AddOwnerScope(nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	return &userpb.User{
+		// TODO: more details for service users?
+		Id: &userpb.UserId{
+			OpaqueId: userID,
+			Type:     userpb.UserType_USER_TYPE_SERVICE,
+			Idp:      "none",
+		},
+	}, scope, nil
+}
+
+type inmemAuthenticator struct {
+	m map[string]string
+}
+
+func (a *inmemAuthenticator) Authenticate(userID string, secret string) error {
+	if secret == "" || a.m[userID] == "" {
+		return errors.New("unknown user")
+	}
+	if a.m[userID] == secret {
+		return nil
+	}
+	return errors.New("secrets do not match")
+}
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/registry/spaces/spaces.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/registry/spaces/spaces.go
index 17f3cea740e..aa8385f6626 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/storage/registry/spaces/spaces.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/registry/spaces/spaces.go
@@ -481,7 +481,7 @@ func (r *registry) findProvidersForResource(ctx context.Context, id string, find
 				},
 			})
 		}
-		spaces, err := r.findStorageSpaceOnProvider(ctx, address, filters, false)
+		spaces, err := r.findStorageSpaceOnProvider(ctx, address, filters, unrestricted)
 		if err != nil {
 			appctx.GetLogger(ctx).Debug().Err(err).Interface("provider", provider).Msg("findStorageSpaceOnProvider by id failed, continuing")
 			continue
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/permissions.go b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/permissions.go
index 1e5017241b5..84814a1641f 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/permissions.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/storage/utils/decomposedfs/node/permissions.go
@@ -22,6 +22,7 @@ import (
 	"context"
 	"strings"
 
+	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
 	provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
 	"github.com/cs3org/reva/v2/pkg/appctx"
 	ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
@@ -84,6 +85,21 @@ func OwnerPermissions() provider.ResourcePermissions {
 	}
 }
 
+// ServiceAccountPermissions defines the permissions for nodes when requested by a service account
+func ServiceAccountPermissions() provider.ResourcePermissions {
+	// TODO: Different permissions for different service accounts
+	return provider.ResourcePermissions{
+		Stat:                 true,
+		ListContainer:        true,
+		GetPath:              true, // for search index
+		InitiateFileUpload:   true, // for personal data export
+		InitiateFileDownload: true, // for full-text-search
+		RemoveGrant:          true, // for share expiry
+		ListRecycle:          true, // for purge-trash-bin command
+		PurgeRecycle:         true, // for purge-trash-bin command
+	}
+}
+
 // Permissions implements permission checks
 type Permissions struct {
 	lu PathLookup
@@ -113,6 +129,10 @@ func (p *Permissions) assemblePermissions(ctx context.Context, n *Node, failOnTr
 		return NoPermissions(), nil
 	}
 
+	if u.GetId().GetType() == userpb.UserType_USER_TYPE_SERVICE {
+		return ServiceAccountPermissions(), nil
+	}
+
 	// are we reading a revision?
 	if strings.Contains(n.ID, RevisionIDDelimiter) {
 		// verify revision key format
diff --git a/vendor/github.com/cs3org/reva/v2/pkg/utils/grpc.go b/vendor/github.com/cs3org/reva/v2/pkg/utils/grpc.go
index 3945d32e8cb..90547443fa2 100644
--- a/vendor/github.com/cs3org/reva/v2/pkg/utils/grpc.go
+++ b/vendor/github.com/cs3org/reva/v2/pkg/utils/grpc.go
@@ -11,19 +11,8 @@ import (
 	"google.golang.org/grpc/metadata"
 )
 
-// Impersonate returns an authenticated reva context and the user it represents
-func Impersonate(userID *user.UserId, gwc gateway.GatewayAPIClient, machineAuthAPIKey string) (context.Context, *user.User, error) {
-	usr, err := GetUser(userID, gwc, machineAuthAPIKey)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	ctx, err := ImpersonateUser(usr, gwc, machineAuthAPIKey)
-	return ctx, usr, err
-}
-
 // GetUser gets the specified user
-func GetUser(userID *user.UserId, gwc gateway.GatewayAPIClient, machineAuthAPIKey string) (*user.User, error) {
+func GetUser(userID *user.UserId, gwc gateway.GatewayAPIClient) (*user.User, error) {
 	getUserResponse, err := gwc.GetUser(context.Background(), &user.GetUserRequest{UserId: userID})
 	if err != nil {
 		return nil, err
@@ -35,19 +24,19 @@ func GetUser(userID *user.UserId, gwc gateway.GatewayAPIClient, machineAuthAPIKe
 	return getUserResponse.GetUser(), nil
 }
 
-// ImpersonateUser impersonates the given user
-func ImpersonateUser(usr *user.User, gwc gateway.GatewayAPIClient, machineAuthAPIKey string) (context.Context, error) {
-	ctx := revactx.ContextSetUser(context.Background(), usr)
+// GetServiceUserContext returns an authenticated context of the given service user
+func GetServiceUserContext(serviceUserID string, gwc gateway.GatewayAPIClient, serviceUserSecret string) (context.Context, error) {
+	ctx := context.Background()
 	authRes, err := gwc.Authenticate(ctx, &gateway.AuthenticateRequest{
-		Type:         "machine",
-		ClientId:     "userid:" + usr.GetId().GetOpaqueId(),
-		ClientSecret: machineAuthAPIKey,
+		Type:         "serviceaccounts",
+		ClientId:     serviceUserID,
+		ClientSecret: serviceUserSecret,
 	})
 	if err != nil {
 		return nil, err
 	}
 	if authRes.GetStatus().GetCode() != rpc.Code_CODE_OK {
-		return nil, fmt.Errorf("error impersonating user: %s", authRes.Status.Message)
+		return nil, fmt.Errorf("error authenticating service user: %s", authRes.Status.Message)
 	}
 
 	return metadata.AppendToOutgoingContext(ctx, revactx.TokenHeader, authRes.Token), nil
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 6d00fbc3bf4..d332df59fed 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -354,7 +354,7 @@ github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1
 github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1
 github.com/cs3org/go-cs3apis/cs3/tx/v1beta1
 github.com/cs3org/go-cs3apis/cs3/types/v1beta1
-# github.com/cs3org/reva/v2 v2.16.1-0.20230828111521-594d4e103741
+# github.com/cs3org/reva/v2 v2.16.1-0.20230829124655-8ba013d7a129
 ## explicit; go 1.20
 github.com/cs3org/reva/v2/cmd/revad/internal/grace
 github.com/cs3org/reva/v2/cmd/revad/runtime
@@ -473,6 +473,7 @@ github.com/cs3org/reva/v2/pkg/auth/manager/owncloudsql
 github.com/cs3org/reva/v2/pkg/auth/manager/owncloudsql/accounts
 github.com/cs3org/reva/v2/pkg/auth/manager/publicshares
 github.com/cs3org/reva/v2/pkg/auth/manager/registry
+github.com/cs3org/reva/v2/pkg/auth/manager/serviceaccounts
 github.com/cs3org/reva/v2/pkg/auth/registry/loader
 github.com/cs3org/reva/v2/pkg/auth/registry/registry
 github.com/cs3org/reva/v2/pkg/auth/registry/static


Test patch:
diff --git a/services/notifications/pkg/service/service_test.go b/services/notifications/pkg/service/service_test.go
index 6b95c74148c..056866391f8 100644
--- a/services/notifications/pkg/service/service_test.go
+++ b/services/notifications/pkg/service/service_test.go
@@ -77,7 +77,7 @@ var _ = Describe("Notifications", func() {
 			cfg := defaults.FullDefaultConfig()
 			cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
 			ch := make(chan events.Event)
-			evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "")
+			evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "", "")
 			go evts.Run()
 
 			ch <- ev
@@ -275,7 +275,7 @@ var _ = Describe("Notifications X-Site Scripting", func() {
 			cfg := defaults.FullDefaultConfig()
 			cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
 			ch := make(chan events.Event)
-			evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "")
+			evts := service.NewEventsNotifier(ch, tc, log.NewLogger(), gatewaySelector, vs, "", "", "", "")
 			go evts.Run()
 
 			ch <- ev
diff --git a/services/storage-users/pkg/task/trash_bin_test.go b/services/storage-users/pkg/task/trash_bin_test.go
index 1066565c958..9800f954f67 100644
--- a/services/storage-users/pkg/task/trash_bin_test.go
+++ b/services/storage-users/pkg/task/trash_bin_test.go
@@ -115,7 +115,7 @@ var _ = Describe("trash", func() {
 			gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil)
 			gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(nil, genericError)
 
-			err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "")
+			err := task.PurgeTrashBin("service-user-id", now, task.Project, gatewaySelector, "")
 			Expect(err).To(HaveOccurred())
 		})
 		It("throws an error if space listing fails", func() {
@@ -123,45 +123,9 @@ var _ = Describe("trash", func() {
 			gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil)
 			gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(nil, genericError)
 
-			err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "")
+			err := task.PurgeTrashBin("service-user-id", now, task.Project, gatewaySelector, "")
 			Expect(err).To(HaveOccurred())
 		})
-		It("throws an error if a personal space user can't be impersonated", func() {
-			listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{personalSpace}
-			gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil)
-			gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil)
-			gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil)
-
-			err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "")
-			Expect(err).To(MatchError(errors.New("can't impersonate space user for space: personal")))
-		})
-		It("throws an error if a project space user can't be impersonated", func() {
-			listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{projectSpace}
-			gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil)
-			gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil)
-			gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil)
-
-			err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "")
-			Expect(err).To(MatchError(errors.New("can't impersonate space user for space: project")))
-		})
-		It("throws an error if a project space has no user with delete permissions", func() {
-			listStorageSpacesResponse.StorageSpaces = []*apiProvider.StorageSpace{projectSpace}
-			projectSpace.Opaque.Map = map[string]*apiTypes.OpaqueEntry{
-				"grants": {
-					Value: MustMarshal(map[string]*apiProvider.ResourcePermissions{
-						"admin": {
-							Delete: false,
-						},
-					}),
-				},
-			}
-			gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(getUserResponse, nil)
-			gatewayClient.On("Authenticate", mock.Anything, mock.Anything).Return(authenticateResponse, nil)
-			gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Return(listStorageSpacesResponse, nil)
-
-			err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "")
-			Expect(err).To(MatchError(errors.New("can't impersonate space user for space: project")))
-		})
 		It("only deletes items older than the specified period", func() {
 			var (
 				recycleItems = map[string][]*apiProvider.RecycleItem{
@@ -231,7 +195,7 @@ var _ = Describe("trash", func() {
 				}, nil,
 			)
 
-			err := task.PurgeTrashBin(user.Id, now, task.Project, gatewaySelector, "")
+			err := task.PurgeTrashBin("service-user-id", now, task.Project, gatewaySelector, "")
 			Expect(err).To(BeNil())
 			Expect(recycleItems["personal"]).To(HaveLen(2))
 			Expect(recycleItems["project"]).To(HaveLen(2))
diff --git a/tests/acceptance/features/apiSpaces/listSpaces.feature b/tests/acceptance/features/apiSpaces/listSpaces.feature
index 87d0d459587..1390176d5c9 100644
--- a/tests/acceptance/features/apiSpaces/listSpaces.feature
+++ b/tests/acceptance/features/apiSpaces/listSpaces.feature
@@ -425,7 +425,5 @@ Feature: List and create spaces
     And the json responded should not contain a space with name "Project Venus"
     Examples:
       | role        |
-      | Admin       |
-      | Space Admin |
       | User        |
       | User Light  |