mirror of
https://github.com/juanfont/headscale.git
synced 2026-04-24 09:38:45 +02:00
oidc: add confirmation page for node registration
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.
This commit is contained in:
@@ -230,6 +230,22 @@ type SSHCheckBinding struct {
|
||||
DstNodeID NodeID
|
||||
}
|
||||
|
||||
// PendingRegistrationConfirmation captures the server-side state needed
|
||||
// to finalise a node registration after the user has confirmed it on
|
||||
// the OIDC interstitial. The OIDC callback resolves the user identity
|
||||
// and node expiry, stores them on the cached AuthRequest, and renders
|
||||
// a confirmation page; only when the user POSTs the confirmation form
|
||||
// does the actual node registration run.
|
||||
//
|
||||
// CSRF is a one-shot per-session token that the OIDC callback set
|
||||
// both as a cookie and as a hidden form field. The confirm POST
|
||||
// handler refuses to proceed unless the cookie and form values match.
|
||||
type PendingRegistrationConfirmation struct {
|
||||
UserID uint
|
||||
NodeExpiry *time.Time
|
||||
CSRF string
|
||||
}
|
||||
|
||||
// AuthRequest represents a pending authentication request from a user or a
|
||||
// node. It carries the minimum data needed to either complete a node
|
||||
// registration (regData populated) or an SSH check-mode auth (sshBinding
|
||||
@@ -257,6 +273,13 @@ type AuthRequest struct {
|
||||
// safely.
|
||||
sshBinding *SSHCheckBinding
|
||||
|
||||
// pendingConfirmation is populated by the OIDC callback for the
|
||||
// node-registration flow once the user identity has been resolved
|
||||
// but before the user has explicitly confirmed the registration on
|
||||
// the interstitial. The /register/confirm POST handler reads it to
|
||||
// finalise the registration without re-running the OIDC flow.
|
||||
pendingConfirmation *PendingRegistrationConfirmation
|
||||
|
||||
finished chan AuthVerdict
|
||||
closed *atomic.Bool
|
||||
}
|
||||
@@ -331,6 +354,22 @@ func (rn *AuthRequest) IsSSHCheck() bool {
|
||||
return rn.sshBinding != nil
|
||||
}
|
||||
|
||||
// SetPendingConfirmation marks this AuthRequest as having an
|
||||
// OIDC-resolved user that is waiting to confirm the registration on
|
||||
// the interstitial. The OIDC callback should call this and then render
|
||||
// the confirmation page; the /register/confirm POST handler reads the
|
||||
// stored UserID/NodeExpiry to finish the registration.
|
||||
func (rn *AuthRequest) SetPendingConfirmation(p *PendingRegistrationConfirmation) {
|
||||
rn.pendingConfirmation = p
|
||||
}
|
||||
|
||||
// PendingConfirmation returns the pending OIDC-resolved registration
|
||||
// state captured by SetPendingConfirmation, or nil if no OIDC callback
|
||||
// has yet resolved an identity for this AuthRequest.
|
||||
func (rn *AuthRequest) PendingConfirmation() *PendingRegistrationConfirmation {
|
||||
return rn.pendingConfirmation
|
||||
}
|
||||
|
||||
func (rn *AuthRequest) FinishAuth(verdict AuthVerdict) {
|
||||
if rn.closed.Swap(true) {
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user