Closed 00dani closed 3 years ago
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.
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.
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. 🤔
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.
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.
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:
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.
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.
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.
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.
Here's a quick survey of current implementations of token endpoints:
me
parameter exists, but does not use it for anythingme
parameter exists, but does not use it for anything sourceme
parameter detailsme
parameter sourceme
parameter sourceme
parameter sourceme
in the request. not strictly necessary since it ends up using the me
that is stored with the authorization code anyway. sourceme
parameter exists, but does not use it sourceme
to find the authorization endpoint to verify the code source
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)
me
parameter enables this feature.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.)
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.
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.
Been a while, but I'd like to close the loop on this! I reviewed the discussion here and came to the same conclusion again.
I'd be curious to hear how any new IndieAuth implementations in the last year land on this thread too. If you're looking at this for the first time, the tl;dr of the suggestion is:
me
parameter but the parameter is not required. Since the identity provider requires the user to authenticate somehow anyway, it can use that to determine their final me
value returned to the application.me
parameter at all. (Generic services like Mintoken can come up with an encoding scheme to include information in the authorization code if needed, and we could standardize this later as well.)There are very few token endpoints that are fully separate from authorization endpoints still. The few that exist (like Mintoken) should not be blockers on this, IMO. I am definitely :+1: on dropping me
wherever we can.
Should it really be a SHOULD
or would a MAY
be even better for the initial GET request? How strongly should the inclusion be phrased?
Something like this would help encourage me to go with a multi-user approach for my site (more so so I can use it for more business-y reasons). Koype uses the me
value only for the initial request but does store it to do a (unnecessary tbh) check that this is what the requester was looking for.
(Originally published at: https://v2.jacky.wtf/post/619ceae3-38ca-4e9d-b223-c038abc9d0c3)
I have now updated Mintoken to take a me
from the endpoint’s query parameters instead of from the actual request body. This basically means you will get a unique endpoint URL per user (as mentioned in this thread before). So that makes for one less implementation that is lagging behind.
I'm making an IndieAuth plugin for ProcessWire. Like the WordPress plugin, it verifies me
parameter exists but doesn't do anything else with it. +1 for making it optional based on this discussion.
I think I misunderstood this topic back in that earlier survey of different implementations. Micro.blog does require me
and is currently a little strict about it matching the default blog for the signed-in user. However, after the user approves the app, it no longer cares about me
. Making it an optional parameter should not be a big deal.
My question is about @aaronpk's comment:
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.
Is that still true? It seems like you could still have a single endpoint for multiple users, if the server knew which user a token was for. I'd rather not change my IndieAuth endpoints, so want to make sure I understand this.
Is that still true? It seems like you could still have a single endpoint for multiple users, if the server knew which user a token was for.
It is still true if we are talking about truly separate token endpoints, like tokens.indieauth.com or a public hosted version of Mintoken. These token endpoints need a way of knowing where they can go to verify a code
when a token is being requested (step 6.3.1.). These token endpoint seem to be extremely rare though.
If your token endpoint is integrated with the authorization endpoint in some other way. Like both having access to the same database, or just being part of the same software in general, you probably do not need this. The token endpoint can just check the code
that was sent to it directly and figure out who the user is. I am thinking that is the route you (ie. Micro.blog) would go.
The fact that most implementations already share back-end infrastructure is why #44 is up for consideration too. It may not make sense to try and specify all the steps that are neccessary for a token endpoint to check things against the authorization endpoint, when implementation chose to take a back road instead of the overhead of extra HTTP calls.
@Zegnat Thanks for the clarification! That makes sense. Micro.blog does handle both parts of IndieAuth so it doesn't sound like it will be an issue for me.
This was discussed at the IndieAuth Popup Session, and the outcome of the discussion was:
I seem to have misremembered something about our discussion during the call, or thought we were talking about specifically dropping the me
from the token endpoint request not the authorization request. Going to leave this issue open and we can address this during the next session.
That is what was confusing me on the call. I'm in favor of dropping me
from the token endpoint (where I currently ignore it anyway). But it's a much bigger change to drop me
from the authorization request.
I should expand on this since I guess "much bigger change" is open to debate. For the authorization request, I currently check that me
matches a blog for the current signed-in user's account. I also return the same value for me
in later responses. This lets someone sign in with (for example) both "manton.micro.blog" and "manton.org".
Dropping me
from the authorization endpoint would simplify my code. But without that me
parameter as a clue, the me
in server responses would need to be handled one of 2 ways:
me
in server response could always be the same for a user. The spec says that it must be at the same domain, so that might cause a problem if the user enters "manton.micro.blog" but the server returns "manton.org"./indieauth/auth
, it could be /indieauth/manton.org/auth
or something similar.As I write this out, I'm leaning toward that 2nd option and no longer relying on me
so that it could be dropped.
Okay, I reviewed this again, this time not at the end of a long day, and I see what happened.
This issue started out as a discussion about dropping me
from the authorization request, and then we realized it would require dropping the me
from the token request too. Dropping it from the token request is a smaller change that affects only the shared token endpoint situation, which was the focus of the topic on the call. We made a decision on the token request issue, and then didn't follow back up with the authorization request aspect.
So, I still agree with my initial comment from earlier: https://github.com/indieweb/indieauth/issues/19#issuecomment-504685451
- The initial GET request to the authorization endpoint SHOULD include a
me
parameter but the parameter is not required. Since the identity provider requires the user to authenticate somehow anyway, it can use that to determine their finalme
value returned to the application.
How does this affect existing services?
me
value in the request anyway, since they know the identity of the only user in the system already.me
present in the request, this account chooser step can be bypassed, providing a better user experience.me
value. Without that, if the user is not yet logged in, the site would have to again ask the user to enter their URL, which is not a good user experience. However it is also not impossible to do this.So in some cases the me
in the request is completely ignored, and in the other cases it provides a hint that can improve the user experience. In no situation is the me
value anything related to the function or security of the flow itself.
There is precedent for this in OpenID Connect as well. There is a parameter called login_hint
, which Google describes as:
When your app knows which user it is trying to authenticate, it can provide this parameter as a hint to the authentication server. Passing this hint suppresses the account chooser and either pre-fills the email box on the sign-in form, or selects the proper session...
I would like to propose that we make the me
URL optional in the authorization request, but clients SHOULD provide it. And clarify that in the authorization request, this me
value is a hint that can be used as an optimization but is not yet a verified value.
This sounds great. Thanks @aaronpk.
One similar thing to this issue is that technically the user doesn't need to enter their full profile URL in the client, they only need to enter enough to discover their authorization endpoint. This provides an interesting optimization of the UX that an application can do.
The application can ask the user to enter the "server name" or "domain name" of their website rather than their full URL. For micro.blog the user can enter just micro.blog
in a client. The client would fetch https://micro.blog/
and look for the authorization endpoint, which is the same as the client would have found if the user had entered manton.micro.blog
or manton.org
.
This also matches the user model of Mastodon more closely, where users typically don't think of their full Mastodon profile URL as their identifier, but they know their username and server name. The user can enter the server name such as mastodon.social
, and the flow can continue on as normal with the server eventually providing the me
URL of https://mastodon.social/@aaronpk
in response to the authorization code request. The user never even really has to be aware of their full profile URL in order for this to work.
The application can ask the user to enter the "server name" or "domain name" of their website rather than their full URL.
Would that in turn mean that the me
coming from the client would not be a full URL in that case either? To go back to the Micro.blog example: @manton could write either manton.org
or micro.blog
, and both will point at the same authorization endpoint where it wants to know what the original URL entered was, right? To pick the correct me
to return? Would it be enough in that case with just a host name?
If that is a case we expect authorization endpoint to take consideration of, login_hint
seems way more descriptive than me
. Especially when the rest of the spec always has me
being a full profile URL.
Well the client still needs to return a fully qualified URL such as https://micro.blog/
(described in URL Canonicalization), but it doesn't necessarily have to be the same as the URL that is returned at the end. That has always been true though, so it's no new change. I was just suggesting that some clients may want to avoid asking users to enter their full URL since it's not strictly necessary anyway.
I do agree that login_hint
is a better name, but I'm not sure it's actually worth making a breaking change for that.
I do agree that
login_hint
is a better name, but I'm not sure it's actually worth making a breaking change for that.
Not in favour of a rename, no. But the Authorization Request step currently describes me
as "The profile URL that the user entered". And I am wondering if it is worth changing that wording away from "URL" is a more hint-like value is acceptable.
Yes I'd like to change the wording there to suggest that it is a hint, and that it may not be the user's full URL.
Interesting. I'll update Micro.blog to support this. I could imagine someone accidentally entering micro.blog
when signing in instead of their blog hostname, so it would be nice if that automatically worked.
Does this cause any problems for the spec requirement that the me
should be at the same domain name? If someone enters micro.blog
, I could resolve it to manton.micro.blog
in the token response, but if I returned manton.org
instead that seems to go against the spec. So basically the me
in the response would need to change to match the original me
hint from the user.
Mmm. I think that is right. The returned me
"MAY be different from what the user initially entered, but MUST be on the same domain." So I guess micro.blog
is only allowed to resolve to micro.blog/manton
?
Actually this is a good question. It's meant to be an exact match, so https://micro.blog
could only resolve to https://micro.blog/manton
, not https://manton.micro.blog
. We could explore relaxing that requirement to allow subdomains, but there's a lot of tricky edge cases there to consider to make sure we don't open up some attack vectors.
But that's the other reason clients "should" provide the me
in the request, because the client needs to make sure the resulting URL is on the same domain as the initial URL, so it's helpful for the client to give a hint to the AS about what the user entered, which is what the client will be comparing against later.
Without the me
URL, these multi-user and multi-domain authorization servers will have a tough time making sure the user doesn't land back at the client with an error about a mismatched domain name.
In that case, I don't think I'll support the user entering micro.blog
. Profile URLs like https://micro.blog/manton
are not the same as subdomains on Micro.blog, because a single username can have multiple blogs. I'll still allow me
to be optional, but if it's specified it really should be the blog URL that the user wants to post to.
So summing up, is the following the change proposal for me
at the Authorization Request step?
me
query component:
me
query component for client and endpoint:
The client SHOULD provide a
me
query component to the authorization endpoint. Either as the exact value thaet the user provided to the client, or the canonical profile URL resulting from the Discovery step.
The authorization endpoint MAY use the provided
me
query component to select a validme
value for the Authorization Code responses: Profile URL Response, or Access Token Response. This is specifically helpful for authorization endpoints where users have multiple supported profile URLs, so the authorization endpoint can make an informed decision on which profile URL the user meant to identify as.
This sounds like it would be backwards compatible with current IndieAuth, clearing up the confusion around what to do with me
when you receive it on the endpoint side, and wrap up this GitHub issue.
I added this text as well as some additional clarifications in this commit
https://github.com/aaronpk/indieauth/commit/6638090a8ec17ee85422eac3a76b5fe6e6c6c706
Looks fine to me! I really think that other than the few clarifying lines this issue does not take much to close out :)
This has been added to the spec! https://indieauth.spec.indieweb.org/#authorization-request
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 correctme
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: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.