Skip to content

feat(auth): store scopes on app passwords - #65268

Open
pringelmann wants to merge 6 commits into
masterfrom
feat/noid/app-password-scope-storage
Open

pringelmann wants to merge 6 commits into
masterfrom
feat/noid/app-password-scope-storage

Conversation

@pringelmann

@pringelmann pringelmann commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

First step towards limited app passwords: a token can now carry a list of scopes. This PR only stores them. Apart from files access nothing enforces them yet, and there's no UI.

How scopes work

  • Stored as a scopes key in the existing scope JSON column, so no migration
  • OAuth-style names, defined by the app that owns the data: files:read, files:share in files, calendar:read, contacts:write etc. in dav
  • Each scope stands on its own. Files are the exception: every files scope needs files:read, and files:read needs files:write until read-only files exist
  • No scopes key means full access. That's every existing token, unchanged
  • Empty list or anything malformed means no access
  • Existing filesystem flag stays and is derived from files:read, so code reading it keeps working
  • Per-app scopes come later (app:<appid>)

Change description

  • A new internal helper reads and validates the scope list
  • The files access check uses it for tokens that have scopes
  • Saving a token no longer wipes its scopes. Before, every save replaced the stored permissions with the single filesystem flag sent by the browser. Now the server merges the change, ignores that flag on scoped tokens and only checks the shape of the list. Setting scopes to null resets the token to full access
  • The occ token list shows the scopes

Next step

Enforcement on routes and DAV, then the access picker in Settings.

Checklist

AI (if applicable)

  • The content of this PR was partly or fully generated using AI (tests, reviewed)

@pringelmann pringelmann added this to the Nextcloud 36 milestone Oct 7, 2026
@pringelmann pringelmann self-assigned this Oct 7, 2026
@pringelmann
pringelmann requested a review from a team as a code owner October 7, 2026 09:47
@pringelmann
pringelmann requested review from Altahrim, CarlSchwan and leftybournes and removed request for a team October 7, 2026 09:47
@pringelmann
pringelmann requested a review from come-nc October 7, 2026 09:47
@pringelmann
pringelmann requested a review from artonge October 7, 2026 09:52
Comment thread lib/private/Authentication/Token/Scopes.php Outdated
Comment thread apps/settings/lib/Controller/AuthSettingsController.php Outdated
Comment thread apps/settings/lib/Controller/AuthSettingsController.php Outdated
Comment thread lib/private/Authentication/Token/TokenScopes.php Outdated
Comment thread lib/private/Authentication/Token/Scopes.php Outdated
@pringelmann
pringelmann force-pushed the feat/noid/app-password-scope-storage branch from f50f496 to dd472f6 Compare October 7, 2026 10:53
@pringelmann
pringelmann requested a review from come-nc October 7, 2026 11:01
Comment thread apps/settings/lib/Controller/AuthSettingsController.php
@pringelmann
pringelmann force-pushed the feat/noid/app-password-scope-storage branch from dd472f6 to e3ec082 Compare October 7, 2026 12:27
@pringelmann
pringelmann requested a review from come-nc October 8, 2026 07:44
@pringelmann
pringelmann added this pull request to stack #65341 October 9, 2026 11:15
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
@pringelmann
pringelmann force-pushed the feat/noid/app-password-scope-storage branch from e3ec082 to 3f0d7ab Compare October 9, 2026 11:25
Comment thread lib/private/Authentication/Token/TokenScopes.php Outdated
Comment thread lib/private/Authentication/Token/TokenScopes.php Outdated
Comment thread lib/private/Authentication/Token/TokenScopes.php Outdated
Comment thread lib/private/Authentication/Token/TokenScopes.php Outdated
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
@pringelmann
pringelmann force-pushed the feat/noid/app-password-scope-storage branch from cc3d87e to e0353ab Compare October 16, 2026 07:38

@nickvergessen nickvergessen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not seeing it here in the diff, so I'm asking. Some things from filesystem e.g. appdata_* need to be always/frequent loadable, e.g. to load identity proof and check for updates, etc.

The other thing I'm wondering is how we could potentially improve the situation for e.g. mobile apps that try to render notifications/activity. It seems they would need at least *:read for all apps/scopes that potentially create activity/notifications as otherwise their resource should not be loadable and they would not be able to render them?

But maybe that is not the idea for now and you simply would use an unscoped token for such cases.

Otherwise while I didn't check the tests files, the general approach looks like we are going into the right direction, so I'm in

@pringelmann

Copy link
Copy Markdown
Contributor Author

Not seeing it here in the diff, so I'm asking. Some things from filesystem e.g. appdata_* need to be always/frequent loadable, e.g. to load identity proof and check for updates, etc.

The other thing I'm wondering is how we could potentially improve the situation for e.g. mobile apps that try to render notifications/activity. It seems they would need at least *:read for all apps/scopes that potentially create activity/notifications as otherwise their resource should not be loadable and they would not be able to render them?

But maybe that is not the idea for now and you simply would use an unscoped token for such cases.

Otherwise while I didn't check the tests files, the general approach looks like we are going into the right direction, so I'm in

Thanks!

  • The scope only gates the user's mounts, appdata is on the root storage and stays loadable. Same path as today's tokens with filesystem access turned off.
  • Mobile/desktop: yes, those keep using unscoped tokens from login flow. Scopes are opt-in for scripts and integrations. A notifications:read option is planned for the scope picker UI, but rendering the resources would still need their app's scope, so full native clients stay on All access. Letting clients request scopes during login flow could be a later follow-up but is not planned for 36.

@pringelmann
pringelmann requested a review from artonge October 16, 2026 08:25
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
Signed-off-by: Peter Ringelmann <peter.ringelmann@nextcloud.com>
@pringelmann
pringelmann requested a review from kesselb October 16, 2026 15:50

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scoped app passwords: store scopes

4 participants