solid / webid-oidc-spec

WebID-OIDC Authentication Spec v0.1.0
MIT License
56 stars 18 forks source link

Proposal: use PoP token issuer (and with that, id token audience) to distinguish web apps #12

Open michielbdejong opened 5 years ago

michielbdejong commented 5 years ago

I wasn't sure whether to create this issue on this repo or on the WAC repo, but this is a suggestion that came up during the 'acl:trustedApp' session of the unconference we did in Boston on 22 March, and I still had a todo item that I needed to convert it to a github issue. :)

I think the idea that @dmitrizagidulin coined was that instead of identifying web apps by their Origin, we could assign a client_id to web apps when we give them access. I think if it works the same way in OIDC as in OAuth, then that means the pod would maintain a list of whitelisted apps, tying client_id to origin.

@dmitrizagidulin so one thing I'm not clear on now that I'm writing this down, is how would this be different in result, when comparing it to the current system where the pod has a whitelist of origins, rather than of client_id's?

cc @jaxoncreed

dmitrizagidulin commented 5 years ago

@michielbdejong

so one thing I'm not clear on now that I'm writing this down, is how would this be different in result, when comparing it to the current system where the pod has a whitelist of origins, rather than of client_id's?

Excellent question. (And just as an added detail, I would advocate that the solid oidc clients actually /use/ the client URLs as client_ids, when registering.)

The main advantage is this: It would extend the "trusted origin" ACL mechanism (and any variations we might come up with), from browser-apps-only, to server side and native apps as well. Meaning, if we treat client_ids as origins, that means all of a sudden, we have greater control over the permissions of service apps, desktop, mobile apps, etc. (As opposed to just assuming they are fully trusted by virtue of not being in the browser, as is the case now.)

justinwb commented 5 years ago

The main advantage is this: It would extend the "trusted origin" ACL mechanism (and any variations we might come up with), from browser-apps-only, to server side and native apps as well.

For reference - there's a documented example of doing exactly that here

jaxoncreed commented 5 years ago

Briefly discussed on community call. There's preliminary consensus on the developer's side to implement this.

gobengo commented 5 years ago

on the developer's side to implement this.

