Beispiel #1
0
        public ActionResult AssertionOptionsPost([FromForm] string username, [FromForm] string userVerification)
        {
            try
            {
                var existingCredentials = new List <PublicKeyCredentialDescriptor>();

                if (!string.IsNullOrEmpty(username))
                {
                    // 1. Get user from DB
                    var user = PasswordlessStore.GetUser(username);
                    if (user == null)
                    {
                        throw new ArgumentException("Username was not registered");
                    }

                    // 2. Get registered credentials from database
                    existingCredentials = PasswordlessStore.GetCredentialsByUser(user).Select(c => c.Descriptor).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 = _fido2.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 #2
0
        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, 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)
                }));
            }
        }
        public AssertionOptionsResponse GetAssertionOptions([FromBody][Required] AssertionOptionsRequest request)
        {
            using (AuthLogic.Disable())
            {
                var existingCredentials = new List <PublicKeyCredentialDescriptor>();

                if (!string.IsNullOrEmpty(request.UserName))
                {
                    existingCredentials = Database.Query <WebAuthnCredentialEntity>()
                                          .Where(a => a.User.Entity.UserName == request.UserName)
                                          .Select(a => new PublicKeyCredentialDescriptor(a.CredentialId))
                                          .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      = UserVerificationRequirement.Discouraged;
                var options = fido2.GetAssertionOptions(
                    existingCredentials,
                    uv,
                    exts
                    );

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

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

                var optionsEntity = new WebAuthnAssertionOptionsEntity
                {
                    Json = options.ToJson()
                }.Save();

                return(new AssertionOptionsResponse
                {
                    AssertionOptions = options,
                    AssertionOptionsId = (Guid)optionsEntity.Id
                });
            }
        }
        public IActionResult AssertionOptionsTest([FromBody] TEST_AssertionClientParams assertionClientParams)
        {
            var username = assertionClientParams.Username;
            // 1. Get user from DB
            var user = DemoStorage.GetUser(username);

            if (user == null)
            {
                return(NotFound("username was not registered"));
            }

            // 2. Get registered credentials from database
            var existingCredentials = DemoStorage.GetCredentialsByUser(user).Select(c => c.Descriptor).ToList();

            var uv = assertionClientParams.UserVerification;

            if (null != assertionClientParams.authenticatorSelection)
            {
                uv = assertionClientParams.authenticatorSelection.UserVerification;
            }

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

            if (null != assertionClientParams.Extensions && null != assertionClientParams.Extensions.Example)
            {
                exts.Example = assertionClientParams.Extensions.Example;
            }

            // 3. Create options
            var options = _fido2.GetAssertionOptions(
                existingCredentials,
                uv,
                exts
                );

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

            // 5. Return options to client
            return(Json(options));
        }
Beispiel #5
0
        public async Task <ActionResult> AssertionOptionsPost([FromForm] string username, [FromForm] string userVerification)
        {
            try
            {
                var existingCredentials = new List <PublicKeyCredentialDescriptor>();

                // 1. Get user from DB
                var signInUser = await _signInManager.GetTwoFactorAuthenticationUserAsync();

                var applicationUser = await _userManager.FindByNameAsync(signInUser.UserName);



                // 2. Get registered credentials from database
                var creds = await _fido2CredentialService.GetCredentialsByUser(applicationUser);

                existingCredentials = creds.Select(c => new PublicKeyCredentialDescriptor(c.Descriptor)).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 = _fido2.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)
                }));
            }
        }
        public async Task <ActionResult> AssertionOptions([FromForm] string email, [FromForm] string userVerification)
        {
            try
            {
                var user = !string.IsNullOrEmpty(email) ? await _userManager.FindByEmailAsync(email) : null;

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

                var existingCredentials = await _auth.GetCredentialsForUser(user.Id);

                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 = _fido2.GetAssertionOptions(
                    existingCredentials,
                    uv,
                    exts
                    );

                // 5. Return options to client
                return(Json(new {
                    allowCredentials = options.AllowCredentials,
                    challenge = options.Challenge,
                    extensions = options.Extensions,
                    error = options.ErrorMessage,
                    status = options.Status,
                    timeout = options.Timeout,
                    userVerification = options.UserVerification?.ToString()?.ToLower()
                }));
            }
            catch (Exception e)
            {
                return(Json(new AssertionOptions {
                    Status = "error", ErrorMessage = e.Message
                }));
            }
        }
