public ActionResult CredentialOptions([FromBody] RegisterModel model)
        {
            var user = new Fido2User
            {
                DisplayName = $"{model.FirstName} {model.LastName}",
                Name        = model.Email,
                Id          = Encoding.UTF8.GetBytes(model.Email)
            };

            var options = fido2.RequestNewCredential(user, new List <PublicKeyCredentialDescriptor>());

            HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson());

            return(Json(options));
        }
        public static string MakeWebAuthnAttestationRequest(Fido2Configuration fido2Config, byte[] challenge, LoginUsr LUser, List <PublicKeyCredentialDescriptor> excludedCredentials)
        {
            string    usrId    = LUser.UsrId.ToString();
            string    usrIdB64 = System.Convert.ToBase64String(usrId.ToUtf8ByteArray());
            Fido2User user     = new Fido2User
            {
                DisplayName = LUser.UsrName,
                /* must be restricted to no more than than 64 for device like yubikey as it would fail without reason */
                //Name = (Guid.NewGuid().ToString() + " " + DateTime.UtcNow.ToString("o")).Left(64),
                //Id= Guid.NewGuid().ToString().ToUtf8ByteArray()
                Name = LUser.LoginName,
                Id   = usrIdB64.ToUtf8ByteArray()
            };
            AuthenticatorSelection authenticatorSelection = new AuthenticatorSelection
            {
                RequireResidentKey = false,
                UserVerification   = UserVerificationRequirement.Discouraged,
                //                 AuthenticatorAttachment = AuthenticatorAttachment.Platform,
            };
            AttestationConveyancePreference      attConveyancePreference = AttestationConveyancePreference.None;
            AuthenticationExtensionsClientInputs clientExtensions        = new AuthenticationExtensionsClientInputs
            {
                Extensions = true,
                SimpleTransactionAuthorization = string.Format("you are registering to {0}", fido2Config.ServerName),
                Location = true,
                UserVerificationMethod = true,
                BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds
                {
                    FAR = float.MaxValue,
                    FRR = float.MaxValue
                }
            };

            var fido2 = new Fido2(fido2Config);

            // must do this for the verification to work
            var options = fido2.RequestNewCredential(user, excludedCredentials, authenticatorSelection, attConveyancePreference, clientExtensions);

            // the challenge is random byte but we need more info, replace it
            options.Challenge = challenge;
            var createRequest = Fido2NetLib.CredentialCreateOptions.Create(fido2Config
                                                                           , challenge, user, authenticatorSelection, attConveyancePreference
                                                                           , excludedCredentials != null && excludedCredentials.Count > 0 ? excludedCredentials : null
                                                                           , clientExtensions);
            string createRequestJson = options.ToJson();

            return(createRequestJson);
        }
Beispiel #3
0
        public async Task <IActionResult> FidoRegister(string button)
        {
            var sub = HttpContext.User.Claims.FirstOrDefault(x => x.Type == "sub")?.Value;

            if (string.IsNullOrEmpty(sub))
            {
                return(RedirectToAction("Index", "Home"));
            }

            var user = await _users.FindByIdAsync(sub);

            if (user == null)
            {
                return(RedirectToAction("Index", "Home"));
            }

            var existingDbKeys = _authContext.FidoLogins.Where(x => x.UserId == user.Id).Select(x => x.PublicKeyIdBytes);
            var existingKeys   = new List <PublicKeyCredentialDescriptor>();

            foreach (var key in existingDbKeys)
            {
                existingKeys.Add(new PublicKeyCredentialDescriptor(key));
            }

            var exts = new AuthenticationExtensionsClientInputs()
            {
                Extensions = true, UserVerificationIndex = true, Location = true, UserVerificationMethod = true, BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds {
                    FAR = float.MaxValue, FRR = float.MaxValue
                }
            };
            Fido2User f2User = new Fido2User()
            {
                DisplayName = user.DisplayName,
                Id          = System.Text.Encoding.ASCII.GetBytes(user.Id),
                Name        = user.UserName
            };
            var authSelect = new AuthenticatorSelection()
            {
                RequireResidentKey = false,
                UserVerification   = UserVerificationRequirement.Preferred
            };

            var options = _lib.RequestNewCredential(f2User, existingKeys, authSelect, AttestationConveyancePreference.Direct, exts);

            HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson());
            return(Json(options));
        }
