The guest identities (#6561) and EAuth (#6598) features will both be
released as part of Zuse 412K, so their +load logic can be collapsed
into a single step.
Keeping a queue of nonces to match the outgoing %pleas we send lets us
recover the nonce for the %done we receive in response. This is
important in the nack case, where we may want to eagerly serve the HTTP
client an error page response, instead of waiting for the timeout timer
to fire.
We probably want something slightly fancier, like a banner or something,
that also shows up on the login page (and perhaps other "system" pages),
but for now this should suffice.
This was removed when the logic for re-subscriptions was taken from %ames
into the generator. Sorting allows us to keep the current (last) subscription
as the last in the list, and in this case we check if it received an ack for
a naxplanation—see https://github.com/urbit/urbit/issues/6065#issuecomment-1322011137—
in which case it's safe to be corked.
Instead of doing formal network traffic on the host-side whenever a
login attempt gets initiated, we now do it no earlier than when we're on
the client-side. This has the important property that network traffic
can only be initiated by authenticated HTTP requests. The previous
implementation, where hosts sent pleas when an unauthenticated HTTP
client said then wanted to log in, was vulnerable to abuse.
So now, formally, the eauth flow starts at the client's confirmation
screen. There is an optional step preceding this, where an attempt is
started on the host (and data is still stored for this), but to get the
redirect target, the host uses remote scry to get the eauth URL out of
the client ship.
Hosts now also give attempt-specific return URLs, useful in case they
are accessible (or even serving different content) from different
hostnames.
+on-kroc was cluttered with ad-hoc logic to indentify stale flows from
failed resubscriptions that were not properly %corked. Here we move
that logic to a generator that, if not in dry mode, will call %ames with a
(list [ship bone]) to %cork them.
Another option would be to move the logic in the generator to a state
update in ames, which will trigger possibly thousands of %ames messages
to be sent, on every ship that runs the state migration—these flows are
not causing a problem that neds to be addressed, and only take extra
space.
If we decide that this needs to be run by everyone, one solution could be
to set up a timer (maybe taking advantage of the fact that ships don't get
the OTA a the same time) that will eventually poke %hood with a
%helm-ames-kroc task.
(1) In `+thread-fail`, `+thread-clean` is called before `+thread-http-fail` and `+cancel-scry` meaning neither of the latter two actually. `serving` has been replaced with `(~(del by serving.state) tid)` in the state and therefore we cannot `(~(get by serving.state) tid)` in `+thread-http-fail` or `+cancel-scry`.
(2) In `+thread-http-fail` return an informative tang as json instead of an empty 500 http response when the failure results from an internal crash.
Description of the issue can be found here: https://github.com/urbit/urbit/issues/6625
The fix involved changing the instance of `+roll` to a `+spin` which increments a counter each time a column contains null tapes. Also, `+row`'s trap stops at the point when the null count equals the number of columns, indicating there's nothing left to render.
We weren't handling these at all. Now we make them enter the same
codepath that %done nacks go into: deleting the attempt and maybe
telling the user if we can.
Note that Eyre will not receive %lost for %boons it crashes on until
Previously, if we noticed %boon handling had caused a crash, we would
transform any existing %boons into %losts, but still emit a new %boon
for the message we ostensibly crashed on.
Now, we make sure to just directly send a %lost if sending the %boon
caused a crash. We drop the existing-moves transformation entirely,
assuming it to vestigial.
This will make it easier to access for tests. The change to its
interface is also nice: in pretty much all scenarios in which we call
it, we already know whether we have redirect deets available to us, so
just provide those as arguments instead of having the function
re-derive.
Brief prompt describing the login attempt's target, properly styled
buttons.
Pulls the CSS code for login pages out into its own arm for cleaner
sharing.
We had naively changed the status code to a 403 "forbidden" response,
which is technically correct, but the "Location" header isn't respected
for that status code, leaving the user with a blank page instead of a
login prompt.
We improve the styling on the login mode switching "tabs", ensure
elements shared between the two modes are visually aligned, do loose
input validation on the name field, and simply don't render the eauth
option at all if the local ship does not yet have an +eauth-url.
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.
Concatenating before we truncate, instead of truncating the entropy by
itself, is slightly simpler.
Because this slightly changes the naming algorithm, we must update the
eyre tests to match.
+read-at-tako was checking for the zero tako, but had the conditional
inverted. Here, we correct the conditional, and fold the
+may-read check into the whole.
This condition got incorrectly inverted during 0fee4ce. Of course, the
logic here is still subtly incorrect: if a session gets deleted before
the timer fires, then we set a second one. Unfortunately, we are now
here to fix the bug right now.
|pump and |sink call into each other in three places
related to nacks and naxplanations (sending a nack,
notifying the |pump of a naxplanation, or dropping a
nack from the |sink). This intra calls are making implicit
updates to more parts of the state than the core should
manage. To avoid that we emit a move to %arvo, encoded
as an %ames plea, to handle that in the next event.
Include and render identities associated with requests, channels, and
login sessions. Provide the ability to kick identities and their
sessions, logging them out.
It is no longer guaranteed that the src.bowl for incoming HTTP-related
events is equal to our.bowl. Instead, it will reflect the identity
associated with the request, our or otherwise.
When serving publicly-accessible endpoints, the assertion never made
much sense, but with recent changes actively prevents guests from
accessing the endpoints. Here, we correct all such cases.
%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.
Now that sessions with non-local identities can exist, the host/local
identity should be empowered to forcefully log off any session it hosts.
Additionally, we augment the logout logic with redirect functionality:
it now respects the "redirect" query parameter in the same way the login
page does. Still defaults to redirecting to the login page.
We previously had no mechanism for giving error responses, if a client
submitted an invalid request into a channel. Guest access makes this
important, because guests cannot interact with remote ships. Attempting
to do so will cause a gall crash.
Here, we add error handling logic to channel request processing. We
catch the invalid cases described above and invalidate the entire batch
of channel requests if they occur. We make sure to drop the moves and
revert the state we changed, and give a 400 to the client that
informally describes the problem(s).
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.
Previously, if an incoming request caused a crash, we would just drop it
on the floor. We should at least have the decency to serve the client a
quick 500 and let them get on with their day.
We make sure not to touch state here. The connection is guaranteed-fresh
because of the task's semantics, and we're handling it in-line in one go.
Notably we only give a simple "crud!" for the body, instead of the full
error trace. We don't know whether the request is authenticated or not
(and who knows if checking was the cause of the crash!), and the crud
might leak sensitive details about the ship it occurred on. For the
owner, the trace still gets printed into the terminal.
=case was added as an argument to read-s to support %cs subs
it was accidentally removed as an argument during a merge,
breaking %cs subs by causing it to always crash in a
comparison between case and [%da now], because case resolved
to the mold rather than a value. this commit restores
intended functionality.