public ActionResult CredentialOptions([FromBody] RegisterModel model) { var user = new Fido2User { DisplayName = $"{model.FirstName} {model.LastName}", Name = model.Email, Id = Encoding.UTF8.GetBytes(model.Email) }; var options = fido2.RequestNewCredential(user, new List <PublicKeyCredentialDescriptor>()); HttpContext.Session.SetString("fido2.attestationOptions", options.ToJson()); return(Json(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); }
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 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 options = _fido2.RequestNewCredential(fidoUser, excludeCredentials, AuthenticatorSelection.Default, 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 <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) })); } }
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) })); } }
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 async Task AddCredentialToUser(Fido2User user, FidoStoredCredential credential) { credential.UserId = user.Id; _applicationDbContext.FidoStoredCredential.Add(credential); await _applicationDbContext.SaveChangesAsync(); }
public IsCredentialIdUniqueToUserParams(byte[] credentialId, Fido2User user) { CredentialId = credentialId; User = user; }
public void AddCredentialToUser(Fido2User user, StoredCredential credential) { credential.UserId = user.Id; _storedCredentials.Add(credential); }
public List <StoredCredential> GetCredentialsByUser(Fido2User user) { return(_storedCredentials.Where(c => c.UserId.SequenceEqual(user.Id)).ToList()); }
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) })); } }
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 <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 ActionResult MakeCredentialOptions([FromForm] string attType, [FromForm] string authType, [FromForm] bool requireResidentKey, [FromForm] string userVerification) { var subjectId = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; try { // 1. Get user from DB by username var dbUser = TestUsers.Users.FirstOrDefault(u => string.Equals(u.SubjectId, subjectId, StringComparison.InvariantCultureIgnoreCase)); if (dbUser == null) { return(BadRequest("no such user")); } var user = new Fido2User { DisplayName = dbUser.Username, Name = dbUser.Username, Id = Encoding.UTF8.GetBytes(dbUser.Username) }; // 2. Get user existing keys by username List <PublicKeyCredentialDescriptor> existingKeys = TestUsers.FidoCredentials.Where(c => c.UserId.SequenceEqual(user.Id)).Select(c => c.Descriptor).ToList(); // 3. Create options var options = _lib.RequestNewCredential( user: user, excludeCredentials: existingKeys, authenticatorSelection: new AuthenticatorSelection { RequireResidentKey = requireResidentKey, UserVerification = userVerification.ToEnum <UserVerificationRequirement>(), AuthenticatorAttachment = string.IsNullOrEmpty(authType) ? (AuthenticatorAttachment?)null : authType.ToEnum <AuthenticatorAttachment>(), }, attestationPreference: attType.ToEnum <AttestationConveyancePreference>(), extensions: new AuthenticationExtensionsClientInputs() { Extensions = true, UserVerificationIndex = true, Location = true, UserVerificationMethod = true, BiometricAuthenticatorPerformanceBounds = new AuthenticatorBiometricPerfBounds { FAR = float.MaxValue, FRR = float.MaxValue } }); // 4. Temporarily store options, session/in-memory cache/redis/db var sessionId = Convert.ToBase64String(IdentityModel.CryptoRandom.CreateRandomKey(64)); TestUsers.FidoAttestationOptions[sessionId] = options.ToJson(); // 5. return options to client return(this.NewtonsoftJsonResult(new MakeCredentialOptionsResponse { SessionId = sessionId, Options = options, })); } catch (Exception e) { return(BadRequest(new CredentialCreateOptions { Status = "error", ErrorMessage = FormatException(e) })); } }
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) })); } }