Beispiel #7
0
        /// <summary>
        /// Returns AssertionOptions including a challenge to the browser/authr to assert existing credentials and authenticate a user.
        /// </summary>
        /// <returns></returns>
        public AssertionOptions GetAssertionOptions(
            IEnumerable <PublicKeyCredentialDescriptor> allowedCredentials,
            UserVerificationRequirement?userVerification,
            AuthenticationExtensionsClientInputs extensions = null)
        {
            var challenge = new byte[_config.ChallengeSize];

            _crypto.GetBytes(challenge);

            var options = AssertionOptions.Create(_config, challenge, allowedCredentials, userVerification, extensions);

            return(options);
        }
Beispiel #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)
                }));
            }
        }
Beispiel #9
0
        /// <summary>
        /// Returns CredentialCreateOptions including a challenge to be sent to the browser/authr to create new credentials
        /// </summary>
        /// <returns></returns>
        /// <param name="attestationPreference">This member is intended for use by Relying Parties that wish to express their preference for attestation conveyance. The default is none.</param>
        /// <param name="excludeCredentials">Recommended. This member is intended for use by Relying Parties that wish to limit the creation of multiple credentials for the same account on a single authenticator.The client is requested to return an error if the new credential would be created on an authenticator that also contains one of the credentials enumerated in this parameter.</param>
        public CredentialCreateOptions RequestNewCredential(
            Fido2User user,
            List <PublicKeyCredentialDescriptor> excludeCredentials,
            AuthenticatorSelection authenticatorSelection,
            AttestationConveyancePreference attestationPreference,
            AuthenticationExtensionsClientInputs extensions = null)
        {
            var challenge = new byte[_config.ChallengeSize];

            _crypto.GetBytes(challenge);

            var options = CredentialCreateOptions.Create(_config, challenge, user, authenticatorSelection, attestationPreference, excludeCredentials, extensions);

            return(options);
        }
