public Result <CredentialCreateOptions> MakeCredentialOptions(int userId, string username, string displayName, long loginId)
        {
            var user = new Fido2User
            {
                Name        = username,
                DisplayName = displayName,
                Id          = BitConverter.GetBytes(userId)
            };

            var authenticatorSelection = new AuthenticatorSelection
            {
                UserVerification = UserVerificationRequirement.Required
            };

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

            var options = _fido2.RequestNewCredential(user, null, authenticatorSelection, AttestationConveyancePreference.Direct);

            _cache.Set($"CredentialOptions:{loginId}", options, _options.Value.ChallengeExpiration);

            _logger.LogInformation($"Created options for login {loginId}");

            return(Result.Success(options));
        }
        public MakeCredentialOptionsResponse MakeCredentialOptions()
        {
            using (AuthLogic.Disable())
            {
                var existingKeys = Database.Query <WebAuthnCredentialEntity>()
                                   .Where(a => a.User.Is(UserEntity.Current))
                                   .Select(a => new PublicKeyCredentialDescriptor(a.CredentialId))
                                   .ToList();

                var authenticatorSelection = new AuthenticatorSelection
                {
                    RequireResidentKey = false, //For usernameless will be true
                    UserVerification   = UserVerificationRequirement.Preferred
                };

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

                var user = UserEntity.Current;

                var fido2User = new Fido2User
                {
                    DisplayName = user.UserName,
                    Name        = user.UserName,
                    Id          = Encoding.UTF8.GetBytes(user.Id.ToString())
                };

                var options = fido2.RequestNewCredential(fido2User, existingKeys, authenticatorSelection, AttestationConveyancePreference.None, exts);

                if (options.Status != "ok")
                {
                    throw new InvalidOperationException(options.ErrorMessage);
                }

                Database.Query <WebAuthnMakeCredentialsOptionsEntity>().Where(a => a.CreationDate < DateTime.Now.AddMonths(-1)).UnsafeDelete();

                var optionsEntity = new WebAuthnMakeCredentialsOptionsEntity
                {
                    User = user.ToLite(),
                    Json = options.ToJson()
                }.Save();

                return(new MakeCredentialOptionsResponse
                {
                    CredentialCreateOptions = options,
                    CreateOptionsId = (Guid)optionsEntity.Id,
                });
            }
        }
Example #3
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 user = new Fido2User
                {
                    DisplayName = displayName,
                    Name        = username,
                    Id          = Encoding.UTF8.GetBytes(username) // byte representation of userID is required
                };

                // 2. Get user existing keys by username
                var items = await _fido2Storage.GetCredentialsByUsername(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());

                // 5. return options to client
                return(Json(options));
            }
            catch (Exception e)
            {
                return(Json(new CredentialCreateOptions {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
Example #4
0
 public PublicKeyforCreate()
 {
     authenticatorSelection = new AuthenticatorSelection();
     attestation            = "";
     timeout   = 0;
     challenge = new byte[0];
     pin       = "";
 }
        public async Task <Result <InitiateAuthenticatorDeviceEnrollmentCommandResult, ErrorData> > Handle(
            InitiateAuthenticatorDeviceEnrollmentCommand request, CancellationToken cancellationToken)
        {
            var currentUserMaybe = this._currentAuthenticatedUserProvider.CurrentAuthenticatedUser;

            if (currentUserMaybe.HasNoValue)
            {
                return(Result.Fail <InitiateAuthenticatorDeviceEnrollmentCommandResult, ErrorData>(
                           new ErrorData(ErrorCodes.UserNotFound)));
            }

            var userMaybe =
                await this._userRepository.Find(currentUserMaybe.Value.UserId, cancellationToken);

            if (userMaybe.HasNoValue)
            {
                return(Result.Fail <InitiateAuthenticatorDeviceEnrollmentCommandResult, ErrorData>(
                           new ErrorData(ErrorCodes.UserNotFound)));
            }

            var user = userMaybe.Value;

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

            var publicKeyCredentialDescriptors =
                user.AuthenticatorDevices.Where(x => !x.IsRevoked).Select(x => new PublicKeyCredentialDescriptor(x.CredentialId)).ToList();

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

            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);

            return(Result.Ok <InitiateAuthenticatorDeviceEnrollmentCommandResult, ErrorData>(
                       new InitiateAuthenticatorDeviceEnrollmentCommandResult(options)));
        }
        public 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})";
                }

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

                // 2. Get user existing keys by username
                var existingKeys = DemoStorage.GetCredentialsByUser(user).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,
                    UserVerificationMethod = true,
                };

                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)
                }));
            }
        }
