Store only SHA256(token) in a new token_digest column and use the digest for the Redis authorization keys instead of the plaintext token. The plaintext is now held in memory only for issuance.
This is the expand step: the plaintext token column stays in place so the old code can keep running during the deploy. A follow-up migration will drop it once rs:rekey_tokens has re-keyed Redis and confirmed no plaintext keys remain.
Refs #259
Real-world user agents (in-app browsers, Android WebView variants) commonly
exceed 255 characters. On PostgreSQL that would raise and 500 the consent
POST and token exchange; SQLite dev/test does not enforce the limit, so it
went unnoticed. Use text columns for user_agent on both tables.
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
- 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
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
db/seeds.rb still required "sidekiq/testing" and wrapped seeding in
Sidekiq::Testing.inline!, but Sidekiq is no longer in the bundle after
the Solid Queue migration, so `rails db:setup` failed with a LoadError.
Run the jobs triggered while seeding inline via Active Job instead,
restoring the previous behavior with the adapter the app actually uses.