diff --git a/api/client/webclient/webclient.go b/api/client/webclient/webclient.go index 8fcb0d01a93eb..daeaed716b3b2 100644 --- a/api/client/webclient/webclient.go +++ b/api/client/webclient/webclient.go @@ -396,8 +396,11 @@ type AuthenticationSettings struct { PrivateKeyPolicy keys.PrivateKeyPolicy `json:"private_key_policy"` // DeviceTrustDisabled provides a clue to Teleport clients on whether to avoid // device authentication. + // Deprecated: Use DeviceTrust.Disabled instead. + // DELETE IN 16.0, replaced by the DeviceTrust field (codingllama). DeviceTrustDisabled bool `json:"device_trust_disabled,omitempty"` - + // DeviceTrust holds cluster-wide device trust settings. + DeviceTrust DeviceTrustSettings `json:"device_trust,omitempty"` // HasMessageOfTheDay is a flag indicating that the cluster has MOTD // banner text that must be retrieved, displayed and acknowledged by // the user. @@ -448,6 +451,13 @@ type GithubSettings struct { Display string `json:"display"` } +// DeviceTrustSettings holds cluster-wide device trust settings that are liable +// to change client behavior. +type DeviceTrustSettings struct { + Disabled bool `json:"disabled,omitempty"` + AutoEnroll bool `json:"auto_enroll,omitempty"` +} + func (ps *ProxySettings) TunnelAddr() (string, error) { // If TELEPORT_TUNNEL_PUBLIC_ADDR is set, nothing else has to be done, return it. if tunnelAddr := os.Getenv(defaults.TunnelPublicAddrEnvar); tunnelAddr != "" { diff --git a/lib/client/api.go b/lib/client/api.go index fc09c5e73abbf..71d55c0e2ddab 100644 --- a/lib/client/api.go +++ b/lib/client/api.go @@ -3305,7 +3305,7 @@ func (tc *TeleportClient) AttemptDeviceLogin(ctx context.Context, key *Key) erro return trace.Wrap(err) } - if !tc.dtAttemptLoginIgnorePing && pingResp.Auth.DeviceTrustDisabled { + if !tc.dtAttemptLoginIgnorePing && (pingResp.Auth.DeviceTrustDisabled || pingResp.Auth.DeviceTrust.Disabled) { log.Debug("Device Trust: skipping device authentication, device trust disabled") return nil } diff --git a/lib/client/api_login_test.go b/lib/client/api_login_test.go index fedc5ddb88444..2f9d71118e14d 100644 --- a/lib/client/api_login_test.go +++ b/lib/client/api_login_test.go @@ -516,6 +516,7 @@ func TestTeleportClient_DeviceLogin(t *testing.T) { resp, err := teleportClient.Ping(ctx) require.NoError(t, err, "Ping failed") require.True(t, resp.Auth.DeviceTrustDisabled, "Expected device trust to be disabled for Teleport OSS") + require.True(t, resp.Auth.DeviceTrust.Disabled, "Expected device trust to be disabled for Teleport OSS") // Test! // AttemptDeviceLogin should obey Ping and not attempt the ceremony. diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index a262e78a88ee9..9a8b31d4bd502 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -869,6 +869,7 @@ func localSettings(cap types.AuthPreference) (webclient.AuthenticationSettings, Local: &webclient.LocalSettings{}, PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), DeviceTrustDisabled: deviceTrustDisabled(cap), + DeviceTrust: deviceTrustSettings(cap), } // Only copy the connector name if it's truly local and not a local fallback. @@ -909,6 +910,7 @@ func oidcSettings(connector types.OIDCConnector, cap types.AuthPreference) webcl PreferredLocalMFA: cap.GetPreferredLocalMFA(), PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), DeviceTrustDisabled: deviceTrustDisabled(cap), + DeviceTrust: deviceTrustSettings(cap), } } @@ -924,6 +926,7 @@ func samlSettings(connector types.SAMLConnector, cap types.AuthPreference) webcl PreferredLocalMFA: cap.GetPreferredLocalMFA(), PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), DeviceTrustDisabled: deviceTrustDisabled(cap), + DeviceTrust: deviceTrustSettings(cap), } } @@ -939,6 +942,15 @@ func githubSettings(connector types.GithubConnector, cap types.AuthPreference) w PreferredLocalMFA: cap.GetPreferredLocalMFA(), PrivateKeyPolicy: cap.GetPrivateKeyPolicy(), DeviceTrustDisabled: deviceTrustDisabled(cap), + DeviceTrust: deviceTrustSettings(cap), + } +} + +func deviceTrustSettings(cap types.AuthPreference) webclient.DeviceTrustSettings { + dt := cap.GetDeviceTrust() + return webclient.DeviceTrustSettings{ + Disabled: deviceTrustDisabled(cap), + AutoEnroll: dt != nil && dt.AutoEnroll, } } diff --git a/lib/web/apiserver_ping_test.go b/lib/web/apiserver_ping_test.go index c323d087a434b..330aebcacd954 100644 --- a/lib/web/apiserver_ping_test.go +++ b/lib/web/apiserver_ping_test.go @@ -117,8 +117,9 @@ func TestPing(t *testing.T) { RPID: "example.com", }, }, - assertResp: func(cap types.AuthPreference, resp *webclient.PingResponse) { + assertResp: func(_ types.AuthPreference, resp *webclient.PingResponse) { assert.True(t, resp.Auth.DeviceTrustDisabled, "Auth.DeviceTrustDisabled") + assert.True(t, resp.Auth.DeviceTrust.Disabled, "Auth.DeviceTrust.Disabled") }, }, { @@ -134,8 +135,29 @@ func TestPing(t *testing.T) { Mode: constants.DeviceTrustModeOptional, }, }, - assertResp: func(cap types.AuthPreference, resp *webclient.PingResponse) { + assertResp: func(_ types.AuthPreference, resp *webclient.PingResponse) { + assert.False(t, resp.Auth.DeviceTrustDisabled, "Auth.DeviceTrustDisabled") + assert.False(t, resp.Auth.DeviceTrust.Disabled, "Auth.DeviceTrust.Disabled") + }, + }, + { + name: "OK device trust auto-enroll", + buildType: modules.BuildEnterprise, + spec: &types.AuthPreferenceSpecV2{ + Type: constants.Local, + SecondFactor: constants.SecondFactorOptional, + Webauthn: &types.Webauthn{ + RPID: "example.com", + }, + DeviceTrust: &types.DeviceTrust{ + Mode: constants.DeviceTrustModeOptional, + AutoEnroll: true, + }, + }, + assertResp: func(_ types.AuthPreference, resp *webclient.PingResponse) { assert.False(t, resp.Auth.DeviceTrustDisabled, "Auth.DeviceTrustDisabled") + assert.False(t, resp.Auth.DeviceTrust.Disabled, "Auth.DeviceTrust.Disabled") + assert.True(t, resp.Auth.DeviceTrust.AutoEnroll, "Auth.DeviceTrust.AutoEnroll") }, }, }