Beispiel #10
0
        public async Task <IActionResult> AssertionOptions([FromForm] string username, [FromForm] string userVerification)
        {
            try
            {
                var existingCredentials = new List <PublicKeyCredentialDescriptor>();

                if (!string.IsNullOrEmpty(username))
                {
                    var user = _dataStore.GetUser(username);
                    if (user == null)
                    {
                        throw new ArgumentException("Username was not registered");
                    }

                    existingCredentials = _dataStore.GetCredentialsByUser(user).Select(c => c.Descriptor).ToList();
                }

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

                var uv      = string.IsNullOrEmpty(userVerification) ? UserVerificationRequirement.Required : userVerification.ToEnum <UserVerificationRequirement>();
                var options = _lib.GetAssertionOptions(
                    existingCredentials,
                    uv,
                    authenticationExtensionsClientInputs
                    );

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

                var ev = new Event(username, "Successfully made an options for assertion", nameof(AuthenticationController), nameof(AssertionOptions));
                await _elasticClient.IndexAsync(ev, i => i.Index(GetIndexName(nameof(Ok))));

                return(Ok(options));
            }

            catch (Exception e)
            {
                var errorEvent = new ErrorEvent(e, username, nameof(AuthenticationController), nameof(AssertionOptions));
                await _elasticClient.IndexAsync(errorEvent, i => i.Index(GetIndexName(nameof(Exception))));

                return(Ok(new AssertionOptions {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
Beispiel #11
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);
        }
        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 #13
0
        /// <summary>
        /// GetLoginAssertionsOptions method implementation
        /// </summary>
        private string GetLoginAssertionsOptions(AuthenticationContext ctx)
        {
            try
            {
                List <MFAPublicKeyCredentialDescriptor> existingCredentials = new List <MFAPublicKeyCredentialDescriptor>();

                if (!string.IsNullOrEmpty(ctx.UPN))
                {
                    var user = RuntimeRepository.GetUser(Config, ctx.UPN);
                    if (user == null)
                    {
                        throw new ArgumentException("Username was not registered");
                    }
                    existingCredentials = RuntimeRepository.GetCredentialsByUser(Config, user).Select(c => c.Descriptor).ToList();
                }

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

                UserVerificationRequirement uv      = this.UserVerificationRequirement.ToEnum <UserVerificationRequirement>();
                AssertionOptions            options = _webathn.GetAssertionOptions(existingCredentials.ToCore(), uv, exts);
                string result = options.ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), EventLogEntryType.Error, 5000);
                string result = (new AssertionOptions {
                    Status = "error", ErrorMessage = string.Format("{0}{1}", e.Message, e.InnerException != null ? " (" + e.InnerException.Message + ")" : "")
                }).ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
        }
Beispiel #14
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));
        }
        public JsonResult MakeCredentialOptionsTest([FromBody] TEST_MakeCredentialParams opts)
        {
            var attType = opts.Attestation;

            var username = new byte[] { };

            try
            {
                username = Base64Url.Decode(opts.Username);
            }
            catch (FormatException)
            {
                username = Encoding.UTF8.GetBytes(opts.Username);
            }

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

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

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

            if (null != opts.Extensions && null != opts.Extensions.Example)
            {
                exts.Example = opts.Extensions.Example;
            }

            // 3. Create options
            var options = _fido2.RequestNewCredential(user, existingKeys, opts.AuthenticatorSelection, opts.Attestation, exts);

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

            // 5. return options to client
            return(Json(options));
        }
        /// <summary>
        /// GetLoginAssertionsOptions method implementation
        /// </summary>
        private string GetLoginAssertionsOptions(AuthenticationContext ctx)
        {
            try
            {
                List <MFAPublicKeyCredentialDescriptor> existingCredentials = new List <MFAPublicKeyCredentialDescriptor>();
                if (!string.IsNullOrEmpty(ctx.UPN))
                {
                    var user = RuntimeRepository.GetUser(Config, ctx.UPN);
                    if (user == null)
                    {
                        throw new ArgumentException("Username was not registered");
                    }
                    existingCredentials = RuntimeRepository.GetCredentialsByUser(Config, user).Select(c => c.Descriptor).ToList();
                }

                AuthenticationExtensionsClientInputs exts = new AuthenticationExtensionsClientInputs()
                {
                    Extensions             = this.Extentions,
                    UserVerificationMethod = this.UserVerificationMethod,
                };

                UserVerificationRequirement uv      = this.UserVerificationRequirement;
                AssertionOptions            options = null;
                if (existingCredentials.Count > 0)
                {
                    options = _webathn.GetAssertionOptions(existingCredentials.ToCore(), uv, exts);
                }
                else
                {
                    options = _webathn.GetAssertionOptions(null, uv, exts);
                }

                string result = options.ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
            catch (Exception e)
            {
                Log.WriteEntry(string.Format("{0}\r\n{1}", ctx.UPN, e.Message), EventLogEntryType.Error, 5000);
                string result = (new AssertionOptions {
                    Status = "error", ErrorMessage = string.Format("{0} / {1}", e.Message, e.InnerException != null ? " (" + e.InnerException.Message + ")" : "")
                }).ToJson();
                ctx.AssertionOptions = result;
                return(result);
            }
        }
Beispiel #17
0
        public IActionResult AssertionOptionsTest([FromBody] TEST_AssertionClientParams assertionClientParams)
        {
            var username = assertionClientParams.Username;
            // 1. Get user from DB
            var user = DemoStorage.GetUser(username);

            if (user == null)
            {
                return(NotFound("username was not registered"));
            }

            // 2. Get registered credentials from database
            var existingCredentials = DemoStorage.GetCredentialsByUser(user).Select(c => c.Descriptor).ToList();

            var uv = assertionClientParams.UserVerification;

            if (null != assertionClientParams.authenticatorSelection)
            {
                uv = assertionClientParams.authenticatorSelection.UserVerification;
            }

            var exts = new AuthenticationExtensionsClientInputs()
            {
                Extensions = true, UserVerificationIndex = true, Location = true
            };

            if (null != assertionClientParams.Extensions &&
                null != assertionClientParams.Extensions.Example &&
                0 != assertionClientParams.Extensions.Example.Length)
            {
                exts.Example = assertionClientParams.Extensions.Example;
            }

            // 3. Create options
            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));
        }