Beispiel #4
0
        public async Task <CredentialCreateOptions> StartWebAuthnRegistrationAsync(User user)
        {
            var providers = user.GetTwoFactorProviders();

            if (providers == null)
            {
                providers = new Dictionary <TwoFactorProviderType, TwoFactorProvider>();
            }
            var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);

            if (provider == null)
            {
                provider = new TwoFactorProvider
                {
                    Enabled = false
                };
            }
            if (provider.MetaData == null)
            {
                provider.MetaData = new Dictionary <string, object>();
            }

            var fidoUser = new Fido2User
            {
                DisplayName = user.Name,
                Name        = user.Email,
                Id          = user.Id.ToByteArray(),
            };

            var excludeCredentials = provider.MetaData
                                     .Where(k => k.Key.StartsWith("Key"))
                                     .Select(k => new TwoFactorProvider.WebAuthnData((dynamic)k.Value).Descriptor)
                                     .ToList();

            var options = _fido2.RequestNewCredential(fidoUser, excludeCredentials, AuthenticatorSelection.Default, AttestationConveyancePreference.None);

            provider.MetaData["pending"] = options.ToJson();
            providers[TwoFactorProviderType.WebAuthn] = provider;
            user.SetTwoFactorProviders(providers);
            await UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, false);

            return(options);
        }
    public async Task <ActionResult> AssertionOptionsPost([FromForm] string username, [FromForm] string userVerification)
    {
        try
        {
            var identityUser = await _signInManager.GetTwoFactorAuthenticationUserAsync();

            if (identityUser == null)
            {
                throw new InvalidOperationException($"Unable to load two-factor authentication user.");
            }

            var existingCredentials = new List <PublicKeyCredentialDescriptor>();

            if (!string.IsNullOrEmpty(identityUser.UserName))
            {
                var user = new Fido2User
                {
                    DisplayName = identityUser.UserName,
                    Name        = identityUser.UserName,
                    Id          = Encoding.UTF8.GetBytes(identityUser.UserName) // byte representation of userID is required
                };

                if (user == null)
                {
                    throw new ArgumentException("Username was not registered");
                }

                // 2. Get registered credentials from database
                var items = await _fido2Store.GetCredentialsByUserNameAsync(identityUser.UserName);

                existingCredentials = items.Select(c => c.Descriptor).NotNull().ToList();
            }

            var exts = new AuthenticationExtensionsClientInputs()
            {
                SimpleTransactionAuthorization = "FIDO", GenericTransactionAuthorization = new TxAuthGenericArg {
                    ContentType = "text/plain", Content = new byte[] { 0x46, 0x49, 0x44, 0x4F }
                }, UserVerificationIndex = true, Location = true, UserVerificationMethod = true
            };

            // 3. Create options
            var uv      = string.IsNullOrEmpty(userVerification) ? UserVerificationRequirement.Discouraged : userVerification.ToEnum <UserVerificationRequirement>();
            var options = _lib.GetAssertionOptions(
                existingCredentials,
                uv,
                exts
                );

            // 4. Temporarily store options, session/in-memory cache/redis/db
            HttpContext.Session.SetString("fido2.assertionOptions", options.ToJson());

            // 5. Return options to client
            return(Json(options));
        }

        catch (Exception e)
        {
            return(Json(new AssertionOptions {
                Status = "error", ErrorMessage = FormatException(e)
            }));
        }
    }
