New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the 'me' parameter to authorization endpoints to be omitted? #19

Open
00dani opened this Issue Jun 24, 2018 · 10 comments

Comments

4 participants
@00dani
Copy link

00dani commented Jun 24, 2018

IndieAuth, being an extension of OAuth 2.0, is almost compatible with "pure" OAuth 2.0 clients. Assuming that unrecognised parameters are ignored on both ends, the only discrepancy is that under IndieAuth, the initial request to the user's authorization endpoint requires an additional me=https://some.url.here/ query parameter.

This parameter is valuable as a convenience for some kinds of authorization endpoint. In particular, if the parameter were not used, authenticating using IndieAuth.com would involve entering your domain twice: once into the site you're really authenticating with, and once again when you get to IndieAuth.com. Clearly this workflow is more annoying, but it is no less secure.

While true IndieAuth clients should always include me for compatibility with multiple-user endpoints like IndieAuth.com, allowing IndieAuth servers to work without this parameter is viable - the correct me value can be determined from session data on the server itself - and allows vanilla OAuth 2.0 clients to interface with the server directly.

For example, the advanced HTTP client applications Postman and Paw both support OAuth 2.0 authorisation, but not IndieAuth authorisation. When configured with an IndieAuth site's authorization and token endpoints, both apps almost work, save for the missing me parameter. Here's how I configured each app for my site:

Postman configured to authorise with 00dani.me using OAuth 2.0.

Paw configured to authorise with 00dani.me using OAuth 2.0.

If the server allows me to be omitted, then both apps can smoothly perform the OAuth dance and receive a valid token, perfect for testing the site's various other endpoints. :)

Additionally, many OAuth client libraries use a three-step approach to achieve both authorisation and authentication: the standard authorization and token endpoints, followed by an "info" endpoint to discover the authorised user's identity. IndieAuth quite easily supports this flow too: the token endpoint can trivially also be used as an info endpoint. So allowing the omission of me would also enable these libraries to authenticate with IndieAuth-enabled servers.

To sum up: in order to improve compatibility with OAuth 2.0 clients, I think the IndieAuth standard should clarify that the purpose of the initial me parameter is convenience, not security, and that servers are allowed to make it optional if desired.

@aaronpk

This comment has been minimized.

Copy link
Member

aaronpk commented Jun 24, 2018

Thanks for the writeup!

This is a really good point. The me parameter in the request is really more of a hint, since the authorization server will ultimately return the final me value at the end of the flow. The client just has to verify that it's on the same domain that was used to discover the authorization endpoint.

I implemented an IndieAuth server into my website quite a while ago, so I checked the code there. It turns out I had some code that verified the me parameter was in the request, but it actually completely ignores that value after that. Since my site is a single-user site, it's always going to return https://aaronparecki.com/ as the profile URL at the end.

I'm inclined to make a change to the spec that says clients SHOULD include the me in the authorization request, but that the authorization server should not require the parameter. I do like that it helps IndieAuth be more in line with OAuth 2.0.

@aaronpk

This comment has been minimized.

Copy link
Member

aaronpk commented Jun 24, 2018

In order to continue the OAuth compatibility, this would also mean that the code exchange (token request) step would need to work without the me parameter as well.

This breaks the ability to use a shared token endpoint between users, since the token endpoint wouldn't know how to verify the authorization code without the me URL at that point. The workaround for shared token endpoints is to have a per-user token endpoint URL, like tokens.indieauth.com/user.example.com/token kind of like how I have per-user webmention endpoints on webmention.io. I'm not sure I'm super happy about this though.

@00dani

This comment has been minimized.

Copy link
Author

00dani commented Jun 25, 2018

Oh, whoops, you're right - the token endpoint also takes a me parameter, and that's a requirement rather than a convenience since there's no UI for making that request. (I think I mixed it up with the IndieAuth authentication flow - you already don't pass me to the authorization endpoint when you POST to it to verify the access code.)

It'd be easy enough to allow servers to work without me for both endpoints, and then those servers would be compatible with pure OAuth 2.0 - most folks' personal sites could do this, and it shouldn't be a problem to change the spec that way. Of course, IndieAuth-supporting clients should still always provide the parameter, for convenience if nothing else.

