示例#1
0
        /// <summary>
        /// 注册:验证用户凭证
        /// <para>当客户端返回响应时,我们验证并注册凭据。</para>
        /// </summary>
        /// <param name="attestationResponse"></param>
        public async Task <CredentialMakeResult> RegisterCredentials(User user, string fido2Name, AuthenticatorAttestationRawResponse attestationResponse)
        {
            // 1. get the options we sent the client
            var jsonOptions = distributedCache.GetString(user.UserId.ToString() + "attestationOptions");

            if (string.IsNullOrEmpty(jsonOptions))
            {
                return(null);
            }
            var options = CredentialCreateOptions.FromJson(jsonOptions);

            // 2. Create callback so that lib can verify credential id is unique to this user
            IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
            {
                //var users = await DemoStorage.GetUsersByCredentialIdAsync(args.CredentialId);
                //if (users.Count > 0)
                //    return false;
                var argUserId = args.CredentialId;
                if (this.dataContext.FIDO2Repository.Where(b => b.CredentialId.Equals(argUserId)).Any())
                {
                    return(false);
                }
                return(true);
            };
            // 2. Verify and make the credentials
            var success = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback);

            // 3. Store the credentials in db
            //var user = dataContext.User.Where(b => b.UserId == userId).FirstOrDefault();
            if (user == null)
            {
                return(null);
            }
            var fido2 = new FIDO2Item()
            {
                Id               = IdGenerator.NextId(),
                UserId           = user.UserId,
                FIDO2Name        = fido2Name,
                CredentialId     = success.Result.CredentialId,
                PublicKey        = success.Result.PublicKey,
                UserHandle       = success.Result.User.Id,
                SignatureCounter = success.Result.Counter,
                CredType         = success.Result.CredType,
                RegDate          = DateTime.Now,
                AaGuid           = success.Result.Aaguid
            };

            dataContext.FIDO2Repository.Add(fido2);

            dataContext.SaveChanges();

            return(success);
        }
        public async Task <ActionResult <CredentialMakeResult> > MakeCredential(AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                // 1. get the options we sent the client
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                // 2. Create callback so that lib can verify credential id is unique to this user
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    var credentialIdString = Base64Url.Encode(args.CredentialId);
                    var cred = await context.StoredCredentials.Where(x => x.DescriptorJson.Contains(credentialIdString)).FirstOrDefaultAsync();

                    if (cred == null)
                    {
                        return(true);
                    }
                    var users = await context.Members.Where(x => x.UserId == cred.UserId).Select(u => new Fido2User
                    {
                        DisplayName = u.DisplayName,
                        Name        = u.UserName,
                        Id          = u.UserId
                    }).ToListAsync();

                    return(users.Count == 0);
                };

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

                context.StoredCredentials.Add(new StoredCredential
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    UserId           = options.User.Id,
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });
                await context.SaveChangesAsync();

                return(Ok(success));
            }
            catch (Exception e)
            {
                return(BadRequest(new CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
        public async Task <CacheItem> ExecuteAsync(string fido2Payload, CacheItem relatedItem)
        {
            var request = OwnIdSerializer.Deserialize <Fido2RegisterRequest>(fido2Payload);

            if (string.IsNullOrWhiteSpace(request.AttestationObject))
            {
                throw new CommandValidationException("Incorrect Fido2 register request: AttestationObject is missing");
            }

            if (string.IsNullOrWhiteSpace(request.ClientDataJson))
            {
                throw new CommandValidationException("Incorrect Fido2 register request: ClientDataJson is missing");
            }

            var fido2Response = new AuthenticatorAttestationRawResponse
            {
                Id       = _encodingService.Base64UrlDecode(NewUserId),
                RawId    = _encodingService.Base64UrlDecode(NewUserId),
                Type     = PublicKeyCredentialType.PublicKey,
                Response = new AuthenticatorAttestationRawResponse.ResponseData
                {
                    AttestationObject = _encodingService.Base64UrlDecode(request.AttestationObject),
                    ClientDataJson    = _encodingService.Base64UrlDecode(request.ClientDataJson)
                }
            };

            var options = new CredentialCreateOptions
            {
                Challenge = _encodingService.ASCIIDecode(relatedItem.Context),
                Rp        = new PublicKeyCredentialRpEntity(
                    _configuration.Fido2.RelyingPartyId,
                    _configuration.Fido2.RelyingPartyName,
                    null),
                User = new Fido2User
                {
                    DisplayName = _configuration.Fido2.UserDisplayName,
                    Name        = _configuration.Fido2.UserName,
                    Id          = _encodingService.Base64UrlDecode(NewUserId)
                }
            };

            var result = await _fido2.MakeNewCredentialAsync(fido2Response, options, args => Task.FromResult(true));

            if (result == null)
            {
                throw new InternalLogicException("Cannot verify fido2 register request");
            }

            var publicKey = _encodingService.Base64UrlEncode(result.Result.PublicKey);

            return(await ProcessFido2RegisterResponseAsync(relatedItem, publicKey, result.Result.Counter,
                                                           _encodingService.Base64UrlEncode(result.Result.CredentialId)));
        }
示例#4
0
        public async Task <ActionResult> MakeCredential(
            [FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                // 1. get the options we sent the client
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

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

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

                    return(true);
                };

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

                // 3. Store the credentials in db
                fidoStore.AddCredentialToUser(options.User, new StoredCredential
                {
                    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.Now,
                    AaGuid           = success.Result.Aaguid
                });

                // 4. return "ok" to the client
                return(Ok(success));
            }
            catch (Exception e)
            {
                return(BadRequest(new Fido2.CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }
示例#5
0
        public async Task <bool> CompleteCreation(string userId, string name, string data)
        {
            try
            {
                var attestationResponse = JObject.Parse(data).ToObject <AuthenticatorAttestationRawResponse>();
                await using var dbContext = _contextFactory.CreateContext();
                var user = await dbContext.Users.Include(applicationUser => applicationUser.Fido2Credentials)
                           .FirstOrDefaultAsync(applicationUser => applicationUser.Id == userId);

                if (user == null || !CreationStore.TryGetValue(userId, out var options))
                {
                    return(false);
                }

                // 2. Verify and make the credentials
                var success =
                    await _fido2.MakeNewCredentialAsync(attestationResponse, options, args => Task.FromResult(true));

                // 3. Store the credentials in db
                var newCredential = new Fido2Credential()
                {
                    Name = name, ApplicationUserId = userId
                };

                newCredential.SetBlob(new Fido2CredentialBlob()
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(success.Result.CredentialId),
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    AaGuid           = success.Result.Aaguid.ToString(),
                });

                await dbContext.Fido2Credentials.AddAsync(newCredential);

                await dbContext.SaveChangesAsync();

                CreationStore.Remove(userId, out _);
                return(true);
            }
            catch (Exception)
            {
                return(false);
            }
        }
示例#6
0
        public async Task <JsonResult> SaveCredentials([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                var fidoCredentials = await fido2.MakeNewCredentialAsync(attestationResponse, options, IsCredentialUnique);

                var storedCredential = new StoredCredential
                {
                    Descriptor       = new PublicKeyCredentialDescriptor(fidoCredentials.Result.CredentialId),
                    PublicKey        = fidoCredentials.Result.PublicKey,
                    UserHandle       = fidoCredentials.Result.User.Id,
                    SignatureCounter = fidoCredentials.Result.Counter,
                    CredType         = fidoCredentials.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = fidoCredentials.Result.Aaguid
                };

                var names  = options.User.DisplayName.Split(' ');
                var result = await oktaClient.Users.CreateUserAsync(new CreateUserWithoutCredentialsOptions
                {
                    Profile = new UserProfile
                    {
                        Login                     = options.User.Name,
                        Email                     = options.User.Name,
                        DisplayName               = options.User.DisplayName,
                        FirstName                 = names[0],
                        LastName                  = names[1],
                        ["CredentialId"]          = Convert.ToBase64String(fidoCredentials.Result.CredentialId),
                        ["PasswordlessPublicKey"] = JsonConvert.SerializeObject(storedCredential)
                    }
                });

                return(Json(fidoCredentials));
            }
            catch (Exception e)
            {
                return(Json(new Fido2.CredentialMakeResult {
                    Status = "error", ErrorMessage = e.Message
                }));
            }
        }
示例#7
0
        public async Task <bool> CompleteWebAuthRegistrationAsync(User user, int id, string name, AuthenticatorAttestationRawResponse attestationResponse)
        {
            var keyId = $"Key{id}";

            var provider = user.GetTwoFactorProvider(TwoFactorProviderType.WebAuthn);

            if (!provider?.MetaData?.ContainsKey("pending") ?? true)
            {
                return(false);
            }

            var options = CredentialCreateOptions.FromJson((string)provider.MetaData["pending"]);

            // Callback to ensure credential id is unique. Always return true since we don't care if another
            // account uses the same 2fa key.
            IsCredentialIdUniqueToUserAsyncDelegate callback = args => Task.FromResult(true);

            var success = await _fido2.MakeNewCredentialAsync(attestationResponse, options, callback);

            provider.MetaData.Remove("pending");
            provider.MetaData[keyId] = new TwoFactorProvider.WebAuthnData
            {
                Name             = name,
                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.Now,
                AaGuid           = success.Result.Aaguid
            };

            var providers = user.GetTwoFactorProviders();

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

            return(true);
        }
        public async Task <Result <Fido2.CredentialMakeResult> > MakeCredentialAsync(AuthenticatorAttestationRawResponse attestationResponse, int userId, long loginId, string deviceName)
        {
            try
            {
                // 1. get the options we sent the client
                var cacheKey = $"CredentialOptions:{loginId}";
                if (!_cache.TryGetValue(cacheKey, out CredentialCreateOptions options))
                {
                    _logger.LogError($"Failed to find credential options for user {userId} login {loginId}");
                    return(Result.Failure <Fido2.CredentialMakeResult>("No challenge found"));
                }
                _cache.Remove(cacheKey);

                // 2. Verify and make the credentials
                var cmr = await _fido2.MakeNewCredentialAsync(attestationResponse, options,
                                                              x => Task.FromResult(true));

                // 3. Store the credentials in db
                _db.DeviceAuthorizations.Add(new DeviceAuthorization
                {
                    UserId           = userId,
                    PublicKey        = cmr.Result.PublicKey,
                    CredentialId     = cmr.Result.CredentialId,
                    SignatureCounter = cmr.Result.Counter,
                    DeviceName       = deviceName
                });
                await _db.SaveChangesAsync();

                // 4. return "ok" to the client
                _logger.LogInformation($"Created credential for user {userId} login {loginId}");
                return(Result.Success(cmr));
            }
            catch (Exception e)
            {
                _logger.LogError(e, $"Failed to make credential for user {userId} login {loginId}");
                return(Result.Failure <Fido2.CredentialMakeResult>("Error making credential"));
            }
        }
示例#9
0
        public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse)
        {
            try
            {
                // 1. get the options we sent the client
                var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions");
                var options     = CredentialCreateOptions.FromJson(jsonOptions);

                // 2. Create callback so that lib can verify credential id is unique to this user
                // IsCredentialIdUniqueToUserAsyncDelegate callback = async (IsCredentialIdUniqueToUserParams args) =>
                // {
                //   // var users = await DemoStorage.GetUsersByCredentialIdAsync(args.CredentialId);
                //   var users = await _fido2CredentialService.GetUsersByCredentialIdAsync(args.CredentialId);
                //   if (users.Count > 0)
                //     return false;
                //
                //   return true;
                // };
                IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) =>
                {
                    // TODO Check if credentials are unique
                    return(true);
                };

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


                var applicationUser = await _userManager.GetUserAsync(HttpContext.User);

                // Schreibe die Credentials in die Datenbank
                var newFido2Credential = await _fido2CredentialService.AddCredentialToUser(new Fido2Credential()
                {
                    UserId           = applicationUser.Id,
                    Descriptor       = success.Result.CredentialId,
                    PublicKey        = success.Result.PublicKey,
                    UserHandle       = success.Result.User.Id,
                    SignatureCounter = success.Result.Counter,
                    CredType         = success.Result.CredType,
                    RegDate          = DateTime.Now,
                    AaGuid           = success.Result.Aaguid
                });

                // applicationUser.Fido2Credentials.Add(newFido2Credential);
                applicationUser.TwoFactorEnabled = true;
                applicationUser.TwoFactorMethod  = TwoFactorType.Fido2;
                await _userManager.UpdateAsync(applicationUser);

                // TODO Return Databse Entry not just JSON


                // 4. return "ok" to the client
                return(new JsonResult(success));
            }
            catch (Exception e)
            {
                return(new JsonResult(new Fido2.CredentialMakeResult {
                    Status = "error", ErrorMessage = FormatException(e)
                }));
            }
        }