(to clarify: this should be normative language in the spec too, not just implemented in certain developers' apps/usecases)

jaxoncreed commented 5 years ago

@gobengo Correct. Given the alternative would be inherently insecure, this will now be normative.

jaxoncreed commented 5 years ago

@dmitrizagidulin I just want to confirm that the following is kosher within oidc specs:

Usually, app developers register their app with IDPs. That's where they get their client_id and can set custom redirect urls. But, under Solid, we obviously don't want to require app developers to register with every pod out there, and we certainly don't want apps to be able to redirect just anywhere.

So, after some discussion, the solution I came up with was having a hard restriction in the spec that says all redirect routes MUST be on the same domain as the client_id. Does that sound okay?

dmitrizagidulin commented 5 years ago

Yeah, I do think that's a reasonable requirement. (Redirect uri to be similar domain as the client_id).

As far as the Dynamic Registration spec, I do vaguely recall that the spec itself points out that the client should feel free to suggest what client_id it wants (meaning, might as well use its uri), but that the IDP would be free to reject it and give it their own.

So yeah, I think it's a decent idea. It's very slightly more restrictive than the current OIDC spec, but in our case, it makes a lot of sense.

dmitrizagidulin commented 5 years ago

(And I do like your suggestion that having those two domains be the same means that there's less need for required Dynamic registration.)

elf-pavlik commented 5 years ago

Usually, app developers register their app with IDPs.

I don't see in this spec explicit mention of OpenID Connect Dynamic Client Registration

Client Registration Response

client_id REQUIRED. Unique Client Identifier. It MUST NOT be currently valid for any other registered Client.

It doesn't seem that client has any influence on how server creates client_id, this spec could mandate specific mechanism for WebID-OIDC

elf-pavlik commented 5 years ago

I think we should consider making it possible to deploy different apps on the same origin. Let's say I would like to only use open source apps and always deploy them on my server. If we don't allow multiple apps (clients) on the same origin I would need to have setup where each app gets its own subdomain.

dmitrizagidulin commented 5 years ago

@elf-pavlik

It doesn't seem that client has any influence on how server creates client_id

So I think I was thinking of Section 8.2 - Stateless Registration, in the Dynamic Registration spec. I think that one could be interpreted that the client can specify their own client id (if the server is willing)

justinwb commented 5 years ago

I think we should consider making it possible to deploy different apps on the same origin. Let's say I would like to only use open source apps and always deploy them on my server. If we don't allow multiple apps (clients) on the same origin I would need to have setup where each app gets its own subdomain.

An option to consider that I believe would address this point by @elf-pavlik:

timbl commented 5 years ago

There seems to be a confusion between webapps and automomous apps, in the protocol given, where it says "Upon her confirmation here, DecentPhotos will be added as a trusted application in her WebID Profile, identified by its application/agent WebID (https://decentphotos.example/appid#this), and..." The trusted app system in Alice's profile gives blanket authority to a web app at a particular origin. It only makes sense for web apps, where the Same Origin Policy would normally have the browser block the origin because of webapps being normally untrustworthy. The app's webid should surely just get added to the ACLs for the files, like that of a human agent.

justinwb commented 5 years ago

The trusted app system in Alice's profile gives blanket authority to a web app at a particular origin. It only makes sense for web apps, where the Same Origin Policy would normally have the browser block the origin because of webapps being normally untrustworthy. The app's webid should surely just get added to the ACLs for the files, like that of a human agent.

Good catch @timbl - you're correct. The intent for that workflow is to demonstrate autonomous apps, which are authorized through the app's webid in the ACLs. Adding to the user's trusted apps in their profile would be unnecessary. Will adjust that.

michielbdejong commented 5 years ago

There are two distinct proposals being discussed here:

I would vote we go with the first one. At least for now, we can still consider the second one in a few months from now?

RubenVerborgh commented 5 years ago

The second one should be an issue in Solid spec.

RubenVerborgh commented 5 years ago

For the original proposal, one problem: isn't the client_id specific per app per browser? Meaning that, if I switch browsers or clear local storage, that I have a "new app"? Or is part of this proposal to change that mechanism?

dmitrizagidulin commented 5 years ago

@RubenVerborgh

isn't the client_id specific per app per browser?

Right, so, part of the proposal is to change the mechanism. Currently, each app has no identifier that persists across browser sessions etc - each time, it asks for the IdP to generate a random client_id for it. This works for authentication purposes, but wastes electrons, and is not very helpful for authorization.

So part of the proposal is to have the app propose a client_id to the IdP, during registration. (Or to possibly even consider section 8.2, stateless registration). Specifically, each app would propose its url (which should match the redirect_uri in some way), as the client_id. Which would then be able to be used in the ACL mechanisms.

RubenVerborgh commented 5 years ago

This works, but one, wastes electrons, and is not very helpful :)

Yeah, not to mention create a huge list on the server of one-time IDs that need to be kept just in case, but will never be used.

So part of the proposal is to have the app propose a client_id to the IdP, during registration.

Got it, good.

michielbdejong commented 5 years ago

Great, I think we all agree then. :) Created issues on two implementations of this spec, to follow this new recommendation. We should also edit the spec text to include it.

zenomt commented 5 years ago

sorry to be late to this thread.

@dmitrizagidulin

So I think I was thinking of Section 8.2 - Stateless Registration, in the Dynamic Registration spec. I think that one could be interpreted that the client can specify their own client id (if the server is willing)

that's not what that section means. 8.2 Implementation Notes on Stateless Dynamic Client Registration says that an authorization server can encode information about the client into the client_id it returns. there's no way for the client to suggest to the server what client_id it would like, and the authorization server should definitely not let the client specify its client_id, because that would destroy a huge aspect of OIDC's security model.

https://github.com/zenomt/python-webid-oidc/blob/master/oidc.py#L199 is the sort of thing Section 8.2 was talking about. that method results in a client ID that looks something like EAEA.Mnf4LyBu24RRpAySC8If3XSYYsvh.FZ_3Y3mZHHDLKY5O. it encodes the requested (and only allowed) response types, hashes of the (only allowed) redirect URIs, and a random salt, so that the client_id is globally unique and can have a client_secret without having to store per-client information in the server.