Ideally, though, OAuth compatibility would be possible for "aggregate" servers like IndieAuth.com as well, since that would enable pure OAuth 2.0 consumers to trivially support all IndieAuth identities by configuring just one set of endpoints. It sounds like that mightn't be possible, though, since you obviously can't use per-user endpoints in such a situation. 🤔

@Zegnat

This comment has been minimized.

Copy link
Member

Zegnat commented Jun 26, 2018

Such multi-user services would be possible as long as all people who choose to use one as their authorization endpoint also use a token endpoint from the same service. Because if you have tight coupling between the authorization and token endpoints it is already known against which endpoint it needs to check.

So where an authorization endpoint could be made so it SHOULD NOT require the presence of a me parameter, a token endpoint can only be made in a way where it MAY make the me parameter optional if it has other ways to validate the token.

I am not a huge fan of MAY in specifications though.

@00dani

This comment has been minimized.

Copy link
Author

00dani commented Jun 26, 2018

An alternative possibility would be to prescribe the format of IndieAuth access codes, as part of the standard. For instance, we could prefix the usual arbitrary implementation-specific access code blob with the expected me value, making it easy for token endpoints to discover the correct authorization endpoint. code=https://00dani.me/$C5r1cuqJk1fUTGrWX4DPHz44jxpgHF or something like that. Then, of course, pure OAuth 2.0 clients would pass through that extra piece of information with no trouble whatsoever, since it's embedded in an existing standard parameter.

It's certainly a messy approach, though, and one might question whether OAuth client compatibility is worth adding this complexity to IndieAuth. Additionally, making a change like this now would introduce potential incompatibility: a token endpoint that knows it can pull information out of the access code might still receive an access code from an authorization endpoint that doesn't embed information in the prescribed format, for instance.

Still, prescribing a format for access codes might not be quite as unreasonable as it seems: after all, client IDs are also treated as opaque in pure OAuth 2.0, whereas in IndieAuth they have a prescribed and meaningful format.

@aaronpk

This comment has been minimized.

Copy link
Member

aaronpk commented Jul 6, 2018

tl;dr The more I think about it, the more I think this parameter enables a use case that isn't really necessary. The me parameter in the code exchange step specifically allows for a token endpoint to be detached from both the Micropub endpoint and the authorization endpoint.

Full details below.

The different use cases that are all supported right now:

Integrated Micropub/Token/Authorization Endpoints

This is the simplest case in terms of architecture, but the most amount of work for a developer. In this case, someone writes all three parts of the system. Since they are part of the same system, the mechanism by which the token endpoint validates authorization codes does not need to be standardized, it's all internal.

Both my website and the Wordpress IndieAuth plugin fall under this case.

Authorization Endpoint Service, Built-In Token and Micropub Endpoints

In this case, someone is building a CMS that includes a Micropub endpoint as well as a token endpoint. However, they want to speed up their development, so they use an authorization endpoint service such as indieauth.com.

The client sends the auth code to the token endpoint, and since the token endpoint is part of the CMS, it already knows the only place it can go to validate the auth code is the authorization endpoint service that it's configured to use. Therefore there is no need for the me parameter, which normally tells the token endpoint where to go to verify the auth code.

Authorization Endpoint and Token Endpoint Service

Specifically this case is where a service provides both an authorization endpoint and token endpoint. This is the quickest path to building a Micropub endpoint, since all you need to do is build out the Micropub endpoint itself, and when any requests come in with a token, the endpoint goes and checks whether the token is valid by testing it against the token endpoint service.

This is a very common case with peoples' individual websites, as it offloads the development and maintenance of the security bits to a service. I provide these as a service at indieauth.com and tokens.indieauth.com.

The interesting thing though is that when a single service provides both, there is also no need for the me parameter at the code exchange step, since the token endpoint already knows where it needs to verify the authorization code since the code was issued by the same system.

Separate Authorization Endpoint and Token Endpoint Services