Beispiel #18
0
        public async Task <ActionResult> AssertionOptionsPost()
        {
            var data = await this._userQueries.GetDeviceInfoForCurrentUser(CancellationToken.None);

            var existingCredentials =
                data.Value.Entities.Select(x => new PublicKeyCredentialDescriptor(x.CredentialId));

            try
            {
                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      = UserVerificationRequirement.Discouraged;
                var options = this._fido2.GetAssertionOptions(
                    existingCredentials,
                    uv,
                    exts);

                // 4. Temporarily store options, session/in-memory cache/redis/db
                this.TempData["fido2.assertionOptions"] = options.ToJson();

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

            catch (Fido2VerificationException e)
            {
                return(this.Json(new AssertionOptions {
                    Status = "error"
                }));
            }
        }
Beispiel #19
0
        private AssertionOptions MakeFido2AssertionOptions(User user, string paymentId)
        {
            var fidoLogins   = _authContext.FidoLogins.Where(x => x.UserId == user.Id);
            var existingKeys = new List <PublicKeyCredentialDescriptor>();

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

            //Getting data to sign
            var payment    = _authContext.Payments.First(x => x.Id == paymentId);
            var payloadObj = new
            {
                paymentId = payment.Id,
                sub       = payment.UserId,
            };
            var payloadStr = JsonConvert.SerializeObject(payloadObj);
            var payload    = Encoding.UTF8.GetBytes(payloadStr);

            var challenge = Fido2NetLib.Base64Url.Encode(payload);

            HttpContext.Session.SetString("fido2.assertionChallenge", challenge);
            HttpContext.Session.SetString("fido2.paymentId", paymentId);
            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
            };
            var uv      = UserVerificationRequirement.Preferred;
            var options = _lib.GetAssertionOptions(
                existingKeys,
                uv,
                exts
                );

            options.Challenge = Encoding.UTF8.GetBytes(challenge);

            // 4. Temporarily store options, session/in-memory cache/redis/db
            HttpContext.Session.SetString("fido2.assertionOptions", options.ToJson());
            return(options);
        }
Beispiel #20
0
        /// <summary>
        /// <para>注册:创建证明选项</para>
        /// <param name="opts"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public CredentialCreateOptions MakeCredentialOptions(User user, MakeCredentialParams opts)
        {
            var fidoUser = opts.GetFido2UserByUser();
            // Create options
            var exts = new AuthenticationExtensionsClientInputs()
            {
                Extensions             = true,
                UserVerificationMethod = true,
            };
            var existingKeys = GetPublicKeyCredentialDescriptors(user.UserId);

            var options = _fido2.RequestNewCredential(
                fidoUser,
                existingKeys,
                opts.AuthenticatorSelection,
                opts.Attestation,
                exts);

            distributedCache.SetString(user.UserId.ToString() + "attestationOptions", options.ToJson(), 120);
            return(options);
        }
Beispiel #21
0
        public ActionResult AssertionOptionsPost([FromForm] string username)
        {
            try
            {
                // 1. Get user from DB
                var user = DemoStorage.GetUser(username);
                if (user == null)
                {
                    throw new ArgumentException("Username was not registered");
                }

                // 2. Get registered credentials from database
                var existingCredentials = DemoStorage.GetCredentialsByUser(user).Select(c => c.Descriptor).ToList();

                var exts = new AuthenticationExtensionsClientInputs()
                {
                    AppID = _origin, SimpleTransactionAuthorization = "demo!", UserVerificationIndex = true, Location = true
                };

                // 3. Create options
                var options = _lib.GetAssertionOptions(
                    existingCredentials,
                    UserVerificationRequirement.Discouraged,
                    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)
                }));
            }
        }
        public async Task <AssertionOptions> RequestLogin(string userId)
        {
            await using var dbContext = _contextFactory.CreateContext();
            var user = await dbContext.Users.Include(applicationUser => applicationUser.Fido2Credentials)
                       .FirstOrDefaultAsync(applicationUser => applicationUser.Id == userId);

            if (!(user?.Fido2Credentials?.Any() is true))
            {
                return(null);
            }
            var existingCredentials = user.Fido2Credentials
                                      .Where(credential => credential.Type == Fido2Credential.CredentialType.FIDO2)
                                      .Select(c => c.GetBlob().Descriptor)
                                      .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,
                Extensions             = true,
                AppID = _fido2Configuration.Origin
            };

            // 3. Create options
            var options = _fido2.GetAssertionOptions(
                existingCredentials,
                UserVerificationRequirement.Discouraged,
                exts
                );

            LoginStore.AddOrReplace(userId, options);
            return(options);
        }
Beispiel #23
0
        //public IActionResult Fido2LoginFailed()
        //{
        //    var returnUrl = HttpContext.Session.GetString("fido2.assertionOptions.returnUrl");
        //    var remLogStr = HttpContext.Session.GetString("fido2.assertionOptions.rememberLogin");
        //    var rememberLogin = string.IsNullOrEmpty(remLogStr) ? false : bool.Parse(remLogStr);

        //    return RedirectToAction("Fido2Login", new { returnUrl, rememberLogin });
        //}

        private async Task <AssertionOptions> PrepareFidoLogin(User user)
        {
            var claimsIdentity = new ClaimsIdentity();

            claimsIdentity.AddClaim(new Claim(JwtClaimTypes.Subject, user.Id));
            await HttpContext.SignInAsync(_fido2AuthenticationScheme, new ClaimsPrincipal(claimsIdentity));

            var fidoLogins = _authenticationContext.FidoLogins.Where(x => x.UserId == user.Id).Select(x => x.PublicKeyIdBytes);

            if (fidoLogins.Count() < 1)
            {
                return(null);
            }

            var existingKeys = new List <PublicKeyCredentialDescriptor>();

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

            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
            };
            var uv      = UserVerificationRequirement.Preferred;
            var options = _lib.GetAssertionOptions(
                existingKeys,
                uv,
                exts
                );

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

            return(options);
        }
        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);
        }
