- 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
Variants are currently broken. So we process the original file with the
most common avatar dimensions and stripping metadata, then hash and
upload only that version.
Sometimes, the pubkey might not be imported in the local keychain
(anymore), but at this point in the code it had been successfully
imported at least once before. So we just (re-)import every time for it
to never fail.