mirror of
https://github.com/juanfont/headscale
synced 2026-04-25 17:15:33 +02:00
Render an interstitial showing device hostname, OS, and machine-key
fingerprint before finalising OIDC registration. The user must POST
to /register/confirm/{auth_id} with a CSRF double-submit cookie.
Removes the TODO at oidc.go:201.
102 lines
3.2 KiB
Go
102 lines
3.2 KiB
Go
package templates
|
|
|
|
import (
|
|
"github.com/chasefleming/elem-go"
|
|
"github.com/chasefleming/elem-go/attrs"
|
|
)
|
|
|
|
// RegisterConfirmInfo carries the human-readable information shown on
|
|
// the registration confirmation interstitial that an OIDC-authenticated
|
|
// user must explicitly accept before a pending node is registered to
|
|
// their identity. The fields here intentionally include enough device
|
|
// detail (hostname, OS, machine-key fingerprint) for the user to
|
|
// recognise whether the device they are about to claim is in fact
|
|
// theirs.
|
|
type RegisterConfirmInfo struct {
|
|
// FormAction is the absolute or relative URL the confirm form
|
|
// POSTs to. Typically /register/confirm/{auth_id}.
|
|
FormAction string
|
|
|
|
// CSRFTokenName is the name of the hidden form field carrying the
|
|
// CSRF token. The corresponding cookie shares this name.
|
|
CSRFTokenName string
|
|
|
|
// CSRFToken is the per-session token that must match the value of
|
|
// the cookie set by the OIDC callback before the POST is honoured.
|
|
CSRFToken string
|
|
|
|
// User is the OIDC-authenticated identity the device will be
|
|
// registered to if the user confirms.
|
|
User string
|
|
|
|
// Hostname is the hostname the registering tailscaled instance
|
|
// reported in its RegisterRequest.
|
|
Hostname string
|
|
|
|
// OS is the operating system the registering tailscaled reported.
|
|
// May be the empty string when the client did not send Hostinfo.
|
|
OS string
|
|
|
|
// MachineKey is the short fingerprint of the registering machine
|
|
// key. The full key is intentionally not shown.
|
|
MachineKey string
|
|
}
|
|
|
|
// RegisterConfirm renders an interstitial page that asks the
|
|
// OIDC-authenticated user to explicitly confirm that they want to
|
|
// register the named device under their account. Without this
|
|
// confirmation step a single GET to /register/{auth_id} could
|
|
// silently complete a phishing-style registration when the victim's
|
|
// IdP allows silent SSO.
|
|
func RegisterConfirm(info RegisterConfirmInfo) *elem.Element {
|
|
deviceList := elem.Ul(nil,
|
|
elem.Li(nil, elem.Strong(nil, elem.Text("Hostname: ")), elem.Text(info.Hostname)),
|
|
elem.Li(nil, elem.Strong(nil, elem.Text("OS: ")), elem.Text(displayOrUnknown(info.OS))),
|
|
elem.Li(nil, elem.Strong(nil, elem.Text("Machine key: ")), Code(elem.Text(info.MachineKey))),
|
|
elem.Li(nil, elem.Strong(nil, elem.Text("Will be registered to: ")), elem.Text(info.User)),
|
|
)
|
|
|
|
form := elem.Form(
|
|
attrs.Props{
|
|
attrs.Method: "POST",
|
|
attrs.Action: info.FormAction,
|
|
},
|
|
elem.Input(attrs.Props{
|
|
attrs.Type: "hidden",
|
|
attrs.Name: info.CSRFTokenName,
|
|
attrs.Value: info.CSRFToken,
|
|
}),
|
|
elem.Button(
|
|
attrs.Props{attrs.Type: "submit"},
|
|
elem.Text("Confirm registration"),
|
|
),
|
|
)
|
|
|
|
return HtmlStructure(
|
|
elem.Title(nil, elem.Text("Headscale - Confirm node registration")),
|
|
mdTypesetBody(
|
|
headscaleLogo(),
|
|
H2(elem.Text("Confirm node registration")),
|
|
P(elem.Text(
|
|
"A device is asking to be added to your tailnet. "+
|
|
"Please review the details below and confirm that this device is yours.",
|
|
)),
|
|
deviceList,
|
|
form,
|
|
P(elem.Text(
|
|
"If you do not recognise this device, close this window. "+
|
|
"The registration request will expire automatically.",
|
|
)),
|
|
pageFooter(),
|
|
),
|
|
)
|
|
}
|
|
|
|
func displayOrUnknown(s string) string {
|
|
if s == "" {
|
|
return "(unknown)"
|
|
}
|
|
|
|
return s
|
|
}
|