From a3be1b7cc5d4283c67b7a2a795be57095cb51df1 Mon Sep 17 00:00:00 2001 From: Caleb Doxsey Date: Mon, 20 Dec 2021 10:44:29 -0700 Subject: [PATCH] devices: switch "default" device type to two built-in default device types (#2835) --- authenticate/authenticate.go | 3 ++- authorize/check_response.go | 3 ++- pkg/webauthnutil/device_type.go | 37 ++++++++++++++++++++++------ pkg/webauthnutil/device_type_test.go | 10 ++++---- pkg/webauthnutil/options_test.go | 16 ++++++------ 5 files changed, 47 insertions(+), 22 deletions(-) diff --git a/authenticate/authenticate.go b/authenticate/authenticate.go index b29fb4709..bad54d1af 100644 --- a/authenticate/authenticate.go +++ b/authenticate/authenticate.go @@ -16,6 +16,7 @@ import ( "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/pkg/cryptutil" + "github.com/pomerium/pomerium/pkg/webauthnutil" ) // ValidateOptions checks that configuration are complete and valid. @@ -133,7 +134,7 @@ func (a *Authenticate) getWebAuthnURL(values url.Values) (*url.URL, error) { uri = uri.ResolveReference(&url.URL{ Path: "/.pomerium/webauthn", RawQuery: buildURLValues(values, url.Values{ - urlutil.QueryDeviceType: {"default"}, + urlutil.QueryDeviceType: {webauthnutil.DefaultDeviceType}, urlutil.QueryEnrollmentToken: nil, urlutil.QueryRedirectURI: {uri.ResolveReference(&url.URL{ Path: "/.pomerium/", diff --git a/authorize/check_response.go b/authorize/check_response.go index 01f7f5e7f..34d568873 100644 --- a/authorize/check_response.go +++ b/authorize/check_response.go @@ -24,6 +24,7 @@ import ( "github.com/pomerium/pomerium/internal/telemetry/requestid" "github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/pkg/policy/criteria" + "github.com/pomerium/pomerium/pkg/webauthnutil" ) func (a *Authorize) handleResultAllowed( @@ -212,7 +213,7 @@ func (a *Authorize) requireWebAuthnResponse( if deviceType, ok := result.Allow.AdditionalData["device_type"].(string); ok { q.Set(urlutil.QueryDeviceType, deviceType) } else { - q.Set(urlutil.QueryDeviceType, "default") + q.Set(urlutil.QueryDeviceType, webauthnutil.DefaultDeviceType) } q.Set(urlutil.QueryRedirectURI, checkRequestURL.String()) signinURL.RawQuery = q.Encode() diff --git a/pkg/webauthnutil/device_type.go b/pkg/webauthnutil/device_type.go index 4e18b0d7f..5c6ce9b46 100644 --- a/pkg/webauthnutil/device_type.go +++ b/pkg/webauthnutil/device_type.go @@ -6,15 +6,25 @@ import ( "github.com/pomerium/webauthn/cose" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" "github.com/pomerium/pomerium/pkg/grpc/databroker" "github.com/pomerium/pomerium/pkg/grpc/device" ) +// DefaultDeviceType is the default device type when none is specified. +const DefaultDeviceType = "any" + +var supportedPublicKeyCredentialParameters = []*device.WebAuthnOptions_PublicKeyCredentialParameters{ + {Type: device.WebAuthnOptions_PUBLIC_KEY, Alg: int64(cose.AlgorithmES256)}, + {Type: device.WebAuthnOptions_PUBLIC_KEY, Alg: int64(cose.AlgorithmRS256)}, + {Type: device.WebAuthnOptions_PUBLIC_KEY, Alg: int64(cose.AlgorithmRS1)}, +} + var predefinedDeviceTypes = map[string]*device.Type{ - "default": { - Id: "default", - Name: "default", + "any": { + Id: "any", + Name: "Any", Specifier: &device.Type_Webauthn{ Webauthn: &device.Type_WebAuthn{ Options: &device.WebAuthnOptions{ @@ -22,11 +32,24 @@ var predefinedDeviceTypes = map[string]*device.Type{ AuthenticatorSelection: &device.WebAuthnOptions_AuthenticatorSelectionCriteria{ UserVerification: device.WebAuthnOptions_USER_VERIFICATION_PREFERRED.Enum(), }, - PubKeyCredParams: []*device.WebAuthnOptions_PublicKeyCredentialParameters{ - {Type: device.WebAuthnOptions_PUBLIC_KEY, Alg: int64(cose.AlgorithmES256)}, - {Type: device.WebAuthnOptions_PUBLIC_KEY, Alg: int64(cose.AlgorithmRS256)}, - {Type: device.WebAuthnOptions_PUBLIC_KEY, Alg: int64(cose.AlgorithmRS1)}, + PubKeyCredParams: supportedPublicKeyCredentialParameters, + }, + }, + }, + }, + "enclave_only": { + Id: "enclave_only", + Name: "Secure Enclave Only", + Specifier: &device.Type_Webauthn{ + Webauthn: &device.Type_WebAuthn{ + Options: &device.WebAuthnOptions{ + Attestation: device.WebAuthnOptions_DIRECT.Enum(), + AuthenticatorSelection: &device.WebAuthnOptions_AuthenticatorSelectionCriteria{ + UserVerification: device.WebAuthnOptions_USER_VERIFICATION_PREFERRED.Enum(), + RequireResidentKey: proto.Bool(true), + AuthenticatorAttachment: device.WebAuthnOptions_PLATFORM.Enum(), }, + PubKeyCredParams: supportedPublicKeyCredentialParameters, }, }, }, diff --git a/pkg/webauthnutil/device_type_test.go b/pkg/webauthnutil/device_type_test.go index d064d1937..b04efb1be 100644 --- a/pkg/webauthnutil/device_type_test.go +++ b/pkg/webauthnutil/device_type_test.go @@ -21,9 +21,9 @@ func TestGetDeviceType(t *testing.T) { client := &mockDataBrokerServiceClient{ get: func(ctx context.Context, in *databroker.GetRequest, opts ...grpc.CallOption) (*databroker.GetResponse, error) { assert.Equal(t, "type.googleapis.com/pomerium.device.Type", in.GetType()) - assert.Equal(t, "default", in.GetId()) + assert.Equal(t, "any", in.GetId()) any, _ := anypb.New(&device.Type{ - Id: "default", + Id: "any", Name: "Example", }) return &databroker.GetResponse{ @@ -35,7 +35,7 @@ func TestGetDeviceType(t *testing.T) { }, nil }, } - deviceType, err := GetDeviceType(ctx, client, "default") + deviceType, err := GetDeviceType(ctx, client, "any") assert.NoError(t, err) assert.Equal(t, "Example", deviceType.GetName()) }) @@ -45,9 +45,9 @@ func TestGetDeviceType(t *testing.T) { return nil, status.Error(codes.NotFound, "not found") }, } - deviceType, err := GetDeviceType(ctx, client, "default") + deviceType, err := GetDeviceType(ctx, client, "any") assert.NoError(t, err) - assert.Equal(t, "default", deviceType.GetName()) + assert.Equal(t, "Any", deviceType.GetName()) }) t.Run("not found", func(t *testing.T) { client := &mockDataBrokerServiceClient{ diff --git a/pkg/webauthnutil/options_test.go b/pkg/webauthnutil/options_test.go index fc8799ac8..91677c4a5 100644 --- a/pkg/webauthnutil/options_test.go +++ b/pkg/webauthnutil/options_test.go @@ -14,21 +14,21 @@ import ( func TestGenerateCreationOptions(t *testing.T) { t.Run("random challenge", func(t *testing.T) { key := []byte{1, 2, 3} - options1 := GenerateCreationOptions(key, predefinedDeviceTypes["default"], &user.User{ + options1 := GenerateCreationOptions(key, predefinedDeviceTypes[DefaultDeviceType], &user.User{ Id: "example", Email: "test@example.com", Name: "Test User", }) - options2 := GenerateCreationOptions(key, predefinedDeviceTypes["default"], &user.User{ + options2 := GenerateCreationOptions(key, predefinedDeviceTypes[DefaultDeviceType], &user.User{ Id: "example", Email: "test@example.com", Name: "Test User", }) assert.NotEqual(t, options1.Challenge, options2.Challenge) }) - t.Run("default", func(t *testing.T) { + t.Run(DefaultDeviceType, func(t *testing.T) { key := []byte{1, 2, 3} - options := GenerateCreationOptions(key, predefinedDeviceTypes["default"], &user.User{ + options := GenerateCreationOptions(key, predefinedDeviceTypes[DefaultDeviceType], &user.User{ Id: "example", Email: "test@example.com", Name: "Test User", @@ -65,13 +65,13 @@ func TestGenerateCreationOptions(t *testing.T) { func TestGenerateRequestOptions(t *testing.T) { t.Run("random challenge", func(t *testing.T) { key := []byte{1, 2, 3} - options1 := GenerateRequestOptions(key, predefinedDeviceTypes["default"], nil) - options2 := GenerateRequestOptions(key, predefinedDeviceTypes["default"], nil) + options1 := GenerateRequestOptions(key, predefinedDeviceTypes[DefaultDeviceType], nil) + options2 := GenerateRequestOptions(key, predefinedDeviceTypes[DefaultDeviceType], nil) assert.NotEqual(t, options1.Challenge, options2.Challenge) }) - t.Run("default", func(t *testing.T) { + t.Run(DefaultDeviceType, func(t *testing.T) { key := []byte{1, 2, 3} - options := GenerateRequestOptions(key, predefinedDeviceTypes["default"], []*device.Credential{ + options := GenerateRequestOptions(key, predefinedDeviceTypes[DefaultDeviceType], []*device.Credential{ {Id: "device1", Specifier: &device.Credential_Webauthn{Webauthn: &device.Credential_WebAuthn{ Id: []byte{4, 5, 6}, }}},