Beispiel #25
0
        public async Task <string> GenerateAsync(string purpose, UserManager <User> manager, User user)
        {
            var userService = _serviceProvider.GetRequiredService <IUserService>();

            if (!(await userService.CanAccessPremium(user)))
            {
                return(null);
            }

            var provider            = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);
            var keys                = LoadKeys(provider);
            var existingCredentials = keys.Select(key => key.Item2.Descriptor).ToList();

            if (existingCredentials.Count == 0)
            {
                return(null);
            }

            var exts = new AuthenticationExtensionsClientInputs()
            {
                UserVerificationMethod = true,
                AppID = CoreHelpers.U2fAppIdUrl(_globalSettings),
            };

            var options = _fido2.GetAssertionOptions(existingCredentials, UserVerificationRequirement.Discouraged, exts);

            // TODO: Remove this when newtonsoft legacy converters are gone
            provider.MetaData["login"] = JsonSerializer.Serialize(options);

            var providers = user.GetTwoFactorProviders();

            providers[TwoFactorProviderType.WebAuthn] = provider;
            user.SetTwoFactorProviders(providers);
            await userService.UpdateTwoFactorProviderAsync(user, TwoFactorProviderType.WebAuthn, logEvent : false);

            return(options.ToJson());
        }
    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)
            }));
        }
    }
 /// <summary>
 /// Returns CredentialCreateOptions including a challenge to be sent to the browser/authr to create new credentials
 /// </summary>
 /// <returns></returns>
 /// <param name="excludeCredentials">Recommended. This member is intended for use by Relying Parties that wish to limit the creation of multiple credentials for the same account on a single authenticator.The client is requested to return an error if the new credential would be created on an authenticator that also contains one of the credentials enumerated in this parameter.</param>
 public CredentialCreateOptions RequestNewCredential(Fido2User user, List <PublicKeyCredentialDescriptor> excludeCredentials, AuthenticationExtensionsClientInputs extensions = null)
 {
     return(RequestNewCredential(user, excludeCredentials, AuthenticatorSelection.Default, AttestationConveyancePreference.None, extensions));
 }
 internal static AssertionOptions Create(Fido2.Configuration config, byte[] challenge, IEnumerable <PublicKeyCredentialDescriptor> allowedCredentials, UserVerificationRequirement userVerification, AuthenticationExtensionsClientInputs extensions)
 {
     return(new AssertionOptions()
     {
         Status = "ok",
         ErrorMessage = string.Empty,
         Challenge = challenge,
         Timeout = config.Timeout,
         RpId = config.ServerDomain,
         AllowCredentials = allowedCredentials ?? new List <PublicKeyCredentialDescriptor>(),
         UserVerification = userVerification,
         Extensions = extensions
     });
 }
Beispiel #29
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 #30
0
 /// <summary>
 /// Create static method implementation
 /// </summary>
 public static RegisterCredentialOptions Create(Fido2Configuration config, byte[] challenge, Fido2User user, AuthenticatorSelection authenticatorSelection, AttestationConveyancePreference attestationConveyancePreference, List <PublicKeyCredentialDescriptor> excludeCredentials, AuthenticationExtensionsClientInputs extensions)
 {
     return(new RegisterCredentialOptions
     {
         Status = "ok",
         ErrorMessage = string.Empty,
         Challenge = challenge,
         Rp = new PublicKeyCredentialRpEntity(config.ServerDomain, config.ServerName, config.ServerIcon),
         Timeout = config.Timeout,
         User = user,
         PubKeyCredParams = new List <PubKeyCredParam>()
         {
             // Add additional as appropriate
             ES256,
             RS256,
             PS256,
             ES384,
             RS384,
             PS384,
             ES512,
             RS512,
             PS512,
         },
         AuthenticatorSelection = authenticatorSelection,
         Attestation = attestationConveyancePreference,
         ExcludeCredentials = excludeCredentials ?? new List <PublicKeyCredentialDescriptor>(),
         Extensions = extensions
     });
 }