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, }); } }
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) })); } }
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) })); } }
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); }
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); }
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 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 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); }
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) })); } }
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 })); }
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) })); } }
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) })); } }
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(); }
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) })); } }