diff --git a/internal/handlers/webauthn_test.go b/internal/handlers/webauthn_test.go
index 434ed496b..9678ff389 100644
--- a/internal/handlers/webauthn_test.go
+++ b/internal/handlers/webauthn_test.go
@@ -52,7 +52,7 @@ func TestWebAuthnGetUser(t *testing.T) {
require.NoError(t, err)
require.NotNil(t, user)
- assert.Equal(t, []byte("john"), user.WebAuthnID())
+ assert.Equal(t, []byte{}, user.WebAuthnID())
assert.Equal(t, "john", user.WebAuthnName())
assert.Equal(t, "john", user.Username)
@@ -109,7 +109,7 @@ func TestWebAuthnGetUserWithoutDisplayName(t *testing.T) {
ctx.StorageMock.EXPECT().LoadWebAuthnDevicesByUsername(ctx.Ctx, "john").Return([]model.WebAuthnDevice{
{
ID: 1,
- RPID: "https://example.com",
+ RPID: "example.com",
Username: "john",
Description: "Primary",
KID: model.NewBase64([]byte("abc123")),
diff --git a/internal/model/u2f_device.go b/internal/model/u2f_device.go
deleted file mode 100644
index 331f43d75..000000000
--- a/internal/model/u2f_device.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package model
-
-// U2FDevice represents a users U2F device row in the database.
-type U2FDevice struct {
- ID int `db:"id"`
- Username string `db:"username"`
- Description string `db:"description"`
- KeyHandle []byte `db:"key_handle"`
- PublicKey []byte `db:"public_key"`
-}
diff --git a/internal/model/webauthn.go b/internal/model/webauthn.go
index 0921ccd24..be2f8c370 100644
--- a/internal/model/webauthn.go
+++ b/internal/model/webauthn.go
@@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/base64"
"encoding/hex"
+ "encoding/json"
"strings"
"time"
@@ -19,9 +20,13 @@ const (
// WebAuthnUser is an object to represent a user for the WebAuthn lib.
type WebAuthnUser struct {
- Username string
- DisplayName string
- Devices []WebAuthnDevice
+ ID int `db:"id"`
+ RPID string `db:"rpid"`
+ Username string `db:"username"`
+ UserID string `db:"userid"`
+ DisplayName string `db:"-"`
+
+ Devices []WebAuthnDevice `db:"-"`
}
// HasFIDOU2F returns true if the user has any attestation type `fido-u2f` devices.
@@ -37,7 +42,7 @@ func (w WebAuthnUser) HasFIDOU2F() bool {
// WebAuthnID implements the webauthn.User interface.
func (w WebAuthnUser) WebAuthnID() []byte {
- return []byte(w.Username)
+ return []byte(w.UserID)
}
// WebAuthnName implements the webauthn.User interface.
@@ -122,11 +127,11 @@ func NewWebAuthnDeviceFromCredential(rpid, username, description string, credent
CreatedAt: time.Now(),
Description: description,
KID: NewBase64(credential.ID),
- PublicKey: credential.PublicKey,
AttestationType: credential.AttestationType,
+ Transport: strings.Join(transport, ","),
SignCount: credential.Authenticator.SignCount,
CloneWarning: credential.Authenticator.CloneWarning,
- Transport: strings.Join(transport, ","),
+ PublicKey: credential.PublicKey,
}
aaguid, err := uuid.Parse(hex.EncodeToString(credential.Authenticator.AAGUID))
@@ -146,12 +151,12 @@ type WebAuthnDevice struct {
Username string `db:"username"`
Description string `db:"description"`
KID Base64 `db:"kid"`
- PublicKey []byte `db:"public_key"`
+ AAGUID uuid.NullUUID `db:"aaguid"`
AttestationType string `db:"attestation_type"`
Transport string `db:"transport"`
- AAGUID uuid.NullUUID `db:"aaguid"`
SignCount uint32 `db:"sign_count"`
CloneWarning bool `db:"clone_warning"`
+ PublicKey []byte `db:"public_key"`
}
// UpdateSignInInfo adjusts the values of the WebAuthnDevice after a sign in.
@@ -172,8 +177,8 @@ func (d *WebAuthnDevice) UpdateSignInInfo(config *webauthn.Config, now time.Time
}
}
-// LastUsed provides LastUsedAt as a *time.Time instead of sql.NullTime.
-func (d *WebAuthnDevice) LastUsed() *time.Time {
+// DataValueLastUsedAt provides LastUsedAt as a *time.Time instead of sql.NullTime.
+func (d *WebAuthnDevice) DataValueLastUsedAt() *time.Time {
if d.LastUsedAt.Valid {
value := time.Unix(d.LastUsedAt.Time.Unix(), int64(d.LastUsedAt.Time.Nanosecond()))
@@ -183,22 +188,43 @@ func (d *WebAuthnDevice) LastUsed() *time.Time {
return nil
}
-// ToData converts this WebAuthnDevice into the data format for exporting etc.
+// DataValueAAGUID provides AAGUID as a *string instead of uuid.NullUUID.
+func (d *WebAuthnDevice) DataValueAAGUID() *string {
+ if d.AAGUID.Valid {
+ value := d.AAGUID.UUID.String()
+
+ return &value
+ }
+
+ return nil
+}
+
func (d *WebAuthnDevice) ToData() WebAuthnDeviceData {
- return WebAuthnDeviceData{
+ o := WebAuthnDeviceData{
+ ID: d.ID,
CreatedAt: d.CreatedAt,
- LastUsedAt: d.LastUsed(),
+ LastUsedAt: d.DataValueLastUsedAt(),
RPID: d.RPID,
Username: d.Username,
Description: d.Description,
KID: d.KID.String(),
- PublicKey: base64.StdEncoding.EncodeToString(d.PublicKey),
+ AAGUID: d.DataValueAAGUID(),
AttestationType: d.AttestationType,
- Transport: d.Transport,
- AAGUID: d.AAGUID.UUID.String(),
SignCount: d.SignCount,
CloneWarning: d.CloneWarning,
+ PublicKey: base64.StdEncoding.EncodeToString(d.PublicKey),
}
+
+ if d.Transport != "" {
+ o.Transports = strings.Split(d.Transport, ",")
+ }
+
+ return o
+}
+
+// MarshalJSON returns the WebAuthnDevice in a JSON friendly manner.
+func (d *WebAuthnDevice) MarshalJSON() (data []byte, err error) {
+ return json.Marshal(d.ToData())
}
// MarshalYAML marshals this model into YAML.
@@ -220,12 +246,14 @@ func (d *WebAuthnDevice) UnmarshalYAML(value *yaml.Node) (err error) {
var aaguid uuid.UUID
- if aaguid, err = uuid.Parse(o.AAGUID); err != nil {
- return err
- }
+ if o.AAGUID != nil {
+ if aaguid, err = uuid.Parse(*o.AAGUID); err != nil {
+ return err
+ }
- if aaguid.ID() != 0 {
- d.AAGUID = uuid.NullUUID{Valid: true, UUID: aaguid}
+ if aaguid.ID() != 0 {
+ d.AAGUID = uuid.NullUUID{Valid: true, UUID: aaguid}
+ }
}
var kid []byte
@@ -241,7 +269,7 @@ func (d *WebAuthnDevice) UnmarshalYAML(value *yaml.Node) (err error) {
d.Username = o.Username
d.Description = o.Description
d.AttestationType = o.AttestationType
- d.Transport = o.Transport
+ d.Transport = strings.Join(o.Transports, ",")
d.SignCount = o.SignCount
d.CloneWarning = o.CloneWarning
@@ -254,18 +282,62 @@ func (d *WebAuthnDevice) UnmarshalYAML(value *yaml.Node) (err error) {
// WebAuthnDeviceData represents a WebAuthn Device in the database storage.
type WebAuthnDeviceData struct {
- CreatedAt time.Time `yaml:"created_at"`
- LastUsedAt *time.Time `yaml:"last_used_at"`
- RPID string `yaml:"rpid"`
- Username string `yaml:"username"`
- Description string `yaml:"description"`
- KID string `yaml:"kid"`
- PublicKey string `yaml:"public_key"`
- AttestationType string `yaml:"attestation_type"`
- Transport string `yaml:"transport"`
- AAGUID string `yaml:"aaguid"`
- SignCount uint32 `yaml:"sign_count"`
- CloneWarning bool `yaml:"clone_warning"`
+ ID int `json:"id" yaml:"-"`
+ CreatedAt time.Time `json:"created_at" yaml:"created_at"`
+ LastUsedAt *time.Time `json:"last_used_at,omitempty" yaml:"last_used_at,omitempty"`
+ RPID string `json:"rpid" yaml:"rpid"`
+ Username string `json:"-" yaml:"username"`
+ Description string `json:"description" yaml:"description"`
+ KID string `json:"kid" yaml:"kid"`
+ AAGUID *string `json:"aaguid,omitempty" yaml:"aaguid,omitempty"`
+ AttestationType string `json:"attestation_type" yaml:"attestation_type"`
+ Transports []string `json:"transports" yaml:"transports"`
+ SignCount uint32 `json:"sign_count" yaml:"sign_count"`
+ CloneWarning bool `json:"clone_warning" yaml:"clone_warning"`
+ PublicKey string `json:"public_key" yaml:"public_key"`
+}
+
+func (d *WebAuthnDeviceData) ToDevice() (device *WebAuthnDevice, err error) {
+ device = &WebAuthnDevice{
+ CreatedAt: d.CreatedAt,
+ RPID: d.RPID,
+ Username: d.Username,
+ Description: d.Description,
+ AttestationType: d.AttestationType,
+ Transport: strings.Join(d.Transports, ","),
+ SignCount: d.SignCount,
+ CloneWarning: d.CloneWarning,
+ }
+
+ if device.PublicKey, err = base64.StdEncoding.DecodeString(d.PublicKey); err != nil {
+ return nil, err
+ }
+
+ var aaguid uuid.UUID
+
+ if d.AAGUID != nil {
+ if aaguid, err = uuid.Parse(*d.AAGUID); err != nil {
+ return nil, err
+ }
+
+ if aaguid.ID() != 0 {
+ device.AAGUID = uuid.NullUUID{Valid: true, UUID: aaguid}
+ }
+ }
+
+ var kid []byte
+
+ if kid, err = base64.StdEncoding.DecodeString(d.KID); err != nil {
+ return nil, err
+ }
+
+ device.KID = NewBase64(kid)
+
+ if d.LastUsedAt != nil {
+ device.LastUsedAt = sql.NullTime{Valid: true, Time: *d.LastUsedAt}
+ }
+
+ return device, nil
}
// WebAuthnDeviceExport represents a WebAuthnDevice export file.
diff --git a/internal/storage/migrations/V0002.Webauthn.mysql.down.sql b/internal/storage/migrations/V0002.WebAuthn.mysql.down.sql
similarity index 100%
rename from internal/storage/migrations/V0002.Webauthn.mysql.down.sql
rename to internal/storage/migrations/V0002.WebAuthn.mysql.down.sql
diff --git a/internal/storage/migrations/V0002.Webauthn.mysql.up.sql b/internal/storage/migrations/V0002.WebAuthn.mysql.up.sql
similarity index 100%
rename from internal/storage/migrations/V0002.Webauthn.mysql.up.sql
rename to internal/storage/migrations/V0002.WebAuthn.mysql.up.sql
diff --git a/internal/storage/migrations/V0002.Webauthn.postgres.down.sql b/internal/storage/migrations/V0002.WebAuthn.postgres.down.sql
similarity index 100%
rename from internal/storage/migrations/V0002.Webauthn.postgres.down.sql
rename to internal/storage/migrations/V0002.WebAuthn.postgres.down.sql
diff --git a/internal/storage/migrations/V0002.Webauthn.postgres.up.sql b/internal/storage/migrations/V0002.WebAuthn.postgres.up.sql
similarity index 100%
rename from internal/storage/migrations/V0002.Webauthn.postgres.up.sql
rename to internal/storage/migrations/V0002.WebAuthn.postgres.up.sql
diff --git a/internal/storage/migrations/V0002.Webauthn.sqlite.down.sql b/internal/storage/migrations/V0002.WebAuthn.sqlite.down.sql
similarity index 100%
rename from internal/storage/migrations/V0002.Webauthn.sqlite.down.sql
rename to internal/storage/migrations/V0002.WebAuthn.sqlite.down.sql
diff --git a/internal/storage/migrations/V0002.Webauthn.sqlite.up.sql b/internal/storage/migrations/V0002.WebAuthn.sqlite.up.sql
similarity index 100%
rename from internal/storage/migrations/V0002.Webauthn.sqlite.up.sql
rename to internal/storage/migrations/V0002.WebAuthn.sqlite.up.sql
diff --git a/internal/storage/migrations/V0003.WebauthnKIDLength.all.down.sql b/internal/storage/migrations/V0003.WebAuthnKIDLength.all.down.sql
similarity index 100%
rename from internal/storage/migrations/V0003.WebauthnKIDLength.all.down.sql
rename to internal/storage/migrations/V0003.WebAuthnKIDLength.all.down.sql
diff --git a/internal/storage/migrations/V0003.WebauthnKIDLength.mysql.up.sql b/internal/storage/migrations/V0003.WebAuthnKIDLength.mysql.up.sql
similarity index 100%
rename from internal/storage/migrations/V0003.WebauthnKIDLength.mysql.up.sql
rename to internal/storage/migrations/V0003.WebAuthnKIDLength.mysql.up.sql
diff --git a/internal/storage/migrations/V0003.WebauthnKIDLength.postgres.up.sql b/internal/storage/migrations/V0003.WebAuthnKIDLength.postgres.up.sql
similarity index 100%
rename from internal/storage/migrations/V0003.WebauthnKIDLength.postgres.up.sql
rename to internal/storage/migrations/V0003.WebAuthnKIDLength.postgres.up.sql
diff --git a/internal/storage/migrations/V0003.WebauthnKIDLength.sqlite.up.sql b/internal/storage/migrations/V0003.WebAuthnKIDLength.sqlite.up.sql
similarity index 100%
rename from internal/storage/migrations/V0003.WebauthnKIDLength.sqlite.up.sql
rename to internal/storage/migrations/V0003.WebAuthnKIDLength.sqlite.up.sql
diff --git a/web/src/models/Methods.ts b/web/src/models/Methods.ts
index d1c0c52e1..baa0cb342 100644
--- a/web/src/models/Methods.ts
+++ b/web/src/models/Methods.ts
@@ -1,5 +1,5 @@
export enum SecondFactorMethod {
TOTP = 1,
- Webauthn,
+ WebAuthn,
MobilePush,
}
diff --git a/web/src/models/Webauthn.ts b/web/src/models/WebAuthn.ts
similarity index 98%
rename from web/src/models/Webauthn.ts
rename to web/src/models/WebAuthn.ts
index bbd75503d..404197d2b 100644
--- a/web/src/models/Webauthn.ts
+++ b/web/src/models/WebAuthn.ts
@@ -89,7 +89,7 @@ export enum AttestationResult {
FailureSyntax,
FailureSupport,
FailureUnknown,
- FailureWebauthnNotSupported,
+ FailureWebAuthnNotSupported,
FailureToken,
}
@@ -111,7 +111,7 @@ export enum AssertionResult {
FailureSyntax,
FailureUnknown,
FailureUnknownSecurity,
- FailureWebauthnNotSupported,
+ FailureWebAuthnNotSupported,
FailureChallenge,
}
diff --git a/web/src/services/UserInfo.ts b/web/src/services/UserInfo.ts
index ea2971172..04e81fa5e 100644
--- a/web/src/services/UserInfo.ts
+++ b/web/src/services/UserInfo.ts
@@ -22,7 +22,7 @@ export function toEnum(method: Method2FA): SecondFactorMethod {
case "totp":
return SecondFactorMethod.TOTP;
case "webauthn":
- return SecondFactorMethod.Webauthn;
+ return SecondFactorMethod.WebAuthn;
case "mobile_push":
return SecondFactorMethod.MobilePush;
}
@@ -32,7 +32,7 @@ export function toString(method: SecondFactorMethod): Method2FA {
switch (method) {
case SecondFactorMethod.TOTP:
return "totp";
- case SecondFactorMethod.Webauthn:
+ case SecondFactorMethod.WebAuthn:
return "webauthn";
case SecondFactorMethod.MobilePush:
return "mobile_push";
diff --git a/web/src/services/Webauthn.ts b/web/src/services/WebAuthn.ts
similarity index 99%
rename from web/src/services/Webauthn.ts
rename to web/src/services/WebAuthn.ts
index ecd2dbfe8..6e113578c 100644
--- a/web/src/services/Webauthn.ts
+++ b/web/src/services/WebAuthn.ts
@@ -16,7 +16,7 @@ import {
PublicKeyCredentialJSON,
PublicKeyCredentialRequestOptionsJSON,
PublicKeyCredentialRequestOptionsStatus,
-} from "@models/Webauthn";
+} from "@models/WebAuthn";
import {
OptionalDataServiceResponse,
ServiceResponse,
diff --git a/web/src/views/DeviceRegistration/RegisterWebAuthn.tsx b/web/src/views/DeviceRegistration/RegisterWebAuthn.tsx
index 33a93ef6a..338fb86c4 100644
--- a/web/src/views/DeviceRegistration/RegisterWebAuthn.tsx
+++ b/web/src/views/DeviceRegistration/RegisterWebAuthn.tsx
@@ -9,9 +9,9 @@ import { IdentityToken } from "@constants/SearchParams";
import { useNotifications } from "@hooks/NotificationsContext";
import { useQueryParam } from "@hooks/QueryParam";
import LoginLayout from "@layouts/LoginLayout";
-import { AttestationResult } from "@models/Webauthn";
+import { AttestationResult } from "@models/WebAuthn";
import { FirstFactorPath } from "@services/Api";
-import { performAttestationCeremony } from "@services/Webauthn";
+import { performAttestationCeremony } from "@services/WebAuthn";
const RegisterWebAuthn = function () {
const styles = useStyles();
@@ -53,7 +53,7 @@ const RegisterWebAuthn = function () {
"The attestation challenge was rejected as malformed or incompatible by your browser.",
);
break;
- case AttestationResult.FailureWebauthnNotSupported:
+ case AttestationResult.FailureWebAuthnNotSupported:
createErrorNotification("Your browser does not support the WebAuthN protocol.");
break;
case AttestationResult.FailureUserConsent:
diff --git a/web/src/views/LoginPortal/LoginPortal.tsx b/web/src/views/LoginPortal/LoginPortal.tsx
index 9a3b9accc..cd143376a 100644
--- a/web/src/views/LoginPortal/LoginPortal.tsx
+++ b/web/src/views/LoginPortal/LoginPortal.tsx
@@ -143,7 +143,7 @@ const LoginPortal = function (props: Props) {
if (configuration.available_methods.size === 0) {
redirect(AuthenticatedRoute, false);
} else {
- if (userInfo.method === SecondFactorMethod.Webauthn) {
+ if (userInfo.method === SecondFactorMethod.WebAuthn) {
redirect(`${SecondFactorRoute}${SecondFactorWebAuthnSubRoute}`);
} else if (userInfo.method === SecondFactorMethod.MobilePush) {
redirect(`${SecondFactorRoute}${SecondFactorPushSubRoute}`);
diff --git a/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx b/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx
index 93a85c0d9..8ec82f231 100644
--- a/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx
+++ b/web/src/views/LoginPortal/SecondFactor/MethodSelectionDialog.tsx
@@ -39,12 +39,12 @@ const MethodSelectionDialog = function (props: Props) {
onClick={() => props.onClick(SecondFactorMethod.TOTP)}
/>
) : null}
- {props.methods.has(SecondFactorMethod.Webauthn) && props.webauthnSupported ? (
+ {props.methods.has(SecondFactorMethod.WebAuthn) && props.webauthnSupported ? (
}
- onClick={() => props.onClick(SecondFactorMethod.Webauthn)}
+ onClick={() => props.onClick(SecondFactorMethod.WebAuthn)}
/>
) : null}
{props.methods.has(SecondFactorMethod.MobilePush) ? (
diff --git a/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx b/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx
index e86b1afd0..6d9b9873e 100644
--- a/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx
+++ b/web/src/views/LoginPortal/SecondFactor/SecondFactorForm.tsx
@@ -19,11 +19,11 @@ import { UserInfo } from "@models/UserInfo";
import { initiateTOTPRegistrationProcess, initiateWebAuthnRegistrationProcess } from "@services/RegisterDevice";
import { AuthenticationLevel } from "@services/State";
import { setPreferred2FAMethod } from "@services/UserInfo";
-import { isWebAuthnSupported } from "@services/Webauthn";
+import { isWebAuthnSupported } from "@services/WebAuthn";
import MethodSelectionDialog from "@views/LoginPortal/SecondFactor/MethodSelectionDialog";
import OneTimePasswordMethod from "@views/LoginPortal/SecondFactor/OneTimePasswordMethod";
import PushNotificationMethod from "@views/LoginPortal/SecondFactor/PushNotificationMethod";
-import WebauthnMethod from "@views/LoginPortal/SecondFactor/WebauthnMethod";
+import WebAuthnMethod from "@views/LoginPortal/SecondFactor/WebAuthnMethod";
export interface Props {
authenticationLevel: AuthenticationLevel;
@@ -41,12 +41,12 @@ const SecondFactorForm = function (props: Props) {
const [methodSelectionOpen, setMethodSelectionOpen] = useState(false);
const { createInfoNotification, createErrorNotification } = useNotifications();
const [registrationInProgress, setRegistrationInProgress] = useState(false);
- const [webauthnSupported, setWebauthnSupported] = useState(false);
+ const [stateWebAuthnSupported, setStateWebAuthnSupported] = useState(false);
const { t: translate } = useTranslation();
useEffect(() => {
- setWebauthnSupported(isWebAuthnSupported());
- }, [setWebauthnSupported]);
+ setStateWebAuthnSupported(isWebAuthnSupported());
+ }, [setStateWebAuthnSupported]);
const initiateRegistration = (initiateRegistrationFunc: () => Promise) => {
return async () => {
@@ -90,7 +90,7 @@ const SecondFactorForm = function (props: Props) {
setMethodSelectionOpen(false)}
onClick={handleMethodSelected}
/>
@@ -126,10 +126,10 @@ const SecondFactorForm = function (props: Props) {
createErrorNotification(err.message)}
diff --git a/web/src/views/LoginPortal/SecondFactor/WebauthnMethod.tsx b/web/src/views/LoginPortal/SecondFactor/WebAuthnMethod.tsx
similarity index 97%
rename from web/src/views/LoginPortal/SecondFactor/WebauthnMethod.tsx
rename to web/src/views/LoginPortal/SecondFactor/WebAuthnMethod.tsx
index 6cd064e5e..f68dbe9ce 100644
--- a/web/src/views/LoginPortal/SecondFactor/WebauthnMethod.tsx
+++ b/web/src/views/LoginPortal/SecondFactor/WebAuthnMethod.tsx
@@ -11,13 +11,13 @@ import { useIsMountedRef } from "@hooks/Mounted";
import { useQueryParam } from "@hooks/QueryParam";
import { useTimer } from "@hooks/Timer";
import { useWorkflow } from "@hooks/Workflow";
-import { AssertionResult } from "@models/Webauthn";
+import { AssertionResult } from "@models/WebAuthn";
import { AuthenticationLevel } from "@services/State";
import {
getAssertionPublicKeyCredentialResult,
getAssertionRequestOptions,
postAssertionPublicKeyCredentialResult,
-} from "@services/Webauthn";
+} from "@services/WebAuthn";
import IconWithContext from "@views/LoginPortal/SecondFactor/IconWithContext";
import MethodContainer, { State as MethodContainerState } from "@views/LoginPortal/SecondFactor/MethodContainer";
@@ -37,7 +37,7 @@ export interface Props {
onSignInSuccess: (redirectURL: string | undefined) => void;
}
-const WebauthnMethod = function (props: Props) {
+const WebAuthnMethod = function (props: Props) {
const signInTimeout = 30;
const [state, setState] = useState(State.WaitTouch);
const styles = useStyles();
@@ -86,7 +86,7 @@ const WebauthnMethod = function (props: Props) {
),
);
break;
- case AssertionResult.FailureWebauthnNotSupported:
+ case AssertionResult.FailureWebAuthnNotSupported:
onSignInErrorCallback(new Error("Your browser does not support the WebAuthN protocol."));
break;
case AssertionResult.FailureUnknownSecurity:
@@ -179,7 +179,7 @@ const WebauthnMethod = function (props: Props) {
);
};
-export default WebauthnMethod;
+export default WebAuthnMethod;
const useStyles = makeStyles((theme: Theme) => ({
icon: {