aaronpk / draft-parecki-oauth-client-id-metadata-document

Other
1 stars 1 forks source link

Support for localhost & non-HTTPS URIs for development purposes? #12

Open ThisIsMissEm opened 4 months ago

ThisIsMissEm commented 4 months ago

We've currently a restriction for client_id URIs that they MUST be https scheme.

In development environments where all software is running on localhost, we should probably provide an exception to that rule, given the issues that can be faced (self-signed certificates, custom root certificates, etc) with settings up HTTPS in development.

matthieusieben commented 4 months ago

At Bluesky, we actually have another use case in mind:

Allow a dev that does not own/deployed a publicly available client metadata on the internet, to use a "loopback client" with a production server. In this case, what we do is we hard code the metadata in the authorization server, using query strings to allow customizing it.

e.g. http://localhost/?client_name=My%20app&redirect_uris=http%3A%2F%2F127.0.0.1%2Fcb would be resolved by the authorization server into:

{
  "client_id": "http://localhost/?client_name=My%20app&redirect_uris=http%3A%2F%2F127.0.0.1%2Fcb",
  "client_name": "My app",
  "redirect_uris": ["http://127.0.0.1/cb"],
  "grant_types": ["authorization_code", "implicit", "refresh_token"],
  "scope": "openid profile offline_access",
  "token_endpoint_auth_method": "none",
  "application_type": "native",
  "dpop_bound_access_tokens": true,
}

Note that in this case, the authorization server adds other restrictions to the client, such as forbidding prompt=none, reducing the validity of tokens, not displaying images & links, etc. The authorization page basically reads "A program on your device wants to get credentials for your account".

This is our exact implementation: https://github.com/bluesky-social/atproto/blob/main/packages/oauth/oauth-types/src/atproto-loopback-client-metadata.ts

ThisIsMissEm commented 4 months ago

@matthieusieben the way we did this at Inrupt for working on Solid apps was to host a client_id at a temporary publicly accessible URL.

The other option is using something that exposes your localhost server to the public internet (which I'd not really recommend)

The reason for this is that the Authorization Server cannot access a localhost URL (without requesting to itself), so the URL of the client_id must resolve to a publicly accessible resource

matthieusieben commented 4 months ago

We know about these solutions. And we also agree that this is what devs should ideally do.

However, we believe it is critical for our business to make it as easy as possible for 3rd party devs to integrate with our systems, which is why we chose this approach. This is particularly important because we decided to adopt the current BCP, which impose a bunch of stuff (DPoP, PKCe, etc) that make OAuth client particularly difficult to implement. Having to expose a json file on the web is arguably quite simple, but we really want to cut any corner we can here.

For this reason, we: 1) Do require Server Side Request Forgery protection to be in place on the OAuth Provider 2) Add an exception for localhost to be interpreted as described

I realize now that there is another solution that we did not envision; that is to offer a "client metadata as a service" endpoint that would use query params to build the client metadata.

I guess that the use of the localhost hostname makes it clear to the authorization server that it is a particular type of client, and that additional restrictions should be applied.

ThisIsMissEm commented 4 months ago

I realize now that there is another solution that we did not envision; that is to offer a "client metadata as a service" endpoint that would use query params to build the client metadata

This was something that the dev tools team I was on floated as an idea, but we went with writing a generator / validator as a project for @laurin-w — unfortunately product at inrupt killed the project after it was built.

If you do support a client_id metadata documents as a service system, I'd recommend limiting it only to localhost & private IP addresses, as to make it a development-only service, such that it can't compromise production systems.

dickhardt commented 4 months ago

FYI: At Hellō https://console.hello.coop we separate development redirect URIs from production redirect URIs. We only allow developers and testers that are registered with the team to use development redirect URIs. We then enable localhost and 127.0.0.1 by default on app creation to simplify getting up and running.

This simplifies development without the security concerns of a deployment that uses localhost.

ThisIsMissEm commented 4 months ago

@dickhardt so you've two clients? one for development and one for production usage? This seems fine to me. Another approach I've seen pretty commonly is to just run a development AS and a production AS, and have laxer requirements on the former.

However, I don't think the client_id URI can be a non-public URI, since the AS SHOULD request the document at this location or fail the authorization request; perhaps it's worth making this a MUST? i.e., MUST request the document, unless it has a cached copy with respect to cache-control HTTP headers.

dickhardt commented 4 months ago

Just one client. If the provided redirect_uri is not in the list of production redirect_uris, then I check if it is a develop redirect_uri and if the user is part of the team. This is when I would allow localhost development flows. Here is a screenshot of our console page.

image

To support localhost, perhaps you have to be a registered developer at the AS?

I don't think I have all the context of the use cases everyone else involved is wanting to solve. It does seem like there could be different requirements for developers vs users.

It seems one of the requirements is to someone can deploy software or use a service that has their own metadata and their own client_id without having to register their app ahead of time. The question is can they do that on localhost.

matthieusieben commented 2 months ago

Another concern in the case of http://localhost clients is the ability to declare the scopes that the client will actually request. There are basically two approaches here:

matthieusieben commented 2 months ago

The drawback of the approach we decided to adopt is that is it quite counter intuitive with what you would expect the server to do with "regular" accessible client metadata documents. In the case a metadata document is actually available, we do require that document to list all the scopes that may be requested during an auth flow, and will return an error if more scopes are requested at auth time.

ThisIsMissEm commented 2 months ago

I think the direction I'm most leaning towards here is having a service hosted for localhost & development usage, I'm chatting with Fastly about if they could host such.

dickhardt commented 2 months ago

A feature of localhost dev is that it works when offline

ThisIsMissEm commented 2 months ago

Right, but for this internet draft, it's specifically about authentication against decentralised services.

Sure, you could run the Authorization Server and App both on localhost and support localhost client ID metadata documents, that'd be fine, but it's very common that the Authorization Server is say, mastodon.social in which case you need a Client ID Metadata Document that can be fetched by mastodon.social

So we could add language that localhost URIs for Client ID Metadata Documents are supported only if the Authorization Server is also on localhost.

dickhardt commented 2 months ago

Works for me. I was calling out potential confusion using the localhost term when using a service

ThisIsMissEm commented 2 months ago

Yeah, I think the current language was written such that it worked first for production environments, which tbh makes sense & in that scenario typically the AS and Client app are not operated by the same entity