aka "mirage" aka "eyre oauth"
With Eyre now supporting both local identity authentication, and fake
guest identities, the logical next step is to support authentication
with real non-local identities. Here, we implement that, building on top
of the groundwork laid by #6561.
The primary change is adding a %real case to Eyre's $identity type, and
implementing an http<->ames<->ames handshaking protocol into Eyre for
negotiating approval of login attempts made by unauthenticated HTTP
clients.
The authentication flow, where a "visitor" logs into a "~host" as their
own "~client" identity can be described in brief as follows:
1) Visitor makes an HTTP request saying they are ~client.
2) ~host tells ~client, over Ames, about its own public-facing hostname.
3) ~client responds with its own public-facing hostname.
4) ~host forwards the visitor to ~client's eauth page.
5) Visitor, there already logged in as ~client, approves the login
attempt.
6) ~client shares a secret with ~host over Ames, and forwards the
visitor to ~host's eauth page, including the secret in the request.
7) ~host sees that the secrets received over Ames and HTTP match, and
gives the visitor a new session token, identifying them as ~client.
The negotiating of hostnames/URLs via Ames is crucial to keeping this
handshake sequence secure.
Discovering a ship's public-facing hostname happens when successful
local logins are made by reading out the Host header from the request.
Users may hard-code a value to override this.
Each eauth login attempt comes with a unique nonce. Both the host and
client track the lifetime of these. The corresponding Ames flow (which
goes from ~host -> ~client) is corked when the login attempt gets
aborted, or its associated session expires.
The logout functionality has been updated to let clients ask to be
logged out of sessions on other ships.
%name now returns the identity of the session associated with the
request. %host will always return the @p of the ship *handling* the
request.
The latter becomes especially important for guest sessions, who can only
interact with agents on the local ship, but will still need to specify
who that ship is.
aka "the open eyre" aka "universal basic identity"
Urbit already supports presence on the clearnet, but fails to expose any
of its interactive affordances to unauthenticated users. Here, we
improve this situation by granting "guest identity" @ps to every
unauthenticated HTTP request, and extending the channels functionality
to them.
Sessions no longer represent only the local identity. Instead, each
session has either the local identity, or a fake guest identity
associated with it.
Every request that does not provide a session key/cookie gets assigned
a fresh one with a guest identity on the spot. As a result, every
single request has an identity associated with it.
The identity of a request gets propagated into userspace, if the request
ends up there.
For normal HTTP requests, this means the src.bowl gets set to that
identity for both the watch and poke of the request. For backwards
compatibility, the authenticated flag on the request noun gets set at
normal: only true if the request came from the local identity.
For channel requests, this means the src.bowl gets set to that identity
for any pokes and watches it sends, and it can only send those to agents
running on the local ship.
The scry endpoint remains unchanged in its behavior: only available to
the local identity.
Notable implementation detail changes in this diff include:
- Factored all gall interactions out into +deal-as.
- Sessions no longer represent exclusively the local identity. This
matters a lot to +give-session-tokens, %code-changed, and logout
handling.
- Session management got factored out into explicit +start-session and
+close-session arms.
if all=& in |yawn, it will delete all listeners ducts,
without notifying them about it, which seems bad,
so we migh adress that separatedly.
Also, it might be cleaner to have a separate task instead of
a flag, to have two paths for "remove me" and "remove all",
this way there won't be an option for a listener to remove all
others, and that will have to be handled explicitly.
We were retrying failed kelvin upgrades as many times as we had apps
that needed to be suspended, because suspending an app triggers an
attempt to run the next kelvin upgrade. This suspends all those apps in
one batch move, and then tries the next kelvin upgrade only once at the
end.
Fixes#6407
Partially addresses #6285