The only case where the me is needed is when the authorization endpoint and token endpoint are both used as services and they are separate services. Imagine a standalone token endpoint service: the job of this service is to verify authorization codes and issue access tokens, and later verify access tokens. In this situation, a request comes in with an unknown authorization code and it needs to verify it. Since it was not part of the system that issued the code, it needs to know how to verify it. Right now, this is enabled because this request also includes the me parameter, so the token endpoint goes and looks up the user's authorization endpoint and verifies the code there.

The thing I'm realizing though is that this is really quite an edge case, and one that I don't think is actually very important. Typically someone who is building a Micropub endpoint themselves will first start by using an authorization/token endpoint service, and there is no benefit to them if those are two separate services. In fact it's probably easier if they are just part of the same system since it's less moving parts to think about at this stage.

Later, that person can decide they want to take over issuing tokens, but still don't want to build out the UI of an authorization service. At this point, they fall under the second use case above. They build out a token endpoint into their software, and since they're using the authorization endpoint service they know where to verify authorization codes.

On the other end of the spectrum, you have people who build the whole thing out themselves, like my website and the Wordpress plugin. In these cases the me is completely irrelevant in the code exchange step.

The particular situation that the me enables is using a separate service for the authorization and token endpoints, and I can't think of a case where that is actually important.

@aaronpk

This comment has been minimized.

Copy link
Member

aaronpk commented Jul 6, 2018

Here's a quick survey of current implementations of token endpoints:

Integrated Micropub/Token/Authorization Endpoints

  • p3k - verifies the me parameter exists, but does not use it for anything
  • Wordpress IndieAuth plugin - verifies the me parameter exists, but does not use it for anything source
  • Micro.blog - does not check or use the me parameter details
  • Commentpara.de - does not check or use the me parameter source
  • Skein - does not check or use the me parameter source
  • Known - does not check or use the me parameter source
  • Silo.pub - checks that the authorization code was issued to the me in the request. not strictly necessary since it ends up using the me that is stored with the authorization code anyway. source

Authorization Endpoint Service, Built-In Token and Micropub Endpoints

  • Redwind - uses indieauth.com as the authorization endpoint service source
  • Neonblog - uses indieauth.com auth.php token.php

Authorization Endpoint and Token Endpoint Service

  • acquiescence - verifies the me parameter exists, but does not use it source

Standalone Token Endpoint Service

  • tokens.indieauth.com - uses the me to find the authorization endpoint to verify the code source
    • In practice, most people end up using tokens.indieauth.com along with indieauth.com since those are the examples in all the documentation.
  • mintoken - uses the me to find the authorization endpoint, but only allows a whitelisted set of endpoints to be used source [readme](https://github.com/Zegnat/php-mintoken#setup readme)
    • This makes mintoken on the edge of a standalone service, since it does end up being tied to particular authorization endpoints. It does provide the ability to be used with multiple, so the me parameter enables this feature.
@Zegnat

This comment has been minimized.

Copy link
Member

Zegnat commented Jul 6, 2018

Discovery might be one of the most involved parts of the specification, so I am not too surprised people aren’t using me if they can avoid it. Though the way the survey looks – where me isn’t even checked for – shows even less implementation than I expected!

Although I like how the two endpoints are uncoupled, it seems like the spec might be able to drop me entirely if we go by implementations alone.

(P.S. Mintoken does full auth endpoint discovery and it would be trivial to use it as a standalone service, the whitelist exists only because I don’t think everyone selfhosting Mintoken wants to automatically be providing it as a service to the entire world.)

@aaronpk

This comment has been minimized.

Copy link
Member

aaronpk commented Jul 7, 2018

If we drop the me from this token exchange step, then we also will need to drop the text in section 6.3.2 that talks about discovering the authorization server.

@mblaney

This comment has been minimized.

Copy link

mblaney commented Oct 18, 2018

Hi I saw this issue come up in irc and just wanted to add some extra data since I just finished adding a token endpoint to dobrado. I currently check that the me parameter matches the logged in user at my authorization endpoint, because it's a multi user system. But since I then exit if they don't match, I don't actually need it and could just as easily store the logged in user's url as the me parameter.

I also check me in my token endpoint, but like has already been said above they're on the same server so it's just an extra check that isn't required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment