Login Approvals

Most connections go straight through: the key resolves to an identity, policies grant it, the session opens.

Two situations pause a login and ask for a confirmation in the console instead.

First use of a key

When a client offers a key the namespace has never seen, ShellHub does not refuse it. It holds the login open and asks a member to approve.

Approving binds that key to the approving account. From then on the key is an identity, and every later connection with it is recognized immediately — no approval, no wrapper, a stock OpenSSH client.

This is the approve-once model. The alternative — approving every login — is what require_reauth is for, and it is off by default.

The server asks in the terminal, as a keyboard-interactive prompt, which is how it reaches a stock client with nothing installed, including one run with -q:

  ShellHub doesn't know this SSH key yet.

  Open the link to add it to your identities:

    https://cloud.shellhub.io/ssh-identities/new/2TV899JA

  Security code:  2TV8 99JA
  Key:            SHA256:2Ur4mPvXqE9V78JBIR26G4zpPW0IWs9ci690kNbQ

  The console gives you a confirmation code once you add it.

Confirmation code:

Open the link and approve. The console then shows a confirmation code; type it at the prompt and the handshake finishes into an ordinary session.

The code is what proves you reached the console, which following a link does not. It is also why a client with nobody in front of it (a CI job, a script, anything using an SSH library) is refused straight away instead of hanging: it has no way to answer, and the server does not wait for an answer that is not coming.

The identity is created for whoever approves, not for whoever was connecting. Approve a stranger’s key and you have handed them a key that connects as you, with session records in your name. Approve only a login you started yourself; if somebody sends you their link, tell them to open it themselves.

Check the security code and the fingerprint before approving. The code is the tail of the link, printed in pairs so it can be read aloud; the fingerprint is the key that was offered. Both appear again in the browser, and they are what tells you the page you are approving belongs to the connection in front of you rather than to somebody else’s attempt at the same moment.

The approval is valid for 90 seconds. Miss it and the connection fails; connect again and a fresh link appears. What waits is the prompt in your own terminal, so a client that cannot answer one is refused rather than parked, and no connection to the device is opened until the login is through. The connection you are logging in from is held until you answer, for at most three minutes.

Rejecting refuses the connection and binds nothing.

You can skip this step by adding the key ahead of time on the SSH Identities page, the usual move for a server key, where nobody is watching for a prompt. See SSH Identities. A CI runner connects as an API key instead; see SSH Access.

Re-authentication required by a policy

A policy can carry require_reauth. When it grants a connection, the identity must confirm out of band before the session opens, even though the key is already known.

Use it on the paths that deserve a deliberate step: production, a privileged login, a device nobody should reach absent-mindedly.

The freshness window

reauth_period decides how often the confirmation is actually demanded. It is a window in seconds, and the freshness belongs to the identity, not to a connection.

That distinction is the whole point:

  • Window set — one confirmation covers every login with that key until the window lapses. Confirm once in the morning, work all day.
  • Window unset, or 0 — confirm on every single login. The only genuinely per-connection setting.

An identity that has never re-authenticated is always challenged.

The window is measured against the identity’s last re-authenticated timestamp, which moves only on a successful re-authentication — not on ordinary use.

When several policies match

The strictest requirement wins: if any matching allow requires re-authentication, the connection requires it, at the shortest window among them.

This is deliberate. Without it, a broad “all members, all devices” grant with no re-auth would silently shadow the narrow policy you wrote specifically to protect production.

The two side by side

First use of a key Re-authentication
Why it appeared The key is not an identity yet A policy requires fresh confirmation
What confirming does Binds the key to the approver Refreshes the identity’s freshness window
Creates anything Yes, a new identity No
Happens again No — the key is now an identity When the window lapses

An approval is pending, then confirmed or rejected. There is no stored expired state — a pending approval past its deadline simply reads as unknown, and is pruned later.

Where to go next

esc