/// <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))); }
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) })); } }
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); } }
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 })); } }
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")); } }
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) })); } }