Implement token exchange from OCM#57234
Conversation
|
Hello there, We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process. Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6 Thank you for contributing to Nextcloud and we hope to hear from you soon! (If you believe you should not receive this message, you can add yourself to the blocklist.) |
869318e to
fa85a7c
Compare
|
Disclaimer: Some of the code in this PR (mainly regarding the tests) was generated by Claude Opus 4.5. I have provided it with very specific prompts, and thoroughly reviewed the output. |
af0f4f2 to
736b3ff
Compare
6e14936 to
84713bc
Compare
4bd40db to
405de22
Compare
Combines the rebased kano-dual-stack-rfc-9421-http-sig PR (RFC 9421 HTTP signature support, OCM Ed25519 keys, JWKS endpoint, occ key management) with enriquepablo's OCM token-exchange and JWT access tokens. The exchange-token flow lives on top of the new RFC 9421 signing path (see the follow-up federatedfilesharing fix). # Conflicts: # lib/private/OCM/OCMDiscoveryService.php
28e946f to
23b9d24
Compare
susnux
left a comment
There was a problem hiding this comment.
IIRC OCM is federatedfilessharing and federation so better implement it there and use proper manager / abstraction rather than span the implementation across four different apps.
Not sure how to review all of the core things, this all affects security related parts of the code. So it would be much better to split the PR into multiple minimal PRs that can be properly reviewed for security rather than one large PR 👀
|
|
||
| $this->manager->emit('\OC\User', 'preLogin', [$dbToken->getLoginName(), $password]); | ||
|
|
||
| // See line 173 in this module, needed for completeLogin |
There was a problem hiding this comment.
This is not really helpful the code will change in the future, better directly add a comment why this is needed rather than link to some line that will change anyways in the future.
There was a problem hiding this comment.
This sounds not like this should be implemented in files_sharing but rather federatedfilessharing app.
There was a problem hiding this comment.
This looks like it should belong to the federation app itself not the dav app.
|
@susnux perhaps if you can find the time, we can schedule a little while to sit together and go through the code, and decide what is the best way to move this forward. |
|
Hey @enriquepablo I would like to review this PR, but in the current shape it is a bit hard. Would it be possible for you to squash a few commits into more self-contained/atomic commits, such that I can review commit-by-commit? This would make it way easier than reviewing the whole PR at once. |
|
Also a guide review in a video call would be awesome, like you already suggested! |
|
@provokateurin I will work on the commits as suggested, hopefully tomorrow, I'll ping you back here once I'm done and then we can schedule a video call. Thank you and @susnux ! |
I will be happy to join a call as well! |
@provokateurin Maybe you would like to review this one too: #60136 They go a little bit together... Carl has already approved, but we need one more 🥺 |
|
@provokateurin @susnux @mickenordin I have squashed the commits down to 12. The 2nd commit is a bit large, I was descending into an unmanageable conflict hell trying to regroup the commits with more granularity. So I think we are ready for the call - I would discuss there also the code relocations suggested by susnux. |
Allow Bearer token auth for WebDAV requests. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
End-to-end OCM bearer-auth flow: - a token endpoint (apps/dav) that exchanges a long-lived refresh token for a short-lived JWT access token, with form-urlencoded payloads, exposed via OCM discovery - a Sabre Bearer-auth backend that validates incoming WebDAV requests carrying an OCM access token - `IUserSession::doTryTokenLogin($token)` to authenticate by token without an `IRequest` - federated share provider issues permanent refresh tokens to remotes on share creation; signs POSTs to the remote token endpoint; honours the `must-exchange-token` capability - files_sharing client exchanges refresh tokens for access tokens when mounting a remote share configured for bearer auth, and uses the access token as the WebDAV Bearer header - cloud_federation_api adapted to the multi-protocol share creation format from the latest OCM draft - federated WebDAV client adapted to Guzzle 7 Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
Introduce `OcmTokenMap` mapping an `oc_authtoken` id to the refresh token it was issued for. Migration adds the `oc_ocm_token_map` table. Token-exchange responses are validated before being persisted. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
By using a JWT that the receiver can verify against a public key, we make it easy for third party services to validate the token independently. This is relevant for example in the context of webapp shares that may defer to a separate service to deliver the actual webapp. NOTE: This requires nextcloud/3rdparty#2413 to be merged before this. Signed-off-by: Micke Nordin <kano@sunet.se> Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
Add a refresh-token column to `federated_reshares` so refresh tokens issued to remotes survive across share-creation requests. The migration is backwards-compatible with shares created before the upgrade. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
Add `access_token` and `access_token_expires` columns to `share_external`. The expires timestamp deduplicates concurrent refresh requests across processes. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
When a WebDAV request fails with 401 and the stored access token is expired, attempt one transparent refresh against the OCM token endpoint and retry. Limited to a single retry per request to avoid loops when the refresh itself fails. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
CleanupExpiredOcmTokensJob periodically deletes expired `oc_ocm_token_map` rows and their backing `oc_authtoken` entries. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
When mounting a federated share created on an instance that predates the bearer-auth upgrade, fall back to password-based authentication instead of attempting token exchange. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
Bundle of tooling-level fixes accumulated during the PR: psalm fixes across affected files, code-style fixes, openapi spec regeneration, sqlite integration test fixes, missing `Override` attributes, and composer autoloader regeneration for new migrations and entities. Signed-off-by: Enrique Pérez Arnaud <enrique@cazalla.net>
See #57152 #57166
Summary
This PR implements the token exchange flow from OCM, allowing Nextcloud to use bearer auth with short lived tokens. This opens up interoperability with OpenCloud/ownCloud OCIS and the possibility to implement webapp shares in the future, which requires this.
Discussion
This is a preliminary PR for discussion. If this goes through there are some things missing with which we'll be grateful to get some guidance:
There is also the question that for access tokens, we are keeping a reference to the corresponding refresh token in the uid field of the access token db record.
Prerequisite
This needs nextcloud/3rdparty#2413 before merging