Example #7
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 authenticatorSelection = new AuthenticatorSelection
            {
                AuthenticatorAttachment = null,
                RequireResidentKey      = false,
                UserVerification        = UserVerificationRequirement.Discouraged
            };
            var options = _fido2.RequestNewCredential(fidoUser, excludeCredentials, authenticatorSelection, AttestationConveyancePreference.None);

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

            return(options);
        }
Example #8
0
        public async Task <IActionResult> MakeCredentialOptions([FromBody] MakeCredentialOptionsRequest request)
        {
            try
            {
                var user = _dataStore.AddUser(request.Username, () => new Fido2User
                {
                    DisplayName = request.DisplayName,
                    Name        = request.Username,
                    Id          = Encoding.UTF8.GetBytes(request.Username)
                });

                var existingKeys = _dataStore.GetCredentialsByUser(user).Select(c => c.Descriptor).ToList();

                var authenticatorSelection = new AuthenticatorSelection
                {
                    RequireResidentKey = request.RequireResidentKey,
                    UserVerification   = request.UserVerification.ToEnum <UserVerificationRequirement>()
                };

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

                var authenticationExtensionsClientInputs = 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, request.AttType.ToEnum <AttestationConveyancePreference>(), authenticationExtensionsClientInputs);

                _memoryCache.Set(Base64Url.Encode(options.Challenge), options.ToJson());

                var ev = new Event(request.Username, "Successfully made credential options", nameof(RegistrationController), nameof(MakeCredentialOptions));
                await _elasticClient.IndexAsync(ev, i => i.Index(GetIndexName(nameof(Ok))));

                return(Ok(options));
            }
            catch (Exception e)
            {
                var errorEvent = new ErrorEvent(e, request.Username, nameof(RegistrationController), nameof(MakeCredentialOptions));
                await _elasticClient.IndexAsync(errorEvent, i => i.Index(GetIndexName(nameof(Exception))));

                return(Ok(new CredentialCreateOptions {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
        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);
        }
Example #10
0
        public async Task <CredentialCreateOptions> RequestCreation(string userId)
        {
            await using var dbContext = _contextFactory.CreateContext();
            var user = await dbContext.Users.Include(applicationUser => applicationUser.Fido2Credentials)
                       .FirstOrDefaultAsync(applicationUser => applicationUser.Id == userId);

            if (user == null)
            {
                return(null);
            }

            // 2. Get user existing keys by username
            var existingKeys =
                user.Fido2Credentials
                .Where(credential => credential.Type == Fido2Credential.CredentialType.FIDO2)
                .Select(c => c.GetFido2Blob().Descriptor).ToList();

            // 3. Create options
            var authenticatorSelection = new AuthenticatorSelection
            {
                RequireResidentKey = false,
                UserVerification   = UserVerificationRequirement.Preferred
            };

            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(
                new Fido2User()
            {
                DisplayName = user.UserName, Name = user.UserName, Id = user.Id.ToBytesUTF8()
            },
                existingKeys, authenticatorSelection, AttestationConveyancePreference.None, exts);

            // options.Rp = new PublicKeyCredentialRpEntity(Request.Host.Host, options.Rp.Name, "");
            CreationStore.AddOrReplace(userId, options);
            return(options);
        }
Example #11
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));
        }