dmitrizagidulin commented 5 years ago

@zenomt - you’re absolutely right, thanks for the catch.

I have an alternate proposal, which should accomplish our overall goal, while still being compliant with the spec. (I’ll ask some of the oidc/oauth folks, as well as this community, to review it.)

zenomt commented 5 years ago

one solution that comes to mind is to extend WebID-OIDC to have another claim in the id_token's claim set: the redirect_uri to which the token (or code) was sent. the redirect_uri claim (or its origin) could be used for access control, for example.

edit the redirect_uri can be added to the audience claim (which can be a list). this requires that the azp claim is present and is the client_id to which the token was actually issued.

one thing i've never been clear on, for the browser-app implicit workflow case: you don't have anything to put in the Authorization: header for some random (not the IdP) server. an id_token is not an access token. the access_token is only meaningful to the IdP and to someone who has the id_token and can compare the access_token to the at_hash claim. perhaps there's some Solid spec i haven't found yet that defines a way to "log in" with an id_token and access_token and get a cookie set or something?

otherwise you need to log in directly to the random server with its normal login page, specify your IdP, and it and your IdP do the OIDC dance.

zenomt commented 5 years ago

i was thinking about my above proposal a lot overnight. while the audience claim seems like the right place to put the redirect_uri, step 3 of 3.1.3.7 ID Token Validation (OIDC Core) is ambiguous and could cause problems. specifically, it says "The ID Token MUST be rejected if the ID Token does not list the Client as a valid audience, or if it contains additional audiences not trusted by the Client." the second clause could mean either "it contains an audience that is not explicitly trusted" or "it contains an audience that is explicitly untrusted". there could be a problem if the redirect_uri is always there for verifying parties that choose the first interpretation.

new proposal: the redirect_uri is added to the aud claims only when the webid scope (or any other scope whose semantics includes this step) is requested by the client.

this still doesn't fully solve the "web app wants to interact with a random server on behalf of the user as their webid" problem though. you want some way to prove to the server that you're acting directly on behalf of the user, and that you're not a MITM that has gotten your hands on an id_token and are presenting it as your own.

i have a sketch of a solution to this second problem. there can be a way to "log in" to the server on behalf of your user (perhaps a new WWW-Authenticate method, or an endpoint discovered via a .well-known URL, or something). the method will include, among other things, a challenge nonce from the server. the app/agent combines this nonce, its own nonce, and the URI or origin or something of the resource it's trying to log in to or access, hashes them all together (perhaps with an HMAC-based construction), and uses that as the nonce to the OIDC server. the returned (signed) id_token will include this nonce. the app/agent then presents the id_token, the challenge nonce, the app/agent nonce, and the URI it thinks it's talking about to the server's login endpoint. the server can look up/verify its nonce to correlate the attempted login session, verify the id_token signature, perform the same hash that the app/agent did to verify the nonce in the id_token, confirm the server URI used refers to it or is allowed, and then apply access controls on the webid and the redirect_uri (or its origin, or whatever it wants). if access is allowed, the server can set a cookie or return an access_token or something.

since the app/agent decides the server URI to include in the nonce it sends to the OIDC server, a third party can't trick the app/agent into giving it an id_token (and other stuff) that the third party could then turn around and use on another server.

update: here's an example exchange illustrating my sketch a little more concretely.

zenomt commented 5 years ago

over the weekend i baked my idea into a complete protocol and wrote up a spec. it addresses app identification for both WebID-OIDC and WebID-TLS, can work with or without any modification to WebID-OIDC, formalizes handling of self-issued OIDC id_tokens, handles the "app/agent accessing resources on random servers on behalf if its user" problem for WebID-OIDC and WebID-TLS, and addresses some of the issues of using WebID-TLS in real life.

i'm happy to submit it in a PR as a proposal, but i'm not sure against which repo or path would be the most appropriate.

@dmitrizagidulin any suggestions?

