For web applications, some sort of storage local storage is required ("MUST...") by the OAuth 2.0 spec as part of the defense against Cross-site request forgery (CSRF). The spec suggests cookies or HTML5 local storage specifically, with cookies being a prevalent implementation as your observations show.
Referencing RFC 6749 - The OAuth 2.0 Authorization Framework we find (emphasis added):
- Security Considerations
As a flexible and extensible framework, OAuth's security
considerations depend on many factors. The following sections
provide implementers with security guidelines focused on the three
client profiles described in Section 2.1: web application,
user-agent-based application, and native application.
...
10.12. Cross-Site Request Forgery
Cross-site request forgery (CSRF) is an exploit in which an attacker
causes the user-agent of a victim end-user to follow a malicious URI
(e.g., provided to the user-agent as a misleading link, image, or
redirection) to a trusting server (usually established via the
presence of a valid session cookie).
A CSRF attack against the client's redirection URI allows an attacker
to inject its own authorization code or access token, which can
result in the client using an access token associated with the
attacker's protected resources rather than the victim's (e.g., save
the victim's bank account information to a protected resource
controlled by the attacker).
The client MUST implement CSRF protection for its redirection URI.
This is typically accomplished by requiring any request sent to the
redirection URI endpoint to include a value that binds the request to
the user-agent's authenticated state (e.g., a hash of the session
cookie used to authenticate the user-agent). The client SHOULD
utilize the "state" request parameter to deliver this value to the
authorization server when making an authorization request.
Once authorization has been obtained from the end-user, the
authorization server redirects the end-user's user-agent back to the
client with the required binding value contained in the "state"
parameter. The binding value enables the client to verify the
validity of the request by matching the binding value to the
user-agent's authenticated state. The binding value used for CSRF
protection MUST contain a non-guessable value (as described in
Section 10.10), and the user-agent's authenticated state (e.g.,
session cookie, HTML5 local storage) MUST be kept in a location
accessible only to the client and the user-agent (i.e., protected by
same-origin policy).