IndieAuth Ticket Auth
Ticket Auth is an extension to IndieAuth that enables a publisher to send authorization, known as a ticket, that can be redeemed for an access token. This allows both solicited and unsolicited invitations to fetch restricted resources.
- Status
- This specification is still in early development.
- Latest Published Version
- https://indieweb.org/IndieAuth_Ticket_Auth
- Participate
- chat
- discussion on GitHub
- Authors
- Contributors: revision history
Introduction
Ticket Auth is an extension to IndieAuth that enables the granting of access to a protected resource (a.k.a. resource server, application, API, etc.). Ticket Auth differs from other proposed extensions in that the extension does not require the client accessing the protected resource to expressly request access, but is instead offered it.
Summary
- Alice has choosen to share private things with Bob.
- Bob advertises a ticket endpoint on his website.
- Alice sends a ticket to Bob's ticket endpoint, which he can redeem for a token to access Alice's private things.
- Bob's ticket endpoint receives a ticket, which he exchanges for a token at Alice's token endpoint.
- Bob can now access Alice's private things.
Use-cases
Use-cases that inspired the development of this protocol:
- be able to fetch private posts from someone's feed
- don't rely on a "follow request" in order to do so
- ...
Note: The most common use case thought of involves a private feed, but it does not inherently have to be a feed.
Brainstorming
In brainstorming some use cases, the following scenarios came up for possible use cases building on integrating multiple existing pieces.
- Alice has a feed containing both public and private posts. By default, fetching it returns only the public posts. Alice decides she wants Bob to get the entire feed. Her site sends a ticket to Bob's ticket endpoint, which redeems it for a token that can access the feed.
- How does Alice decide to send Bob a ticket? Not really important. It could be she monitors who is subscribed to her feed somehow, and wants to offer the full version. It could be Bob and Alice are friends and she invites him unsolicited.
- What does Bob do with the token? Assuming that Bob's ticket endpoint is coupled together with his Microsub endpoint, and he's already subscribed to her feed, it should automatically start fetching the feed with the token to get the enhanced version with the private posts. If Bob wasn't already following, it would still get the token, but it would send the feed to a moderation queue or a dedicated Microsub channel for Bob to decide how to handle it.
- If Alice has a single piece of restricted content she wants to share with Bob, it would work similarly to above, without the consideration of whether Bob was already privy to a different version of the content.
- In another scenario, Carol wants to share private posts, but not a specific private post. She can opt to create a form on her site that send tickets to anyone who authenticates to it using IndieAuth, and then subsequently decide internally what content each user represented by the token gets. This is effectively giving everyone a login into your site.
Protocol
Step 1: Decide to issue an access token
Alice has choosen to share private things with Bob.
This is in contrast to a mechanism of advertising the fact that there may be private/protected content at a URL by using something like a WWW-Authenticate header. By indicating there may be more to see at a URL, that itself is leaking information.
The decision to share could be triggered by Bob notifying Alice that he is following Alice, or it could be without any affirmative action on Bob's part. This isn't strictly necessary but may provide a better user experience for granting these tokens.
- TODO: Expand on methods of Bob notifying Alice could be a follow post, or some other protocol, but is not related to authentication or required.
Why not send the access token directly?
When you send a ticket, you are sending an unsolicited payload to the receiver. The ticket is not requested by the receiver, so you cannot guarantee they will be protecting it if they aren't expecting it. (It may be logged in intermediate proxy servers, written to log files on the server, etc.) If the receiver supports receiving tickets, then they will be explicitly requesting an access token by exchanging the ticket, so you can be more confident they will take measures to protect the access token. This way the access token is only delivered via an explicitly requested HTTPS connection to your server.
Additionally, given some IndieAuth servers don't support expiration of tokens(despite it being a recommendation), this could allow permanent access to protected data, which is not ideal.
Step 2: Issue an access token
Alice has decided to send an access token to Bob. (Either because Alice decided to out of the blue, or because Alice noticed Bob's follow notification.)
Create the IndieAuth ticket
Alice generates a short-lived ticket which will last approximately a minute at most. The reason for short-lived is that you are sending something unsolicited to someone and it is safer to send something short-lived and single use, and require action to retrieve the actual token.
- Feed URL = https://alice.example.com/private/
- Ticket = 32985723984723985792834
- Deliver to ticket endpoint of the subject: https://bob.example.org/
Alice discovers the ticket endpoint of the subject. See #Discovery
Alice makes a POST request to the ticket endpoint. This is basically saying "if you wanted to fetch this as this person and see private stuff, here's a ticket you can use to get an access token".
ticket
- a random string that can be redeemed for an access tokenresource
- the access token will work at this URLsubject
- the access token should be used when acting on behalf of this URL
POST https://auth.example.org/ticket Content-Type: application/x-www-form-urlencoded ticket=32985723984723985792834 &resource=https://alice.example.com/private/ &subject=https://bob.example.org
When a ticket is sent, if there is no problem with the request, the ticket endpoint MUST return an HTTP 200 OK code or HTTP 202 Accepted if it is going to asynchronously make the token request.
Redeem the ticket for an access token
Bob's ticket endpoint redeems the ticket for an access token
The ticket endpoint makes a GET or HEAD request to discover the token endpoint of the resource, https://alice.example.com/private/
, searching for an HTTP Link element or a HTML <link> element with the appropriate rel value for the IndieAuth metadata endpoint, indieauth-metadata
or fallback to the older token_endpoint
.
The ticket endpoint makes a POST request to the token endpoint to exchange the ticket for an access token. The POST request contains the following parameters:
grant_type=ticket
ticket
- The ticket to be redeemed
The IndieAuth spec does not currently address the issuance of resource-limited tokens. The discussion for that is at https://github.com/indieweb/indieauth/issues/82. Regardless of this adoption, if a token endpoint wishes to support the ticket grant type, then it MUST support the issuance of resource-limited tokens.
Should this also include client_id? https://github.com/indieweb/indieauth/issues/85
Example
POST https://alice.example.com/token Content-type: application/x-www-form-urlencoded Accept: application/json grant_type=ticket &ticket=32985723984723985792834
Response is an access token as specified in the IndieAuth Spec.
Step 3: Use the access token
Now Bob's authorization endpoint has the remote access token that could be used to fetch Alice's feed.
How to get it to the reader or the webmention endpoint verifier is up to those implementations, it may be internal, or may use another spec to coordinate.
Notes
The current implementation expects that all pages that could be deemed as a resource
would have either the newer rel=indieauth-metadata
or the older but still in use rel=token_endpoint
Advantages:
- The thing that gets tokens only needs one new public endpoint, the endpoint that receives tickets
- Issuing tickets is completely voluntary, and does not have to be prompted by an action by the eventual receiver of the token
Discovery
A ticket endpoint can be discovered by fetching the URL of the subject and looking for the indieauth-metadata rel values as described in the IndieAuth Discovery.
The client fetches the metadata document and finds the ticket_endpoint property in the JSON body.
Some ticket endpoints may be advertised by a separate link header for those implemented prior to the addition of the indieauth-metadata endpoint, however, any IndieAuth implementation that supports a metadata endpoint SHOULD NOT advertise the ticket endpoint separately.
Token Issuing
How a website decides to issue tokens does not need to be defined by this spec.
Aaron Parecki created an interface to be able to send tickets to arbitrary URLs, and shows the token info after the ticket has been redeemed.
Martijn van der Ven created a public interface on the token_endpoint where arbitrary homepage URLs can be given for anyone to receive a token.
Proposed mechanism for requesting a ticket
https://github.com/indieweb/indieauth/issues/87
In order to request a ticket, a POST request would be made, indicating the subject is interested in accessing private content. How a website decides whether to provide a ticket in this case, and what resources it would unlock does not need to be defined by this spec.
In responding to a request, the endpoint would take the same approach as the webmention spec. Returning a 201, 202, or 400/500 for user and server errors respectively. The endpoint need not reveal whether it has chosen to issue a ticket, as the ticket endpoint would receive or not receive one as appropriate.
Open questions
- Would a token grant access to anything more specific than the provided resource, or would it be only for that specific resource? (e.g. should a token for
https://example.com/alice/
also work onhttps://example.com/alice/feed
)- Moved discussion to https://github.com/indieweb/indieauth/issues/83
- Any provision for providing an expiry time as part of the granted ticket?
- Moved discussion to https://github.com/indieweb/indieauth/issues/84
- Should it be assumed that the ticket endpoint will automatically redeem all tokens? It's technically an implementation detail, not a requirement of the spec, but witht he amount of time the ticket is valid, this seems a good move. Assuming the token endpoint supports revocation.
- Where does the ticket endpoint go looking for the token endpoint? Currently the only thing it is sent that it can act on is the
resource
value. If that is also supposed to function as sort of a root URL of resources it gives access to, is it correct to assume the token endpoint can be discovered at that exact URL? (See first open Q.)Martijn van der Ven ended up setting the resource as their root domain, which made it impossible to go get a token with the token endpoint only discoverable from the /martijn/ path. This was because of the assumption that
resource
was likerealm
in that it could be anything, but that stopped the ticket endpointDavid Shanske was writing from ever redeeming. Short discussion in chat.
- Something that can be fixed by providing a specific
iss
/issuer
value along with theresource
in the POST to the ticket endpoint?Jamie Tanna was wondering about the
iss
matching the issuer of the token (i.e. the IndieAuth server's URL) but agrees withMartijn van der Ven's point about it being in the initial POST to the ticket endpoint, and matching the identity URL of the user who is providing access
Jamie Tanna's POC requires the
issuer
parameter
- It was pointed out that by putiting the token endpoint header on a resource URL, you may be indicating there is something there. For example, you choose to have your private pages return 404. Then how does the ticket endpoint discover the token endpoint? As above, it should return a
me
/iss
/issuer
parameter- This indicates a few different use cases for granting tickets; it seems that some people want to generate tickets for a specific resource (e.g. a private post) sent securely to specific recipients, whereas other people want to generate tickets for being able to subscribe to a feed or otherwise retrieve data in a logged-in state. The original TicketAuth draft was to support the latter; do we also need to support the former? If
resource
points to specific private blog entries, that implies that it's up to the publisher to specifically send push notifications about all private content to all recipients, which has a lot of far-reaching implications.
- This indicates a few different use cases for granting tickets; it seems that some people want to generate tickets for a specific resource (e.g. a private post) sent securely to specific recipients, whereas other people want to generate tickets for being able to subscribe to a feed or otherwise retrieve data in a logged-in state. The original TicketAuth draft was to support the latter; do we also need to support the former? If
- Should we make it clear that multiple
resource
s could be granted from a single ticket? Following RFC8707 would make it possible to do soJamie Tanna's receiver supports
resource
andresource[]
- Would it make sense to have
resource
for specific items,realm
for blanket grants for an entire website/URL prefix/etc., andissuer
for where to look for the token endpoint as an override if the endpoint can't be determined fromresource
/realm
?
- How to handle non-canonical/redirecting profile URLs? Since the ticket itself never goes through the full IndieAuth flow, the granter can't get the canonical identity URL from the
authorization_endpoint
.- As of this change in Publ, if Publ gets a ticket request for a page with a
rel="canonical"
, it forwards the request to the canonical page. No trust is assumed based on the original requesting URL, and the ticket only goes to the canonical URL's declared endpoint.
- As of this change in Publ, if Publ gets a ticket request for a page with a
IndieWeb Examples
Martijn van der Ven
As of 2021-07-04 (at 21:22 UTC), Martijn van der Ven has a token endpoint with the ability for anyone to request a ticket. It is accessible through https://vanderven.se/martijn/token/.
As of 2021-07-10 (Create Day), the homepage at https://vanderven.se/martijn/ will show a welcome message and an alternative photo when a valid Bearer token is send in the Authorization
header.
Please treat the whole thing as experimental, and feel free to test your own endpoints against it!
Tokens created through there are meant for the entire domain (the resource
is https://vanderven.se/
) and have no scope
set. They are purely to identify someone as their URL. Because of the experimental phase of this spec, tokens are kept to short life times (3 days).
- Special: while requesting tickets is declared out-of-scope for this extension, the way it is implemented here follows the pattern from IndieAuthβs Token Revocation. A
POST
withaction=ticket
triggers the sending of a ticket.
As of 2022-09-04 (IWC Berlin), the source is available as a GitHub Gist.
David Shanske
As of 2021-07-04, David Shanske has a proof of concept ticket endpoint on his test site that can receive tickets and redeem the associated token and store it. As of 2021-07-10, it has a management interface but no redemption methodology.
Jamie Tanna
As of 2021-07-07, Jamie Tanna has created a POC ticket endpoint which is Open Source and deployed to Heroku for anyone else to use it.
This implementation requires an issuer
to reference the profile URL of the user who's token_endpoint
is to be used for the ticket exchange.
This returns the token exchange body to the caller, and logs some information, but not the granted access token (currently), nor does it store it.
It does not yet utilize it for anything or have an interface to do so.
Jamie is currently using this on his testing identity.
fluffy
As of 2021-07-08, fluffy has added a token granting endpoint to Publ, which can be tested at a publ test instance. It supports the following TicketAuth login flows:
- Manually request a ticket by going to
https://dev.beesbuzz.biz/_tokens?me=YOUR_URL_HERE
- Send a POST request to
https://dev.beesbuzz.biz/_tokens
with parameterssubject=YOUR_URL_HERE
and, optionally,scope=REQUESTED_SCOPES
(per the proposed mechanism of a ticket request, as of 2022-03-20) - Refresh tokens (per standard OAuth2)
- Log in to the test site and a ticket will be automatically sent to you
The resulting token can be tested on the human-readable profile page or by sending a token verification request to https://dev.beesbuzz.biz/_tokens
.
This is also available on fluffy's personal site; see the user profile page for things to experiment with (including manually-generated tokens).
aaronpk
As of 2021-07-10, Aaron Parecki has implemented both sending and receiving tokens on a standalone test website.