Beispiel #6
0
        public async Task <JsonResult> MakeCredentialOptions([FromForm] string username, [FromForm] string displayName, [FromForm] string attType, [FromForm] string authType, [FromForm] bool requireResidentKey, [FromForm] string userVerification)
        {
            try
            {
                if (string.IsNullOrEmpty(username))
                {
                    username = $"{displayName} (Usernameless user created at {DateTime.UtcNow})";
                }

                var identityUser = await _userManager.FindByEmailAsync(username);

                var user = new Fido2User
                {
                    DisplayName = identityUser.UserName,
                    Name        = identityUser.UserName,
                    Id          = Encoding.UTF8.GetBytes(identityUser.UserName) // byte representation of userID is required
                };

                // 2. Get user existing keys by username
                var items = await _fido2Storage.GetCredentialsByUsername(identityUser.UserName);

                var existingKeys = new List <PublicKeyCredentialDescriptor>();
                foreach (var publicKeyCredentialDescriptor in items)
                {
                    existingKeys.Add(publicKeyCredentialDescriptor.Descriptor);
                }

                // 3. Create options
                var authenticatorSelection = new AuthenticatorSelection
                {
                    RequireResidentKey = requireResidentKey,
                    UserVerification   = userVerification.ToEnum <UserVerificationRequirement>()
                };

                if (!string.IsNullOrEmpty(authType))
                {
                    authenticatorSelection.AuthenticatorAttachment = authType.ToEnum <AuthenticatorAttachment>();
                }

                var exts = new AuthenticationExtensionsClientInputs()
                {
                    Extensions = true, UserVerificationIndex = true, Location = true, UserVerificationMethod = true, BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds {
                        FAR = float.MaxValue, FRR = float.MaxValue
                    }
                };

                var options = _lib.RequestNewCredential(user, existingKeys, authenticatorSelection, attType.ToEnum <AttestationConveyancePreference>(), exts);

                // 4. Temporarily store options, session/in-memory cache/redis/db
                //HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson());
                var uniqueId = Guid.NewGuid().ToString();
                UniqueId = uniqueId;
                await _distributedCache.SetStringAsync(uniqueId, options.ToJson());

                // 5. return options to client
                return(Json(options));
            }
            catch (Exception e)
            {
                return(Json(new CredentialCreateOptions {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
Beispiel #7
0
        public async Task <FidoStoredCredential> AddSecurityKeyAsync(string userEmail, IJSRuntime jsRuntime)
        {
            if (string.IsNullOrWhiteSpace(userEmail))
            {
                throw new ArgumentNullException(nameof(userEmail));
            }

            if (jsRuntime == null)
            {
                throw new ArgumentNullException(nameof(jsRuntime));
            }

            var fidoUser = new Fido2User
            {
                Id          = Encoding.UTF8.GetBytes(userEmail),
                Name        = userEmail,
                DisplayName = userEmail
            };

            var authenticatorSelection = new AuthenticatorSelection
            {
                RequireResidentKey      = RequireResidentKey,
                UserVerification        = UserVerification,
                AuthenticatorAttachment = null // platform and cross-platform
            };

            var exts = new AuthenticationExtensionsClientInputs
            {
                Extensions            = true,
                UserVerificationIndex = true,
                Location = true,
                UserVerificationMethod = true,
                BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds
                {
                    FAR = float.MaxValue,
                    FRR = float.MaxValue
                }
            };

            var credentials = await GetCredentialsByUserEmail(userEmail);

            var options = _lib.RequestNewCredential(fidoUser, credentials.Select(x => x.Descriptor).ToList(), authenticatorSelection, AttestationType, exts);

            var attestationResponseJson = await jsRuntime.InvokeAsync <string>("createCredentials", TimeSpan.FromMinutes(3), JsonConvert.SerializeObject(options, new JsonSerializerSettings {
                Converters = new List <JsonConverter> {
                    new StringEnumConverter()
                }
            }));

            var attestationResponse = JsonConvert.DeserializeObject <AuthenticatorAttestationRawResponse>(attestationResponseJson);

            // Create callback so that lib can verify credential id is unique to this user
            IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
            {
                var users = await GetUsersByCredentialIdAsync(args.CredentialId);

                if (users.Count > 0)
                {
                    return(false);
                }

                return(true);
            };

            // Verify and make the credentials
            var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback);

            // Store the credentials in db
            return(await _fidoCredentialsRepository.AddAsync(new FidoStoredCredential
            {
                UserId = options.User.Id,
                Username = options.User.Name,
                SecurityKeyName = $"Security Key",
                Descriptor = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                PublicKey = success.Result.PublicKey,
                UserHandle = success.Result.User.Id,
                SignatureCounter = success.Result.Counter,
                CredType = success.Result.CredType,
                RegDate = DateTime.UtcNow,
                AaGuid = success.Result.Aaguid
            }));
        }
Beispiel #8
0
 public async Task AddCredentialToUser(Fido2User user, FidoStoredCredential credential)
 {
     credential.UserId = user.Id;
     _applicationDbContext.FidoStoredCredential.Add(credential);
     await _applicationDbContext.SaveChangesAsync();
 }
 public IsCredentialIdUniqueToUserParams(byte[] credentialId, Fido2User user)
 {
     CredentialId = credentialId;
     User         = user;
 }
Beispiel #10
0
 public void AddCredentialToUser(Fido2User user, StoredCredential credential)
 {
     credential.UserId = user.Id;
     _storedCredentials.Add(credential);
 }
Beispiel #11
0
 public List <StoredCredential> GetCredentialsByUser(Fido2User user)
 {
     return(_storedCredentials.Where(c => c.UserId.SequenceEqual(user.Id)).ToList());
 }
        public async Task <JsonResult> MakeCredentialOptions(
            [FromForm] string attType,
            [FromForm] string authType,
            [FromForm] bool requireResidentKey,
            [FromForm] string userVerification)
        {
            try
            {
                var identityUser = await _userManager.GetUserAsync(User);

                if (identityUser == null)
                {
                    throw new Exception("Unable to retrieve user.");
                }

                var fido2User = new Fido2User
                {
                    DisplayName = identityUser.UserName,
                    Name        = identityUser.UserName,
                    Id          = UTF8Encoding.UTF8.GetBytes(identityUser.UserName)
                };

                var fido2StoredCredentials = await _fido2Service.GetFido2StoredCredentialsByUserNameAsync(identityUser.UserName);

                var existingKeys = new List <PublicKeyCredentialDescriptor>();
                foreach (var fido2StoredCredential in fido2StoredCredentials)
                {
                    existingKeys.Add(fido2StoredCredential.Descriptor);
                }
                var authenticatorSelections = new AuthenticatorSelection
                {
                    RequireResidentKey = requireResidentKey,
                    UserVerification   = userVerification.ToEnum <UserVerificationRequirement>()
                };
                if (!string.IsNullOrEmpty(authType))
                {
                    authenticatorSelections.AuthenticatorAttachment = authType.ToEnum <AuthenticatorAttachment>();
                }
                var exts = new AuthenticationExtensionsClientInputs()
                {
                    Extensions            = true,
                    UserVerificationIndex = true,
                    Location = true,
                    UserVerificationMethod = true,
                    BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds {
                        FAR = float.MaxValue, FRR = float.MaxValue
                    }
                };
                var options = _fido2.RequestNewCredential(fido2User, existingKeys, authenticatorSelections, attType.ToEnum <AttestationConveyancePreference>(), exts);

                if (options.Status != "ok")
                {
                    throw new Exception("Failed to create options.");
                }

                var uniqueId = Guid.NewGuid().ToString();
                UniqueId = uniqueId;

                await _distributedCache.SetStringAsync(uniqueId, options.ToJson());

                return(Json(options));
            }
            catch (Exception exception)
            {
                return(Json(new CredentialCreateOptions()
                {
                    Status = "error", ErrorMessage = CommonFunctions.FormatException(exception)
                }));
            }
        }
Beispiel #13
0
        public async Task <IActionResult> InitialAuthDeviceRegistration()
        {
            var currentUserMaybe = this._currentUserService.CurrentUser;

            if (currentUserMaybe.HasNoValue)
            {
                return(this.Json(new CredentialCreateOptions {
                    Status = "error"
                }));
            }

            try
            {
                var fidoUser = new Fido2User
                {
                    Name        = currentUserMaybe.Value.Username,
                    DisplayName = currentUserMaybe.Value.Username,
                    Id          = currentUserMaybe.Value.UserId.ToByteArray(),
                };

                var publicKeyCredentialDescriptors = new List <PublicKeyCredentialDescriptor>();
                var data = await this._userQueries.GetDeviceInfoForCurrentUser(CancellationToken.None);

                if (data.HasValue)
                {
                    publicKeyCredentialDescriptors =
                        data.Value.Entities.Select(x => new PublicKeyCredentialDescriptor(x.CredentialId)).ToList();
                }

                var authenticatorSelection = new AuthenticatorSelection
                {
                    RequireResidentKey      = false,
                    UserVerification        = UserVerificationRequirement.Preferred,
                    AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform,
                };

                var authenticationExtensionsClientInputs = new AuthenticationExtensionsClientInputs
                {
                    Extensions            = true,
                    UserVerificationIndex = true,
                    Location = true,
                    UserVerificationMethod = true,
                    BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds
                    {
                        FAR = float.MaxValue,
                        FRR = float.MaxValue,
                    },
                };

                var options = this._fido2.RequestNewCredential(fidoUser, publicKeyCredentialDescriptors,
                                                               authenticatorSelection, AttestationConveyancePreference.None, authenticationExtensionsClientInputs);

                this.TempData["CredentialData"] = options.ToJson();
                return(this.Json(options));
            }
            catch (Fido2VerificationException e)
            {
                return(this.Json(new CredentialCreateOptions
                {
                    Status = "error", ErrorMessage = this.FormatException(e)
                }));
            }
        }
        public async Task <ActionResult <CredentialCreateOptions> > MakeCredentialOptions(CredentialOption option)
        {
            try
            {
                // 註冊使用者
                var user = await context.Members.Where(x => x.UserName == option.UserName).FirstOrDefaultAsync();

                if (user == null)
                {
                    user = new Member
                    {
                        MemberId    = Guid.NewGuid(),
                        UserName    = option.UserName,
                        DisplayName = option.DisplayName,
                        UserId      = Encoding.UTF8.GetBytes(option.UserName)
                    };
                    context.Members.Add(user);
                    await context.SaveChangesAsync();
                }
                var fidoUser = new Fido2User
                {
                    DisplayName = user.DisplayName,
                    Name        = user.UserName,
                    Id          = user.UserId
                };
                // 取得 Key. 排除已經註冊過的 Credentials
                var existingKeys = await context.StoredCredentials.Where(x => x.UserId == user.UserId).Select(x => x.Descriptor).ToListAsync();

                // 建立 Option
                var authenticatorSelection = new AuthenticatorSelection
                {
                    RequireResidentKey = option.RequireResidentKey,
                    UserVerification   = option.UserVerification.ToEnum <UserVerificationRequirement>()
                };
                if (!string.IsNullOrEmpty(option.AuthType))
                {
                    authenticatorSelection.AuthenticatorAttachment = option.AuthType.ToEnum <AuthenticatorAttachment>();
                }

                var exts = new AuthenticationExtensionsClientInputs()
                {
                    Extensions = true, UserVerificationIndex = true, Location = true, UserVerificationMethod = true, BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds {
                        FAR = float.MaxValue, FRR = float.MaxValue
                    }
                };

                var options = _fido2.RequestNewCredential(fidoUser, existingKeys, authenticatorSelection, option.AttType.ToEnum <AttestationConveyancePreference>(), exts);

                // Temporarily store options, session/in-memory cache/redis/db
                HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson());

                // 回傳
                return(Ok(options));
            }
            catch (Exception e)
            {
                return(BadRequest(new CredentialCreateOptions {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
Beispiel #15
0
        public ActionResult MakeCredentialOptions([FromForm] string attType, [FromForm] string authType, [FromForm] bool requireResidentKey, [FromForm] string userVerification)
        {
            var subjectId = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value;

            try
            {
                // 1. Get user from DB by username
                var dbUser = TestUsers.Users.FirstOrDefault(u => string.Equals(u.SubjectId, subjectId, StringComparison.InvariantCultureIgnoreCase));
                if (dbUser == null)
                {
                    return(BadRequest("no such user"));
                }
                var user = new Fido2User
                {
                    DisplayName = dbUser.Username,
                    Name        = dbUser.Username,
                    Id          = Encoding.UTF8.GetBytes(dbUser.Username)
                };

                // 2. Get user existing keys by username
                List <PublicKeyCredentialDescriptor> existingKeys = TestUsers.FidoCredentials.Where(c => c.UserId.SequenceEqual(user.Id)).Select(c => c.Descriptor).ToList();

                // 3. Create options
                var options = _lib.RequestNewCredential(
                    user: user,
                    excludeCredentials: existingKeys,
                    authenticatorSelection: new AuthenticatorSelection
                {
                    RequireResidentKey      = requireResidentKey,
                    UserVerification        = userVerification.ToEnum <UserVerificationRequirement>(),
                    AuthenticatorAttachment = string.IsNullOrEmpty(authType) ? (AuthenticatorAttachment?)null : authType.ToEnum <AuthenticatorAttachment>(),
                },
                    attestationPreference: attType.ToEnum <AttestationConveyancePreference>(),
                    extensions: new AuthenticationExtensionsClientInputs()
                {
                    Extensions            = true,
                    UserVerificationIndex = true,
                    Location = true,
                    UserVerificationMethod = true,
                    BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds {
                        FAR = float.MaxValue, FRR = float.MaxValue
                    }
                });

                // 4. Temporarily store options, session/in-memory cache/redis/db
                var sessionId = Convert.ToBase64String(IdentityModel.CryptoRandom.CreateRandomKey(64));
                TestUsers.FidoAttestationOptions[sessionId] = options.ToJson();

                // 5. return options to client
                return(this.NewtonsoftJsonResult(new MakeCredentialOptionsResponse
                {
                    SessionId = sessionId,
                    Options = options,
                }));
            }
            catch (Exception e)
            {
                return(BadRequest(new CredentialCreateOptions {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
Beispiel #16
0
        public async Task <JsonResult> MakeCredentialOptions([FromForm] string username,
                                                             [FromForm] string displayName,
                                                             [FromForm] string attType,
                                                             [FromForm] string authType,
                                                             [FromForm] bool requireResidentKey,
                                                             [FromForm] string userVerification)
        {
            try
            {
                var applicationUser = await _userManager.GetUserAsync(HttpContext.User);

                // 1. Get user from DB by username (in our example, auto create missing users)
                var user = new Fido2User
                {
                    Id = Encoding.UTF8.GetBytes(applicationUser.Id) // byte representation of userID is required
                };

                // 2. Get user existing keys by username
                // var existingKeys = DemoStorage.GetCredentialsByUser(user).Select(c => c.Descriptor).ToList();
                var existingKeys = new List <PublicKeyCredentialDescriptor>();
                //_fido2CredentialService.GetCredentialsByUser(applicationUser).Select(c => c.Descriptor).ToList();

                // 3. Create options
                var authenticatorSelection = new AuthenticatorSelection
                {
                    RequireResidentKey = requireResidentKey,
                    UserVerification   = userVerification.ToEnum <UserVerificationRequirement>()
                };

                if (!string.IsNullOrEmpty(authType))
                {
                    authenticatorSelection.AuthenticatorAttachment = authType.ToEnum <AuthenticatorAttachment>();
                }

                var exts = new AuthenticationExtensionsClientInputs()
                {
                    Extensions            = true,
                    UserVerificationIndex = true,
                    Location = true,
                    UserVerificationMethod = true,
                    BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds
                    {
                        FAR = float.MaxValue,
                        FRR = float.MaxValue
                    }
                };

                var options = _fido2.RequestNewCredential(user, existingKeys, authenticatorSelection, attType.ToEnum <AttestationConveyancePreference>(), exts);

                // 4. Temporarily store options, session/in-memory cache/redis/db
                HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson());

                // 5. return options to client
                return(Json(options));
            }
            catch (Exception e)
            {
                return(Json(new CredentialCreateOptions {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }