Add an explicit spec for the storage-first handoff (remoteStorage spec
section 11): launching an app passes only `#remotestorage=<address>`,
never an access token. The app then runs the application-first
(now PKCE-capable) flow itself.
Part of #254
- Add user_agent to remote_storage_authorizations and rs_authorization_codes
- Capture the browser's user agent at consent, carry it on the authorization
code, and store it on the authorization when the code is exchanged
- Store it directly for the implicit flow too
With the reuse shortcut gone, each authorization is already a distinct
per-device token; this adds the metadata needed to label and display them.
Part of #254
- Enqueue the expiry job and the "app connected" notification on
after_commit instead of after_create, so they only fire once the (now
transactional) code exchange commits
- Enforce the PKCE code_challenge format (43-128 unreserved chars, S256)
with model validations, not only at the controller
- Drop the now-unused RsAuthorizationCode#expired?
- Cover the client_id-vs-stored-client_id check
Part of #254
- Make authorization-code redemption atomic: claim the code with a guarded
delete inside the transaction that creates the authorization, so concurrent
exchanges can't both issue a token (RFC 6749 4.1.3)
- Compare the token request's client_id host to the code's stored client_id
(defense-in-depth alongside the exact redirect_uri match)
- Reject unknown response_type values with unsupported_response_type
- Validate the PKCE code_challenge (43-128 unreserved chars, S256) so a client
can't persist an over-long or malformed challenge
- Return invalid_request for missing token-endpoint parameters, and set
Cache-Control: no-store on error responses too
- Use the right separator when the redirect_uri already has a query string
- Look codes up through the active scope; drop the unused consume!; serialize
permissions as JSON
- Purge abandoned authorization codes via a recurring Solid Queue task
Part of #254
The code is redeemed by the client immediately in the OAuth callback, so
a 60-second window is plenty (and matches common practice; RFC 6749 caps
authorization codes at 10 minutes).
Part of #254
Implements the optional OAuth 2.0 Authorization Code grant with PKCE
(RFC 7636) for remoteStorage, as described in the remoteStorage spec
(section 10.1).
- Advertise the authorization and token endpoints plus the supported
code_challenge method (S256) in WebFinger, and bump the advertised
spec version to draft-dejong-remotestorage-27
- Add RsAuthorizationCode: a short-lived (5 min), single-use authorization
code stored only as a SHA256 digest
- Add RsOauthManager services for issuing and exchanging codes, including
S256 verification
- Add a cross-origin POST /rs/oauth/token endpoint that exchanges a code
(and code_verifier) for an access token, with CORS and no CSRF token
- Consent dialog now carries response_type / code_challenge through the
form and redirects with the code in the query string for the code flow
- Remove the existing-token reuse shortcut so every authorization results
in a distinct authorization (and token)
Part of #254
The specs deleted a global authorizations:* pattern from the shared
test Redis DB, so concurrent test processes could wipe each other's
keys mid-example, causing intermittent empty/mismatched set failures.
Drop all global pattern deletes and the now-unused
redis_rs_delete_keys helper. Each spec now reads or asserts only the
exact key derived from its own random token, so runs can no longer
interfere regardless of ordering or concurrency.
Also fixes a vacuous assertion in the #destroy spec, which checked
authorizations:<user.address>:* (never matching the keys, which use
user.cn), to assert the exact key is gone.
"stores a token in redis" asserted an exact count of Redis keys for the
user, but database cleanup only covers SQL and Redis keys accumulate
across examples and spec files. It also relied on a previously-run
example having created the authorization, so it failed depending on test
order and leftover state.
Clear the authorizations:* namespace before each example in the #create
group, create the authorization under test, and assert on its specific
token key instead of a global count.
The admin "Default services" setting writes the LDAP serviceEnabled
attribute, but remoteStorage access was also gated behind an unused
per-user Flipper flag that nothing ever enabled, so remoteStorage was
inaccessible for everyone.
Gate remoteStorage on service_enabled? like the other services, and make
the admin toggle reflect the LDAP attribute (this also fixes it reading
current_user instead of the user being viewed). E-Mail keeps its Flipper
gate for now.