edit: move link to github this is the full spec, if that will help suggesting an appropriate place to submit properly: WebID HTTP Authorization Protocol

jaxoncreed commented 5 years ago

Hi @zenomt thanks so much for your contribution! I'd like to clarify a few things with you. Would you be open to hopping on a call, or I'll be in the Fremont / Mountainview area next week so we could meet up in person.

zenomt commented 5 years ago

hi @jaxoncreed . i'm happy to have a call. i made my email address visible in my github profile, so you can email me directly to arrange.

zenomt commented 5 years ago

i hope i can be excused for not having read all closed tickets in all solid source repos. :) i did find a discussion by @dmitrizagidulin at https://github.com/solid/node-solid-server/issues/1061 which describes exactly the main situation i address in my proposal (IdP/OP, app, and RS are all separate), the discussion including references to PoP tokens (thanks @jaxoncreed for pointing me in that direction when we spoke on the phone).

i haven't yet found documentation on acquiring and using PoP tokens, except for source code.

edit i've read enough of the source that i think i understand how PoP tokens are acquired and would be used in the above 3-party scenario. i have some concerns but they should be made against a spec, not against the source.

i would like to suggest that security protocols (especially) be documented before implementation, both so that the protocol can be examined and vetted by as many security experts as possible, and so that implementations can be audited against the specification to make sure the implementations are correct.

RubenVerborgh commented 5 years ago

Problem this issue aims to solve: https://github.com/solid/webid-oidc-spec/issues/33

michielbdejong commented 5 years ago

In the end we decided that the resource server should look at the issuer of the PoP token. The token validation library will additionally check that is issuer is included in the audience list of the identity token that the PoP token is about.

michielbdejong commented 5 years ago

at least that's the conclusion @dmitrizagidulin and @jaxoncreed and I just reached :)

RubenVerborgh commented 5 years ago

Can there then be an issue proposing that solution in detail please?

RubenVerborgh commented 5 years ago

Also, let's please keep this draft solution as-is, and propose different solutions in different issues.

And let's not forget to first complete https://github.com/solid/webid-oidc-spec/issues/33, such that we know what requirements the solution should satisfy, and that we can check whether the proposed solution does.

jaxoncreed commented 5 years ago

The detailed explanation of this is in https://github.com/solid/webid-oidc-spec/pull/27. I'm thinking of eliminating the applicationWorkflow.md file in favor of the detailed version

zenomt commented 5 years ago

Also, let's please keep this draft solution as-is, and propose different solutions in different issues.

+1 . however, without a new Issue for this yet, i'd like to say

... we decided that the resource server should look at the issuer of the PoP token. [...] issuer is included in the audience list of the identity token ...

also +1

i feel that this aspect is orthogonal to the "what exactly is the appid" problem. this addresses "how do you find it, whatever it is".

timbl commented 5 years ago

This whole issue seems completely misguided and to be based on a complete misunderstanding about web apps. The definition (more or less) of a web app is (more or less, from our point of view) a script which a user runs in a browser in a web page, by virtue of just following a link to the page. The browser warns the server with the "Origin" header, which the web cannot control (nor the user). The Origin header is the only way of identifying a web app. No other identification is appropriate or needed. Propose this issue be closed as it seems to be adding a lot of FUD.

timbl commented 5 years ago

(Bots, independent services running on the internet, are different. They have a webid of their own. It is good to call them bots instead of apps as apps can be web apps or native apps or bots. The Origin header is never used at all in any way with bots)

jaxoncreed commented 5 years ago

@timbl Do you not think there is a place for web apps that have a backend component that queries pods instead of the web browser? These should still be protected by some kind of trusted app acl, but wouldn't have the origin header because it's not coming from the web browser.

Maybe a backend component isn't really the Solid way of doing it? What do you think?

zenomt commented 5 years ago

@jaxoncreed @timbl also please see my comment in https://github.com/solid/webid-oidc-spec/issues/33#issuecomment-505227042 about why i think this is also (and i think primarily) applicable to pure in-browser app scenarios.

and also discussion at https://github.com/solid/solid-spec/issues/144 .