Our Personal Data Server from scratch! tranquil.farm

fix(auth): enforce scope checks on app passwords #677

Merged oyster.cafe
targeting main from jola.dev/tranquil-pds:johanna/enforce-scope-checks-on-app-passwords
Merged opened by and assigned to oyster.cafe
targeting main from jola.dev/tranquil-pds:johanna/enforce-scope-checks-on-app-passwords

I originally just noticed that read only app passwords can take write actions, like writing a record or even creating an app password, and then I realized it wasn't just app passwords, the same was true for OAuth sessions.

Tranquil skipped checking scopes on app passwords, only OAuth tokens were checked (scope checks were skipped with is_oauth()). With this PR, app passwords are also checked, and only tokens with scope=access, aka account passwords, are skipped. That's the main change here.

This also introduces a new requirement on 30 endpoints to require account:*?action=manage, which OAuth tokens for Tranquil's dashboard already has. App passwords normally do not have this and will not be able to take management action. This is because they didn't have sufficient scope checks, eg creating app password had no scope checks at all.

Delegated OAuth sessions now have the controller's did for checks, which is also required by finalize_repo_write for audit logging and delete_app_passwords_by_controller wouldn't work either. When creating app passwords with Full Access the server normally fills in scopes based on the session, but for delegated accounts it would just do atproto. Now it takes the controllers scopes. Additionally, a service auth request with no lxm is rejected unless the token has transition:generic. Previously it was just treated as a global allow on all XRPC endpoints.

Also also this reads atproto-proxy header to get aud on put_preferences so we can validate the scope here too, otherwise anyone can write preferences.

With this PR it's likely that people will bump into some edges where they previously eg logged in with an app password and were able to take actions that didn't match the auth method, they just didn't realize. The changes will take effect immediately when an instance is updated, no relogin required.

These endpoints now require AccountControl

App passwords
- com.atproto.server.listAppPasswords
- com.atproto.server.createAppPassword
- com.atproto.server.revokeAppPassword

Passwords
- _account.changePassword
- _account.setPassword
- _account.removePassword

Passkeys
- com.atproto.server.startPasskeyRegistration
- com.atproto.server.finishPasskeyRegistration
- com.atproto.server.updatePasskey
- com.atproto.server.deletePasskey

TOTP
- com.atproto.server.createTotpSecret
- com.atproto.server.enableTotp
- com.atproto.server.disableTotp
- com.atproto.server.regenerateBackupCodes

Sessions and devices
- _account.listSessions
- _account.revokeSession
- _account.revokeAllSessions
- _account.updateLegacyLoginPreference
- _account.revokeTrustedDevice
- _account.updateTrustedDevice
- POST /oauth/establish-session

Delegation
- _delegation.addController
- _delegation.removeController
- _delegation.updateControllerScopes
- _delegation.createDelegatedAccount
- POST /oauth/delegation/auth-token

SSO
- POST /oauth/sso/initiate, only when linking an account
- POST /oauth/sso/unlink

Account
- _account.updateDidDocument
- _account.updateNotificationPrefs
- com.atproto.server.requestAccountDelete

And these are under Admin

Admin endpoints (22), checked through the Admin policy
- com.atproto.admin: sendEmail, deleteAccount, getAccountInfo, getAccountInfos, searchAccounts, updateAccountEmail, updateAccountHandle, updateAccountPassword, getSubjectStatus,
  updateSubjectStatus, getInviteCodes, disableInviteCodes, disableAccountInvites and enableAccountInvites
- com.atproto.server: createInviteCode and createInviteCodes
- _admin: getServerStats, setAdminStatus, getSignalStatus, linkSignalDevice, unlinkSignalDevice and updateServerConfig
Commits 0