devices: switch "default" device type to two built-in default device types (#2835)

This commit is contained in:
Caleb Doxsey 2021-12-20 10:44:29 -07:00 committed by GitHub
parent 9408401dbd
commit a3be1b7cc5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 47 additions and 22 deletions

View file

@ -16,6 +16,7 @@ import (
"github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/cryptutil" "github.com/pomerium/pomerium/pkg/cryptutil"
"github.com/pomerium/pomerium/pkg/webauthnutil"
) )
// ValidateOptions checks that configuration are complete and valid. // 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{ uri = uri.ResolveReference(&url.URL{
Path: "/.pomerium/webauthn", Path: "/.pomerium/webauthn",
RawQuery: buildURLValues(values, url.Values{ RawQuery: buildURLValues(values, url.Values{
urlutil.QueryDeviceType: {"default"}, urlutil.QueryDeviceType: {webauthnutil.DefaultDeviceType},
urlutil.QueryEnrollmentToken: nil, urlutil.QueryEnrollmentToken: nil,
urlutil.QueryRedirectURI: {uri.ResolveReference(&url.URL{ urlutil.QueryRedirectURI: {uri.ResolveReference(&url.URL{
Path: "/.pomerium/", Path: "/.pomerium/",

View file

@ -24,6 +24,7 @@ import (
"github.com/pomerium/pomerium/internal/telemetry/requestid" "github.com/pomerium/pomerium/internal/telemetry/requestid"
"github.com/pomerium/pomerium/internal/urlutil" "github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/policy/criteria" "github.com/pomerium/pomerium/pkg/policy/criteria"
"github.com/pomerium/pomerium/pkg/webauthnutil"
) )
func (a *Authorize) handleResultAllowed( func (a *Authorize) handleResultAllowed(
@ -212,7 +213,7 @@ func (a *Authorize) requireWebAuthnResponse(
if deviceType, ok := result.Allow.AdditionalData["device_type"].(string); ok { if deviceType, ok := result.Allow.AdditionalData["device_type"].(string); ok {
q.Set(urlutil.QueryDeviceType, deviceType) q.Set(urlutil.QueryDeviceType, deviceType)
} else { } else {
q.Set(urlutil.QueryDeviceType, "default") q.Set(urlutil.QueryDeviceType, webauthnutil.DefaultDeviceType)
} }
q.Set(urlutil.QueryRedirectURI, checkRequestURL.String()) q.Set(urlutil.QueryRedirectURI, checkRequestURL.String())
signinURL.RawQuery = q.Encode() signinURL.RawQuery = q.Encode()

View file

@ -6,15 +6,25 @@ import (
"github.com/pomerium/webauthn/cose" "github.com/pomerium/webauthn/cose"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"github.com/pomerium/pomerium/pkg/grpc/databroker" "github.com/pomerium/pomerium/pkg/grpc/databroker"
"github.com/pomerium/pomerium/pkg/grpc/device" "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{ var predefinedDeviceTypes = map[string]*device.Type{
"default": { "any": {
Id: "default", Id: "any",
Name: "default", Name: "Any",
Specifier: &device.Type_Webauthn{ Specifier: &device.Type_Webauthn{
Webauthn: &device.Type_WebAuthn{ Webauthn: &device.Type_WebAuthn{
Options: &device.WebAuthnOptions{ Options: &device.WebAuthnOptions{
@ -22,14 +32,27 @@ var predefinedDeviceTypes = map[string]*device.Type{
AuthenticatorSelection: &device.WebAuthnOptions_AuthenticatorSelectionCriteria{ AuthenticatorSelection: &device.WebAuthnOptions_AuthenticatorSelectionCriteria{
UserVerification: device.WebAuthnOptions_USER_VERIFICATION_PREFERRED.Enum(), UserVerification: device.WebAuthnOptions_USER_VERIFICATION_PREFERRED.Enum(),
}, },
PubKeyCredParams: []*device.WebAuthnOptions_PublicKeyCredentialParameters{ PubKeyCredParams: supportedPublicKeyCredentialParameters,
{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)},
}, },
}, },
}, },
}, },
"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,
},
},
},
}, },
} }

View file

@ -21,9 +21,9 @@ func TestGetDeviceType(t *testing.T) {
client := &mockDataBrokerServiceClient{ client := &mockDataBrokerServiceClient{
get: func(ctx context.Context, in *databroker.GetRequest, opts ...grpc.CallOption) (*databroker.GetResponse, error) { 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, "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{ any, _ := anypb.New(&device.Type{
Id: "default", Id: "any",
Name: "Example", Name: "Example",
}) })
return &databroker.GetResponse{ return &databroker.GetResponse{
@ -35,7 +35,7 @@ func TestGetDeviceType(t *testing.T) {
}, nil }, nil
}, },
} }
deviceType, err := GetDeviceType(ctx, client, "default") deviceType, err := GetDeviceType(ctx, client, "any")
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "Example", deviceType.GetName()) assert.Equal(t, "Example", deviceType.GetName())
}) })
@ -45,9 +45,9 @@ func TestGetDeviceType(t *testing.T) {
return nil, status.Error(codes.NotFound, "not found") 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.NoError(t, err)
assert.Equal(t, "default", deviceType.GetName()) assert.Equal(t, "Any", deviceType.GetName())
}) })
t.Run("not found", func(t *testing.T) { t.Run("not found", func(t *testing.T) {
client := &mockDataBrokerServiceClient{ client := &mockDataBrokerServiceClient{

View file

@ -14,21 +14,21 @@ import (
func TestGenerateCreationOptions(t *testing.T) { func TestGenerateCreationOptions(t *testing.T) {
t.Run("random challenge", func(t *testing.T) { t.Run("random challenge", func(t *testing.T) {
key := []byte{1, 2, 3} key := []byte{1, 2, 3}
options1 := GenerateCreationOptions(key, predefinedDeviceTypes["default"], &user.User{ options1 := GenerateCreationOptions(key, predefinedDeviceTypes[DefaultDeviceType], &user.User{
Id: "example", Id: "example",
Email: "test@example.com", Email: "test@example.com",
Name: "Test User", Name: "Test User",
}) })
options2 := GenerateCreationOptions(key, predefinedDeviceTypes["default"], &user.User{ options2 := GenerateCreationOptions(key, predefinedDeviceTypes[DefaultDeviceType], &user.User{
Id: "example", Id: "example",
Email: "test@example.com", Email: "test@example.com",
Name: "Test User", Name: "Test User",
}) })
assert.NotEqual(t, options1.Challenge, options2.Challenge) 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} key := []byte{1, 2, 3}
options := GenerateCreationOptions(key, predefinedDeviceTypes["default"], &user.User{ options := GenerateCreationOptions(key, predefinedDeviceTypes[DefaultDeviceType], &user.User{
Id: "example", Id: "example",
Email: "test@example.com", Email: "test@example.com",
Name: "Test User", Name: "Test User",
@ -65,13 +65,13 @@ func TestGenerateCreationOptions(t *testing.T) {
func TestGenerateRequestOptions(t *testing.T) { func TestGenerateRequestOptions(t *testing.T) {
t.Run("random challenge", func(t *testing.T) { t.Run("random challenge", func(t *testing.T) {
key := []byte{1, 2, 3} key := []byte{1, 2, 3}
options1 := GenerateRequestOptions(key, predefinedDeviceTypes["default"], nil) options1 := GenerateRequestOptions(key, predefinedDeviceTypes[DefaultDeviceType], nil)
options2 := GenerateRequestOptions(key, predefinedDeviceTypes["default"], nil) options2 := GenerateRequestOptions(key, predefinedDeviceTypes[DefaultDeviceType], nil)
assert.NotEqual(t, options1.Challenge, options2.Challenge) 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} 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: "device1", Specifier: &device.Credential_Webauthn{Webauthn: &device.Credential_WebAuthn{
Id: []byte{4, 5, 6}, Id: []byte{4, 5, 6},
}}}, }}},