public void Constructor_GiveValidArguments_PropertiesAreSet() { var credentialCreateOptions = new CredentialCreateOptions(); var result = new InitiateAuthenticatorDeviceEnrollmentCommandResult(credentialCreateOptions); Assert.Equal(credentialCreateOptions, result.Options); }
public async Task <CredentialMakeResult> MakeCredential([Required, FromBody] MakeCredentialsRequest request) { using (AuthLogic.Disable()) { var optionsEntity = Database.Retrieve <WebAuthnMakeCredentialsOptionsEntity>(request.CreateOptionsId); var options = CredentialCreateOptions.FromJson(optionsEntity.Json); var result = await fido2.MakeNewCredentialAsync(request.AttestationRawResponse, options, async (args) => { return(!(await Database.Query <WebAuthnCredentialEntity>().AnyAsync(c => c.CredentialId == args.CredentialId))); }); if (result.Status != "ok") { throw new InvalidOperationException(options.ErrorMessage); } new WebAuthnCredentialEntity { CredentialId = result.Result.CredentialId, PublicKey = result.Result.PublicKey, User = Lite.ParsePrimaryKey <UserEntity>(Encoding.UTF8.GetString(result.Result.User.Id)), Counter = (int)result.Result.Counter, CredType = result.Result.CredType, Aaguid = result.Result.Aaguid, }.Save(); return(result); } }
public EnrollAuthenticatorDeviceCommand(string name, AuthenticatorAttestationRawResponse authenticatorAttestationRawResponse, CredentialCreateOptions credentialCreateOptions) { this.AuthenticatorAttestationRawResponse = authenticatorAttestationRawResponse; this.CredentialCreateOptions = credentialCreateOptions; this.Name = name; }
public async Task <JsonResult> MakeCredentialResultTest([FromBody] AuthenticatorAttestationRawResponse attestationResponse) { // 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); if (users.Count > 0) { return(false); } return(true); }; // 2. Verify and make the credentials var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback); // 3. Store the credentials in db DemoStorage.AddCredentialToUser(options.User, new StoredCredential { Descriptor = new PublicKeyCredentialDescriptor(success.Result.CredentialId), PublicKey = success.Result.PublicKey, UserHandle = success.Result.User.Id, SignatureCounter = success.Result.Counter }); // 4. return "ok" to the client return(Json(success)); }
public void Constructor_GiveValidArguments_PropertiesAreSet() { var authenticatorAttestationRawResponse = new AuthenticatorAttestationRawResponse(); var credentialCreateOptions = new CredentialCreateOptions(); var command = new EnrollAuthenticatorDeviceCommand("name", authenticatorAttestationRawResponse, credentialCreateOptions); Assert.Equal("name", command.Name); Assert.Equal(authenticatorAttestationRawResponse, command.AuthenticatorAttestationRawResponse); Assert.Equal(credentialCreateOptions, command.CredentialCreateOptions); }
public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse, CancellationToken cancellationToken) { 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 = static async(args, cancellationToken) =>
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))); }
/// <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 <IActionResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse) { var o = new { challenge = string.Empty, origin = string.Empty, type = string.Empty }; var username = string.Empty; try { o = JsonConvert.DeserializeAnonymousType((Encoding.UTF8.GetString(attestationResponse.Response.ClientDataJson)), o); var jsonOptions = _memoryCache.Get <string>(o.challenge); var options = CredentialCreateOptions.FromJson(jsonOptions); username = options.User.Name; async Task <bool> Callback(IsCredentialIdUniqueToUserParams args) { var users = await _dataStore.GetUsersByCredentialIdAsync(args.CredentialId); return(users.Count <= 0); } var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, Callback); _dataStore.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 }); var ev = new Event(username, "Successfully logged the person in", nameof(RegistrationController), nameof(MakeCredential)); await _elasticClient.IndexAsync(ev, i => i.Index(GetIndexName(nameof(Ok)))); return(Ok(success)); } catch (Exception e) { var errorEvent = new ErrorEvent(e, username, nameof(RegistrationController), nameof(MakeCredential)); await _elasticClient.IndexAsync(errorEvent, i => i.Index(GetIndexName(nameof(Exception)))); return(Ok(new Fido2.CredentialMakeResult { Status = "error", ErrorMessage = FormatException(e) + $"ClientDataJson = {Encoding.UTF8.GetString(attestationResponse.Response.ClientDataJson)}" })); } }
public async Task <ActionResult> MakeCredential([ModelBinder(typeof(NewtonsoftJsonAdapter.Binder))] MakeCredentialRequest request) { var subjectId = User.Claims.FirstOrDefault(c => c.Type == "sub")?.Value; try { // 1. get the options we sent the client var jsonOptions = TestUsers.FidoAttestationOptions[request.SessionId]; var options = CredentialCreateOptions.FromJson(jsonOptions); // 2. Verify and make the credentials var success = await _lib.MakeNewCredentialAsync( attestationResponse : request.AttestationResponse, origChallenge : options, isCredentialIdUniqueToUser : async(IsCredentialIdUniqueToUserParams args) => { var users = TestUsers.FidoCredentials.Where(c => c.Descriptor.Id.SequenceEqual(args.CredentialId)).ToList(); if (users.Count > 0) { return(false); } return(true); }); // 3. Store the credentials in db TestUsers.FidoCredentials.Add(new TestUsers.StoredFidoCredential { UserId = options.User.Id, SubjectId = subjectId, 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 CredentialMakeResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public async Task <IActionResult> CompleteAuthDeviceRegistration( [FromBody] CompleteAuthDeviceRegistrationRequest model) { var jsonOptions = this.TempData["CredentialData"] as string; var options = CredentialCreateOptions.FromJson(jsonOptions); var result = await this._mediator.Send(new EnrollAuthenticatorDeviceCommand( model.Name, model.AttestationResponse, options)); return(this.Json(result.IsFailure ? new CompleteAuthDeviceRegistrationResponse() : new CompleteAuthDeviceRegistrationResponse(result.Value.CredentialMakeResult, result.Value.DeviceId, model.Name))); }
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 <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 <IActionResult> CompleteAuthDeviceRegistration( [FromBody] CompleteAuthDeviceRegistrationRequest model) { var jsonOptions = this.TempData["CredentialData"] as string; var options = CredentialCreateOptions.FromJson(jsonOptions); var result = await this._mediator.Send(new EnrollDeviceCommand( model.Name, model.AttestationResponse, options)); if (result.IsFailure) { return(this.Json(new Fido2.CredentialMakeResult { Status = "error" })); } return(this.Json(result.Value.CredentialMakeResult)); }
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 <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse attestationResponse, [FromQuery] CredentialCreateOptions options) { var user = await GetCurrentLocalUserAsync(); try { // 2. Create callback so that lib can verify credential id is unique to this user IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) => { var users = await _auth.GetUserIdsByCredentialIdAsync(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 await _auth.AddCredential(user, new StoredCredential { Descriptor = new PublicKeyCredentialDescriptor(success.Result.CredentialId), PublicKey = success.Result.PublicKey, UserId = BitConverter.GetBytes(user.Id), 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(Json(success)); } catch (Exception e) { return(Json(new { Status = "error", ErrorMessage = e.Message })); } }
public void TestAuthenticatorAttestationResponseNotUserPresent() { var challenge = RandomGenerator.Default.GenerateBytes(128); var rp = "https://www.passwordless.dev"; var authData = new AuthenticatorData( SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(rp)), AuthenticatorFlags.UV, 0, null, null ).ToByteArray(); var clientDataJson = Encoding.UTF8.GetBytes( JsonConvert.SerializeObject ( new { Type = "webauthn.create", Challenge = challenge, Origin = rp, } ) ); var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Response = new AuthenticatorAttestationRawResponse.ResponseData() { AttestationObject = CBORObject.NewMap().Add("fmt", "testing").Add("attStmt", CBORObject.NewMap()).Add("authData", authData).EncodeToBytes(), ClientDataJson = clientDataJson }, }; var origChallenge = new CredentialCreateOptions { Attestation = AttestationConveyancePreference.Direct, AuthenticatorSelection = new AuthenticatorSelection { AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform, RequireResidentKey = true, UserVerification = UserVerificationRequirement.Required, }, Challenge = challenge, ErrorMessage = "", PubKeyCredParams = new List <PubKeyCredParam>() { new PubKeyCredParam { Alg = COSE.Algorithm.ES256, Type = PublicKeyCredentialType.PublicKey, } }, Rp = new PublicKeyCredentialRpEntity(rp, rp, ""), Status = "ok", User = new Fido2User { Name = "testuser", Id = Encoding.UTF8.GetBytes("testuser"), DisplayName = "Test User", }, Timeout = 60000, }; IsCredentialIdUniqueToUserAsyncDelegate callback = (args) => { return(Task.FromResult(true)); }; var lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origin = rp, }); var ex = Assert.ThrowsAsync <Fido2VerificationException>(() => lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback)); Assert.Equal("User Present flag not set in authenticator data", ex.Result.Message); }
public async Task TestAuthenticatorOrigins(string origin, string expectedOrigin) { var challenge = RandomGenerator.Default.GenerateBytes(128); var rp = origin; var acd = new AttestedCredentialData(("00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-40-FE-6A-32-63-BE-37-D1-01-B1-2E-57-CA-96-6C-00-22-93-E4-19-C8-CD-01-06-23-0B-C6-92-E8-CC-77-12-21-F1-DB-11-5D-41-0F-82-6B-DB-98-AC-64-2E-B1-AE-B5-A8-03-D1-DB-C1-47-EF-37-1C-FD-B1-CE-B0-48-CB-2C-A5-01-02-03-26-20-01-21-58-20-A6-D1-09-38-5A-C7-8E-5B-F0-3D-1C-2E-08-74-BE-6D-BB-A4-0B-4F-2A-5F-2F-11-82-45-65-65-53-4F-67-28-22-58-20-43-E1-08-2A-F3-13-5B-40-60-93-79-AC-47-42-58-AA-B3-97-B8-86-1D-E4-41-B4-4E-83-08-5D-1C-6B-E0-D0").Split('-').Select(c => Convert.ToByte(c, 16)).ToArray()); var authData = new AuthenticatorData( SHA256.Create().ComputeHash(Encoding.UTF8.GetBytes(origin)), AuthenticatorFlags.UP | AuthenticatorFlags.AT, 0, acd, null ).ToByteArray(); var clientDataJson = Encoding.UTF8.GetBytes( JsonConvert.SerializeObject ( new { Type = "webauthn.create", Challenge = challenge, Origin = rp, } ) ); var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Response = new AuthenticatorAttestationRawResponse.ResponseData() { AttestationObject = CBORObject.NewMap().Add("fmt", "none").Add("attStmt", CBORObject.NewMap()).Add("authData", authData).EncodeToBytes(), ClientDataJson = clientDataJson }, }; var origChallenge = new CredentialCreateOptions { Attestation = AttestationConveyancePreference.Direct, AuthenticatorSelection = new AuthenticatorSelection { AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform, RequireResidentKey = true, UserVerification = UserVerificationRequirement.Required, }, Challenge = challenge, ErrorMessage = "", PubKeyCredParams = new List <PubKeyCredParam>() { new PubKeyCredParam { Alg = COSE.Algorithm.ES256, Type = PublicKeyCredentialType.PublicKey, } }, Rp = new PublicKeyCredentialRpEntity(rp, rp, ""), Status = "ok", User = new Fido2User { Name = "testuser", Id = Encoding.UTF8.GetBytes("testuser"), DisplayName = "Test User", }, Timeout = 60000, }; IsCredentialIdUniqueToUserAsyncDelegate callback = (args) => { return(Task.FromResult(true)); }; var lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origin = expectedOrigin, }); var result = await lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback); }
public async Task <Fido2.CredentialMakeResult> MakeAttestationResponse() { _attestationObject.Set("authData", _authData); var attestationResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Response = new AuthenticatorAttestationRawResponse.ResponseData() { AttestationObject = _attestationObject.EncodeToBytes(), ClientDataJson = _clientDataJson, }, Extensions = new AuthenticationExtensionsClientOutputs() { AppID = true, AuthenticatorSelection = true, BiometricAuthenticatorPerformanceBounds = true, GenericTransactionAuthorization = new byte[] { 0xf1, 0xd0 }, SimpleTransactionAuthorization = "test", Extensions = new string[] { "foo", "bar" }, Example = "test", Location = new GeoCoordinatePortable.GeoCoordinate(42.523714, -71.040860), UserVerificationIndex = new byte[] { 0xf1, 0xd0 }, UserVerificationMethod = new ulong[][] { new ulong[] { 4 // USER_VERIFY_PASSCODE_INTERNAL }, }, } }; var origChallenge = new CredentialCreateOptions { Attestation = AttestationConveyancePreference.Direct, AuthenticatorSelection = new AuthenticatorSelection { AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform, RequireResidentKey = true, UserVerification = UserVerificationRequirement.Required, }, Challenge = _challenge, ErrorMessage = "", PubKeyCredParams = new List <PubKeyCredParam>() { new PubKeyCredParam { Alg = COSE.Algorithm.ES256, Type = PublicKeyCredentialType.PublicKey, } }, Rp = new PublicKeyCredentialRpEntity(rp, rp, ""), Status = "ok", User = new Fido2User { Name = "testuser", Id = Encoding.UTF8.GetBytes("testuser"), DisplayName = "Test User", }, Timeout = 60000, }; IsCredentialIdUniqueToUserAsyncDelegate callback = (args) => { return(Task.FromResult(true)); }; var lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origin = rp, }); var credentialMakeResult = await lib.MakeNewCredentialAsync(attestationResponse, origChallenge, callback); return(credentialMakeResult); }
public async Task <IActionResult> RegisterCallback([FromBody] AuthenticatorAttestationRawResponse model) { 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")); } try { // 1. get the options we sent the client var jsonOptions = HttpContext.Session.GetString("fido2.attestationOptions"); var options = CredentialCreateOptions.FromJson(jsonOptions); var authenticatorName = HttpContext.Session.GetString("fido2.attestationOptions.authenticatorType"); // 2. Create callback so that lib can verify credential id is unique to this user IsCredentialIdUniqueToUserAsyncDelegate callback = async(IsCredentialIdUniqueToUserParams args) => { var users = _authContext.FidoLogins.Where(l => l.PublicKeyIdBytes.SequenceEqual(args.CredentialId)); if (users.Count() > 0) { return(false); } return(true); }; // 2. Verify and make the credentials var success = await _lib.MakeNewCredentialAsync(model, options, callback); var parsedResponse = AuthenticatorAttestationResponse.Parse(model);; var authData = new AuthenticatorData(parsedResponse.AttestationObject.AuthData); var dbUser = _authContext.Users.First(x => x.Id == user.Id); dbUser.TwoFactorEnabled = true; var login = new FidoLogin() { PublicKeyIdBytes = success.Result.CredentialId, PublicKeyId = Fido2NetLib.Base64Url.Encode(success.Result.CredentialId), AaGuid = success.Result.Aaguid.ToString(), PublicKey = success.Result.PublicKey, SignatureCounter = success.Result.Counter, CredType = success.Result.CredType, RegistrationDate = DateTime.Now, User = dbUser, UserHandle = success.Result.User.Id, AuthenticatorName = authenticatorName }; _authContext.FidoLogins.Add(login); _authContext.SaveChanges(); // 4. return "ok" to the client return(Json(new { success = true })); } catch (Exception e) { return(Json(new { error = true })); } }
public InitiateAuthenticatorDeviceEnrollmentCommandResult(CredentialCreateOptions options) { this.Options = options; }
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 _fido2Storage.GetUsersByCredentialIdAsync(args.CredentialId); if (users.Count > 0) { return(false); } return(true); }; // 2. Verify and make the credentials var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback); // 3. Store the credentials in db await _fido2Storage.AddCredentialToUser(options.User, new FidoStoredCredential { Username = options.User.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 }); // 4. return "ok" to the client var user = await _userManager.GetUserAsync(User); if (user == null) { return(Json(new CredentialMakeResult { Status = "error", ErrorMessage = _sharedLocalizer["FIDO2_USER_NOTFOUND", _userManager.GetUserId(User)] })); } await _userManager.SetTwoFactorEnabledAsync(user, true); var userId = await _userManager.GetUserIdAsync(user); return(Json(success)); } catch (Exception e) { return(Json(new CredentialMakeResult { Status = "error", ErrorMessage = FormatException(e) })); } }
public async Task <JsonResult> MakeCredential([FromBody] AuthenticatorAttestationRawResponse authenticatorAttestationRawResponse) { try { var user = await _userManager.GetUserAsync(User); if (user == null) { throw new Exception("Unable to retrieve user."); } var jsonOptions = await _distributedCache.GetStringAsync(UniqueId); if (string.IsNullOrEmpty(jsonOptions)) { throw new Exception("Cant get Credential options from cache."); } var options = CredentialCreateOptions.FromJson(jsonOptions); IsCredentialIdUniqueToUserAsyncDelegate isCredentialIdUniqueToUserAsyncDelegate = async(IsCredentialIdUniqueToUserParams isCredentialIdUniqueToUserParams) => { var fido2Users = await _fido2Service.GetFido2UsersByCredentialIdAsync(isCredentialIdUniqueToUserParams.CredentialId); if (fido2Users.Count > 0) { return(false); } return(true); }; var result = await _fido2.MakeNewCredentialAsync(authenticatorAttestationRawResponse, options, isCredentialIdUniqueToUserAsyncDelegate); if (result.Status != "ok") { throw new Exception("Unable to create credential."); } var newFido2StoredCredential = new Fido2StoredCredential { }; newFido2StoredCredential.UserName = options.User.Name; newFido2StoredCredential.UserId = options.User.Id; newFido2StoredCredential.PublicKey = result.Result.PublicKey; newFido2StoredCredential.UserHandle = result.Result.User.Id; newFido2StoredCredential.SignatureCounter = result.Result.Counter; newFido2StoredCredential.CredType = result.Result.CredType; newFido2StoredCredential.RegDate = DateTime.Now; newFido2StoredCredential.AaGuid = Guid.NewGuid(); newFido2StoredCredential.Descriptor = new PublicKeyCredentialDescriptor(result.Result.CredentialId); _fido2Service.AddFido2StoredCredential(newFido2StoredCredential); return(Json(result)); } catch (Exception exception) { return(Json(new CredentialCreateOptions() { Status = "error", ErrorMessage = CommonFunctions.FormatException(exception) })); } }
public async Task TestApplePublicKeyMismatch() { var cpkBytes = new byte[] { 0xa5, 0x01, 0x02, 0x03, 0x26, 0x20, 0x01, 0x21, 0x58, 0x20, 0x79, 0xfe, 0x59, 0x08, 0xbb, 0x51, 0x29, 0xc8, 0x09, 0x38, 0xb7, 0x54, 0xc0, 0x4d, 0x2b, 0x34, 0x0e, 0xfa, 0x66, 0x15, 0xb9, 0x87, 0x69, 0x8b, 0xf5, 0x9d, 0xa4, 0xe5, 0x3e, 0xa3, 0xe6, 0xfe, 0x22, 0x58, 0x20, 0xfb, 0x03, 0xda, 0xa1, 0x27, 0x0d, 0x58, 0x04, 0xe8, 0xab, 0x61, 0xc1, 0x5a, 0xac, 0xa2, 0x43, 0x5c, 0x7d, 0xbf, 0x36, 0x9d, 0x71, 0xca, 0x15, 0xc5, 0x23, 0xb0, 0x00, 0x4a, 0x1b, 0x75, 0xb7 }; _credentialPublicKey = new CredentialPublicKey(cpkBytes); var authData = new AuthenticatorData(_rpIdHash, _flags, _signCount, _acd, _exts).ToByteArray(); _attestationObject.Set("authData", new CborByteString(authData)); var clientData = new { type = "webauthn.create", challenge = _challenge, origin = "https://www.passwordless.dev", }; var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(clientData); var invalidX5cStrings = StackAllocSha256(authData, clientDataJson); var trustPath = invalidX5cStrings .Select(x => new X509Certificate2(Convert.FromBase64String(x))) .ToArray(); var X5c = new CborArray { { trustPath[0].RawData }, { trustPath[1].RawData } }; ((CborMap)_attestationObject["attStmt"]).Set("x5c", X5c); var attestationResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Response = new AuthenticatorAttestationRawResponse.ResponseData() { AttestationObject = _attestationObject.Encode(), ClientDataJson = clientDataJson, } }; var origChallenge = new CredentialCreateOptions { Attestation = AttestationConveyancePreference.Direct, AuthenticatorSelection = new AuthenticatorSelection { AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform, RequireResidentKey = true, UserVerification = UserVerificationRequirement.Discouraged, }, Challenge = _challenge, ErrorMessage = "", PubKeyCredParams = new List <PubKeyCredParam>() { new PubKeyCredParam(COSE.Algorithm.ES256) }, Rp = new PublicKeyCredentialRpEntity("https://www.passwordless.dev", "6cc3c9e7967a.ngrok.io", ""), Status = "ok", User = new Fido2User { Name = "testuser", Id = Encoding.UTF8.GetBytes("testuser"), DisplayName = "Test User", }, Timeout = 60000, }; IsCredentialIdUniqueToUserAsyncDelegate callback = (args, cancellationToken) => { return(Task.FromResult(true)); }; IFido2 lib = new Fido2(new Fido2Configuration() { ServerDomain = "6cc3c9e7967a.ngrok.io", ServerName = "6cc3c9e7967a.ngrok.io", Origins = new HashSet <string> { "https://www.passwordless.dev" }, }); var credentialMakeResult = await lib.MakeNewCredentialAsync(attestationResponse, origChallenge, callback); }
public void TestAuthenticatorAttestationResponseInvalidRawType() { var challenge = RandomGenerator.Default.GenerateBytes(128); var rp = "fido2.azurewebsites.net"; var clientDataJson = Encoding.UTF8.GetBytes( JsonConvert.SerializeObject ( new { Type = "webauthn.create", Challenge = challenge, Origin = rp, } ) ); var rawResponse = new AuthenticatorAttestationRawResponse { Type = null, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Response = new AuthenticatorAttestationRawResponse.ResponseData() { AttestationObject = CBORObject.NewMap().Add("fmt", "testing").Add("attStmt", CBORObject.NewMap()).Add("authData", new byte[0]).EncodeToBytes(), ClientDataJson = clientDataJson }, }; var origChallenge = new CredentialCreateOptions { Attestation = AttestationConveyancePreference.Direct, AuthenticatorSelection = new AuthenticatorSelection { AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform, RequireResidentKey = true, UserVerification = UserVerificationRequirement.Required, }, Challenge = challenge, ErrorMessage = "", PubKeyCredParams = new List <PubKeyCredParam>() { new PubKeyCredParam { Alg = -7, Type = PublicKeyCredentialType.PublicKey, } }, Rp = new PublicKeyCredentialRpEntity(rp, rp, ""), Status = "ok", User = new Fido2User { Name = "testuser", Id = Encoding.UTF8.GetBytes("testuser"), DisplayName = "Test User", }, Timeout = 60000, }; IsCredentialIdUniqueToUserAsyncDelegate callback = (args) => { return(Task.FromResult(true)); }; var lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origin = rp, }); var ex = Assert.ThrowsAsync <Fido2VerificationException>(() => lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback)); Assert.Equal("AttestationResponse is missing type with value 'public-key'", ex.Result.Message); }
public void TestAuthenticatorAttestationResponseUVRequired() { var challenge = RandomGenerator.Default.GenerateBytes(128); var rp = "https://www.passwordless.dev"; var acd = new AttestedCredentialData(("00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-40-FE-6A-32-63-BE-37-D1-01-B1-2E-57-CA-96-6C-00-22-93-E4-19-C8-CD-01-06-23-0B-C6-92-E8-CC-77-12-21-F1-DB-11-5D-41-0F-82-6B-DB-98-AC-64-2E-B1-AE-B5-A8-03-D1-DB-C1-47-EF-37-1C-FD-B1-CE-B0-48-CB-2C-A5-01-02-03-26-20-01-21-58-20-A6-D1-09-38-5A-C7-8E-5B-F0-3D-1C-2E-08-74-BE-6D-BB-A4-0B-4F-2A-5F-2F-11-82-45-65-65-53-4F-67-28-22-58-20-43-E1-08-2A-F3-13-5B-40-60-93-79-AC-47-42-58-AA-B3-97-B8-86-1D-E4-41-B4-4E-83-08-5D-1C-6B-E0-D0").Split('-').Select(c => Convert.ToByte(c, 16)).ToArray()); var authData = new AuthenticatorData( SHA256.HashData(Encoding.UTF8.GetBytes(rp)), AuthenticatorFlags.AT | AuthenticatorFlags.UP, 0, acd ).ToByteArray(); var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(new { type = "webauthn.create", challenge = challenge, origin = rp, }); var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Response = new AuthenticatorAttestationRawResponse.ResponseData() { AttestationObject = new CborMap { { "fmt", "none" }, { "attStmt", new CborMap() }, { "authData", authData } }.Encode(), ClientDataJson = clientDataJson }, }; var origChallenge = new CredentialCreateOptions { Attestation = AttestationConveyancePreference.Direct, AuthenticatorSelection = new AuthenticatorSelection { AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform, RequireResidentKey = true, UserVerification = UserVerificationRequirement.Required, }, Challenge = challenge, ErrorMessage = "", PubKeyCredParams = new List <PubKeyCredParam>() { new PubKeyCredParam(COSE.Algorithm.ES256) }, Rp = new PublicKeyCredentialRpEntity(rp, rp, ""), Status = "ok", User = new Fido2User { Name = "testuser", Id = Encoding.UTF8.GetBytes("testuser"), DisplayName = "Test User", }, Timeout = 60000, }; IsCredentialIdUniqueToUserAsyncDelegate callback = (args, cancellationToken) => { return(Task.FromResult(true)); }; IFido2 lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origins = new HashSet <string> { rp }, }); var ex = Assert.ThrowsAsync <Fido2VerificationException>(() => lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback)); Assert.Equal("User Verified flag not set in authenticator data and user verification was required", ex.Result.Message); }
public void TestAuthenticatorAttestationResponseNoAttestedCredentialData() { var challenge = RandomGenerator.Default.GenerateBytes(128); var rp = "https://www.passwordless.dev"; var authData = new AuthenticatorData( SHA256.HashData(Encoding.UTF8.GetBytes(rp)), AuthenticatorFlags.UP | AuthenticatorFlags.UV, 0, null ).ToByteArray(); var clientDataJson = JsonSerializer.SerializeToUtf8Bytes(new { type = "webauthn.create", challenge = challenge, origin = rp, }); var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, Id = new byte[] { 0xf1, 0xd0 }, RawId = new byte[] { 0xf1, 0xd0 }, Response = new AuthenticatorAttestationRawResponse.ResponseData() { AttestationObject = new CborMap { { "fmt", "testing" }, { "attStmt", new CborMap() }, { "authData", authData } }.Encode(), ClientDataJson = clientDataJson }, }; var origChallenge = new CredentialCreateOptions { Attestation = AttestationConveyancePreference.Direct, AuthenticatorSelection = new AuthenticatorSelection { AuthenticatorAttachment = AuthenticatorAttachment.CrossPlatform, RequireResidentKey = true, UserVerification = UserVerificationRequirement.Discouraged, }, Challenge = challenge, ErrorMessage = "", PubKeyCredParams = new List <PubKeyCredParam>() { new PubKeyCredParam(COSE.Algorithm.ES256) }, Rp = new PublicKeyCredentialRpEntity(rp, rp, ""), Status = "ok", User = new Fido2User { Name = "testuser", Id = Encoding.UTF8.GetBytes("testuser"), DisplayName = "Test User", }, Timeout = 60000, }; IsCredentialIdUniqueToUserAsyncDelegate callback = (args, cancellationToken) => { return(Task.FromResult(true)); }; IFido2 lib = new Fido2(new Fido2Configuration() { ServerDomain = rp, ServerName = rp, Origins = new HashSet <string> { rp }, }); var ex = Assert.ThrowsAsync <Fido2VerificationException>(() => lib.MakeNewCredentialAsync(rawResponse, origChallenge, callback)); Assert.Equal("Attestation flag not set on attestation data", ex.Result.Message); }
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 jsonOptions = await _distributedCache.GetStringAsync(UniqueId); if (string.IsNullOrEmpty(jsonOptions)) { throw new Exception("Can't get CredentialOptions from cache"); } 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 _fido2Storage.GetUsersByCredentialIdAsync(args.CredentialId); if (users.Count > 0) { return(false); } return(true); }; // 2. Verify and make the credentials var success = await _lib.MakeNewCredentialAsync(attestationResponse, options, callback); // 3. Store the credentials in db await _fido2Storage.AddCredentialToUser(options.User, new FidoStoredCredential { Username = options.User.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 }); // 4. return "ok" to the client var user = await _userManager.GetUserAsync(User); if (user == null) { return(Json(new CredentialMakeResult { Status = "error", ErrorMessage = $"Unable to load user with ID '{_userManager.GetUserId(User)}'." })); } await _userManager.SetTwoFactorEnabledAsync(user, true); if (await _userManager.CountRecoveryCodesAsync(user) == 0) { var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10); RecoveryCodes = recoveryCodes.ToArray(); } return(Json(success)); } catch (Exception e) { return(Json(new CredentialMakeResult { Status = "error", ErrorMessage = FormatException(e) })); } }
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) })); } }