Example #12
0
        public void TestStringIsSerializable()
        {
            var x2 = new AuthenticatorSelection();

            x2.UserVerification = UserVerificationRequirement.Discouraged;
            var json = JsonConvert.SerializeObject(x2);
            var c3   = JsonConvert.DeserializeObject <AuthenticatorSelection>(json);

            Assert.Equal("discouraged", c3.UserVerification);
            Assert.Equal(UserVerificationRequirement.Discouraged, c3.UserVerification);

            Assert.NotEqual("required", c3.UserVerification);
            Assert.NotEqual(UserVerificationRequirement.Required, c3.UserVerification);

            Assert.True("discouraged" == UserVerificationRequirement.Discouraged);
            Assert.False("discouraged" != UserVerificationRequirement.Discouraged);

            Assert.False("required" == UserVerificationRequirement.Discouraged);
            Assert.True("required" != UserVerificationRequirement.Discouraged);

            // testing where string and membername mismatch

            var y1    = AuthenticatorAttachment.CrossPlatform;
            var yjson = JsonConvert.SerializeObject(y1);

            Assert.Equal("\"cross-platform\"", yjson);

            var y2 = JsonConvert.DeserializeObject <AuthenticatorAttachment>(yjson);

            Assert.Equal("cross-platform", y2);

            // test list of typedstrings
            var z1    = new[] { AuthenticatorTransport.Ble, AuthenticatorTransport.Usb, AuthenticatorTransport.Nfc };
            var zjson = JsonConvert.SerializeObject(z1);
            var z2    = JsonConvert.DeserializeObject <AuthenticatorTransport[]>(zjson);

            Assert.All(z2, (x) => z1.Contains(x));
            Assert.True(z1.SequenceEqual(z2));
        }
        public static string MakeWebAuthnAssertionRequest(Fido2Configuration fido2Config, byte[] challenge, List <PublicKeyCredentialDescriptor> allowedCredentials)
        {
            AuthenticatorSelection authSelection = new AuthenticatorSelection
            {
                RequireResidentKey = false,
                UserVerification   = UserVerificationRequirement.Preferred,
                //                AuthenticatorAttachment = null,
            };
            AuthenticationExtensionsClientInputs clientExtensions = new AuthenticationExtensionsClientInputs
            {
                Extensions = true,
                SimpleTransactionAuthorization = string.Format("you are registering to {0}", fido2Config.ServerName),
                Location = true,
                UserVerificationMethod = true,
            };

            var fido2 = new Fido2(fido2Config);

            var    assertionRequest     = Fido2NetLib.AssertionOptions.Create(fido2Config, challenge, allowedCredentials, UserVerificationRequirement.Preferred, clientExtensions);
            string assertionRequestJson = assertionRequest.ToJson();

            return(assertionRequestJson);
        }
Example #14
0
        public JsonResult MakeCredentialOptions([FromForm] string username, [FromForm] string attType, [FromForm] string authType, [FromForm] bool requireResidentKey, [FromForm] string userVerification)
        {
            try
            {
                // 1. Get user from DB by username (in our example, auto create missing users)
                var user = DemoStorage.GetOrAddUser(username, () => new User
                {
                    DisplayName = "Display " + username,
                    Name        = username,
                    Id          = Encoding.UTF8.GetBytes(username) // byte representation of userID is required
                });

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

                // 3. Create options
                var authenticatorSelection = new AuthenticatorSelection
                {
                    AuthenticatorAttachment = !string.IsNullOrEmpty(authType) ? AuthenticatorAttachment.Parse(authType) : null,
                    RequireResidentKey      = requireResidentKey,
                    UserVerification        = UserVerificationRequirement.Parse(userVerification)
                };
                var options = _lib.RequestNewCredential(user, existingKeys, authenticatorSelection, AttestationConveyancePreference.Parse(attType));

                // 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)
                }));
            }
        }
        public async Task <IActionResult> GetCredentialOptions(string authenticatorAttachment, string attestationType, bool requireResidentKey, string userVerification)
        {
            ViewData["Controller"] = "Account";
            ViewData["Action"]     = nameof(ChangePassword);

            var user = await GetCurrentLocalUserAsync();

            // 2. Get user existing keys by username
            var existingKeys = await _auth.GetCredentialsForUser(user.Id);

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

            if (!string.IsNullOrEmpty(authenticatorAttachment))
            {
                authenticatorSelection.AuthenticatorAttachment = authenticatorAttachment.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(new Fido2User()
            {
                Id          = BitConverter.GetBytes(user.Id),
                DisplayName = user.DisplayName,
                Name        = user.UserName,
            }, existingKeys, authenticatorSelection, attestationType.ToEnum <AttestationConveyancePreference>(), exts);

            // 5. return options to client
            return(Json(new {
                attestation = options.Attestation.ToString().ToLower(),
                authenticatorSelection = new {
                    authenticatorAttachment = CustomValue(options.AuthenticatorSelection.AuthenticatorAttachment),
                    requireResidentKey = options.AuthenticatorSelection.RequireResidentKey,
                    userVerification = options.AuthenticatorSelection.UserVerification.ToString().ToLower(),
                },
                challenge = options.Challenge,//TrimEnd().Replace('+', '-').Replace('/', '_'),
                excludeCredentials = options.ExcludeCredentials,
                extensions = options.Extensions,
                error = options.ErrorMessage,
                rp = options.Rp,
                status = options.Status,
                user = options.User,
                pubKeyCredParams = options.PubKeyCredParams.Select(k => new {
                    type = "public-key", // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions/pubKeyCredParams
                    alg = k.Alg
                }).ToArray(),
                timeout = options.Timeout,
            }));
        }
        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)
                }));
            }
        }
