solid / solid-spec

Solid specification draft 0.7.0
Creative Commons Zero v1.0 Universal
1.13k stars 103 forks source link

Require cross-origin requests to use bearer tokens instead of cookies? #144

Open michielbdejong opened 5 years ago

michielbdejong commented 5 years ago

This came up in a conversation with @kjetilk just now; he pointed me to https://github.com/solid/node-solid-server/blob/release/v5.0.0/test/integration/authentication-oidc-test.js#L269-L285 and its explanation https://github.com/solid/node-solid-server/pull/1118#discussion_r261361565 - basically I think that 200 should be a 401, and even trusted web apps (whether through server-global config or through acl:trustedApp) should only be allowed to use bearer tokens, not cookies?

cc @RubenVerborgh

gobengo commented 5 years ago

2 cents:

When building systems in the past, I generally have found clearer behavior and less security oopsies from the principle that resource servers / APIs (like solid-server) should only ever infer end-user authorization from the Authorization header, never cookies. Most of the times the resource server never needs any notion of 'session' and can be stateless. And stateless systems tend to be easier to reason about, and definitely easier to scale.

Any relying-party web app/UI that helps a user perform a session of operations on those resources should maintain it's own session (including info to generate Authorization header values for the resource server requests) however is appropriate for that session/experience. Any technical decisions and tradeoffs about storing the session can be made completely orthogonally from technical decisions about the resource server, and e.g. data stores for sessions vs the actual resources can be scaled separately, as they usually have very different properties (transient, tiny vs durable, big).

(This is me rationalizing why I agree with you)

RubenVerborgh commented 5 years ago

I generally have found clearer behavior and less security oopsies from the principle that resource servers / APIs (like solid-server) should only ever infer end-user authorization from the Authorization header, never cookies.

Having dealt with first-hand the issues that come from cookies in NSS, I agree, but just want to mention:

a) performance (our Authorization check includes a network request, although I guess we could cache that) b) one step in the OIDC processs (to obtain the Authorization header) relies on a separate mechanism, such as a cookie

kjetilk commented 5 years ago

Right, so this sounds to me like a practical matter where what we can achieve on the implementation side needs to guide the spec, but it also sounds like we agree that a cookie-less authentication mechanism is desired. Fair summary?

elf-pavlik commented 5 years ago

From what I recall app only sets 🍪 with OP and uses PoP tokens with all the RS (which also act as RP from what I recall). Currently many deployments combine OP with personal storage which seems to result in 🍪 getting used. I use dedicated NSS instance just for OP, custom setup for WebID profile, and another instance of NSS for storage with RDF sources and another for media Non-RDF sources. Organization storages will also stay on dedicated instances. This way I'll have chance to test more general setup than all mighty pod responsible for everything.

gobengo commented 5 years ago

a) performance (our Authorization check includes a network request, although I guess we could cache that)

Yes, it's standard to cache that if you actually need to speed up performance. But that network roundtrip is actually a feature. The cache time is directly proportional (equal?) to the amount of time it takes the authorization server to 'revoke' any given key that is stolen/compromised, etc. Alternatively, for revocation, you can 'push' all revocations to each instance of the authorization server process so that it can check in memory. But at scale you may have lots of revocations and storing every revoked key or client id requires unbounded storage. To bound the storage requirements of the set of revoked keys, people use probabilistic data structures like a bloom filter, which should never have false negatives to "is this key revoked?". It may have a false positive every once in awhile, but even then you'd double check with a network roundtrip only in that rare case so it all works out.

But I think not optimizing this at all is going to be fine for 99% of individual use cases.

b) one step in the OIDC processs (to obtain the Authorization header) relies on a separate mechanism, such as a cookie

If I understand what you mean, this is a cookie at the authorization server's web UI that presents the user a session to help create the Authorization across several pages (e.g. so they can have separate screens for authentication, then authorizing the client to have a JWT with certain claims, then clicking 'are you sure?'). This is still usually logically (and in many projects physically) separate from the resource server, where stateless/sessionless/cookieless is best, and also separate from the relying party, which could be all kinds of clients, some of which might be web apps that maintain their own session/cookie.

IIRC node-solid-server might implement the logical authorization server and resource server in the same process (which is a cool convenience for the 99% who don't have their own OIDC OP running), but the cookie/session from the former might be best thought of as being orthogonal from the latter, and eventually you might want to pull the former out alltogether and have oidc-op not just be a library but able to be used as a separate networked process.

RubenVerborgh commented 5 years ago

Yes, what @elf-pavlik and @gobengo say are correct. So we probably want to adjust the issue title to reflect this.

@megoth The fix did mix up OP and RS auth indeed, but we did so willingly, as this was the old situation (so the benefit is that old apps like Warp start working again).

elf-pavlik commented 5 years ago

This is still usually logically (and in many projects physically) separate from the resource server, where stateless/sessionless/cookieless is best, and also separate from the relying party, which could be all kinds of clients, some of which might be web apps that maintain their own session/cookie.

From: https://github.com/solid/webid-oidc-spec#relying-party-rp

A Relying Party is a POD or a client app that has to rely on an ID Token that's issued by a Provider. In the spec, when Alice tries to access a resource on bob.com, Bob's POD acts as the Relying Party, in that interaction. And correspondingly, Alice's POD, alice.com, will serve as the Identity Provider, again for that interaction.

Incidentally, when Alice tries to access a resource on her own POD, alice.com plays all of the roles -- it's both the Provider and a Relying Party (as well as the Resource Server).

While Solid server doesn't have to act as OP, it looks that with WebID-OIDC solid servers practically always acts as both RP + RS. Solid apps seem to usually act as Presenter https://github.com/solid/webid-oidc-spec/blob/master/README.md#presenter

Very likely digressing here. While currently having :cookie: session with OP makes sense. With navigator.credentials.get({... mediation: silent}) one wouldn't need any cookies https://github.com/solid/solid/issues/142#issuecomment-388896593