Example #17
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
            }));
        }
Example #18
0
        public JsonResult MakeCredentialOptions([FromForm] string username,
                                                [FromForm] string displayName,
                                                [FromForm] string attType,
                                                [FromForm] string authType,
                                                [FromForm] bool requireResidentKey,
                                                [FromForm] string userVerification)
        {
            try
            {
                // user must already exist in Identity
                var identityUser = _users.FindByUsername(username);
                if (identityUser == null)
                {
                    throw new Exception("User not found");
                }

                if (!HttpContext.User.IsAuthenticated())
                {
                    throw new Exception("User is not authenticated");
                }
                ;

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

                // 2. Get user existing keys by username
                var existingKeys = PasswordlessStore.GetCredentialsByUser(user).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)
                }));
            }
        }
Example #19
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 <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)
                }));
            }
        }
Example #21
0
        static void Main(string[] args)
        {
            Console.WriteLine($"webauthn.dll API version: {WebAuthnApi.GetApiVersionNumber()}");
            WebAuthnApi.IsUserVerifyingPlatformAuthenticatorAvailable(out var vpa);
            Console.WriteLine($"Verifying Platform Authenticator available: {vpa}");

            var config = new Fido2.Configuration
            {
                ChallengeSize = 32,
                Origin        = "https://foo.net/bar",
                ServerDomain  = "foo.net",
                ServerName    = "Foo Server"
            };
            var user1 = new User {
                Name = "Testuser1", Id = new byte[32]
            };

            new Random().NextBytes(user1.Id);

            var fido2NetLib = new Fido2(config);
            var excl        = new List <F2.PublicKeyCredentialDescriptor>();

            var selection = new AuthenticatorSelection
            {
                AuthenticatorAttachment = F2.AuthenticatorAttachment.CrossPlatform,
                RequireResidentKey      = false,
                UserVerification        = F2.UserVerificationRequirement.Discouraged
            };

            //generate valid Fido2 request with Fido2Net
            var f2req = fido2NetLib.RequestNewCredential(user1, excl, selection, F2.AttestationConveyancePreference.Direct);

            //translate objects into our own types
            var rp = new RelayingPartyInfo {
                Id = f2req.Rp.Id, Name = f2req.Rp.Name
            };
            var user = new UserInfo {
                UserId = f2req.User.Id, Name = f2req.User.Name, DisplayName = f2req.User.DisplayName
            };

            var json = $"{{\r\n\t\"type\" : \"webauthn.create\",\r\n\t\"challenge\" : \"{Convert.ToBase64String(f2req.Challenge)}\",\r\n\t\"origin\" : \"{config.Origin}\"\r\n}}";

            var clientData = new ClientData {
                ClientDataJSON = Encoding.UTF8.GetBytes(json), HashAlgorithm = HashAlgorithm.Sha256
            };
            var coseParams  = f2req.PubKeyCredParams.Select(p => new CoseCredentialParameter((CoseAlgorithm)p.Alg)).ToList();
            var makeOptions = new AuthenticatorMakeCredentialOptions
            {
                TimeoutMilliseconds             = (int)f2req.Timeout,
                UserVerificationRequirement     = (UserVerificationRequirement)f2req.AuthenticatorSelection.UserVerification + 1,
                AuthenticatorAttachment         = ((AuthenticatorAttachment?)f2req.AuthenticatorSelection.AuthenticatorAttachment + 1) ?? AuthenticatorAttachment.Any,
                RequireResidentKey              = f2req.AuthenticatorSelection.RequireResidentKey,
                AttestationConveyancePreference = (AttestationConveyancePreference)f2req.Attestation + 1,
                ExcludeCredentialsEx            = f2req.ExcludeCredentials
                                                  .Select(ec => new CredentialEx(ec.Id,
                                                                                 ec.Transports.Aggregate(CtapTransport.NoRestrictions, (acc, n) => acc | (CtapTransport)(1 << (int)n)))).ToList()
            };

            //call WinAPI
            var windowHandle = Process.GetCurrentProcess().MainWindowHandle;
            var res          = WebAuthnApi.AuthenticatorMakeCredential(windowHandle, rp, user, coseParams, clientData, makeOptions, out var credential);

            if (res != WebAuthnResult.Ok)
            {
                Console.WriteLine(WebAuthnApi.GetErrorName(res));
                return;
            }

            //translate back and verify
            var resp = new AuthenticatorAttestationRawResponse
            {
                Id       = credential.CredentialId,
                RawId    = credential.CredentialId,
                Response = new AuthenticatorAttestationRawResponse.ResponseData
                {
                    AttestationObject = credential.AttestationObject,
                    ClientDataJson    = Encoding.UTF8.GetBytes(json)
                },
                Type = F2.PublicKeyCredentialType.PublicKey
            };
            var makeRes = fido2NetLib.MakeNewCredentialAsync(resp, f2req, _ => Task.FromResult(true)).Result;

            Console.WriteLine($"Register Result: {makeRes.Result.Status}");
            Console.WriteLine($"Attestation CN: {credential.CommonAttestation.Certificates.FirstOrDefault()?.Subject}");


            for (; ;)
            {
                //--------------------- Assertion ----------------


                var allowed = new List <F2.PublicKeyCredentialDescriptor>
                {
                    new F2.PublicKeyCredentialDescriptor(credential.CredentialId)
                };

                //generate valid Fido2 request with Fido2Net
                var f2reqA = fido2NetLib.GetAssertionOptions(allowed, F2.UserVerificationRequirement.Discouraged);

                //translate objects into our own types

                var jsonA =
                    $"{{\r\n\t\"type\" : \"webauthn.get\",\r\n\t\"challenge\" : \"{Convert.ToBase64String(f2reqA.Challenge)}\",\r\n\t\"origin\" : \"{config.Origin}\"\r\n}}";

                var clientDataA = new ClientData
                {
                    ClientDataJSON = Encoding.UTF8.GetBytes(jsonA),
                    HashAlgorithm  = HashAlgorithm.Sha256
                };

                WebAuthnApi.GetCancellationId(out var cancelId);

                var getOptions = new AuthenticatorGetAssertionOptions()
                {
                    CancellationId              = cancelId,
                    TimeoutMilliseconds         = (int)f2reqA.Timeout,
                    UserVerificationRequirement = (UserVerificationRequirement)f2reqA.UserVerification + 1,
                    AllowedCredentialsEx        = f2reqA.AllowCredentials
                                                  .Select(ec => new CredentialEx(ec.Id,
                                                                                 ec.Transports?.Aggregate(CtapTransport.NoRestrictions,
                                                                                                          (acc, n) => acc | (CtapTransport)(1 << (int)n))
                                                                                 ?? CtapTransport.NoRestrictions)).ToList()
                };


                //call WinAPI
                Console.WriteLine("Canceling Assertion in 5sec...");
                Task.Run(async() =>
                {
                    await Task.Delay(5000);
                    WebAuthnApi.CancelCurrentOperation(cancelId);
                });


                res = WebAuthnApi.AuthenticatorGetAssertion(windowHandle, config.ServerDomain, clientDataA, getOptions, out var assertion);
                if (res != WebAuthnResult.Ok)
                {
                    if (res == WebAuthnResult.Canceled)
                    {
                        Console.WriteLine("Canceled");
                        Console.ReadKey();
                        continue;
                    }

                    Console.WriteLine($"0x{(int)res:X} " + WebAuthnApi.GetErrorName(res));
                    break;
                }

                //translate back and verify
                var respA = new AuthenticatorAssertionRawResponse()
                {
                    Id       = assertion.Credential.CredentialId,
                    RawId    = assertion.Credential.CredentialId,
                    Type     = F2.PublicKeyCredentialType.PublicKey,
                    Response = new AuthenticatorAssertionRawResponse.AssertionResponse()
                    {
                        AuthenticatorData = assertion.AuthenticatorData,
                        Signature         = assertion.Signature,
                        UserHandle        = assertion.UserId,
                        ClientDataJson    = Encoding.UTF8.GetBytes(jsonA)
                    },
                };
                var getResult = fido2NetLib.MakeAssertionAsync(respA, f2reqA, makeRes.Result.PublicKey, makeRes.Result.Counter,
                                                               _ => Task.FromResult(true)).Result;
                Console.WriteLine($"Assertion Result: {getResult.Status}");

                Console.WriteLine("Press any key to test assertion again");
                Console.ReadKey();
            }

            Console.ReadKey();
        }
Example #22
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)
                }));
            }
        }