public async Task <LoginResponse> MakeAssertion([FromBody][Required] MakeAssertionRequest request) { using (AuthLogic.Disable()) using (Transaction tr = new Transaction()) { var assertionOptions = Database.Retrieve <WebAuthnAssertionOptionsEntity>(request.AssertionOptionsId); var options = AssertionOptions.FromJson(assertionOptions.Json); var cred = Database.Query <WebAuthnCredentialEntity>().SingleEx(cred => cred.CredentialId == request.AssertionRawResponse.Id); var res = await fido2.MakeAssertionAsync(request.AssertionRawResponse, options, cred.PublicKey, (uint)cred.Counter, (args) => { if (!MemoryExtensions.SequenceEqual <byte>(cred.CredentialId, args.CredentialId)) { return(Task.FromResult(false)); } var userId = Encoding.UTF8.GetBytes(cred.User.Id.ToString()); if (!MemoryExtensions.SequenceEqual <byte>(userId, args.UserHandle)) { return(Task.FromResult(false)); } return(Task.FromResult(true)); }); cred.Counter++; cred.Save(); var user = cred.User.RetrieveAndForget(); AuthServer.OnUserPreLogin(ControllerContext, user); AuthServer.AddUserSession(ControllerContext, user); var token = AuthTokenServer.CreateToken(user); return(tr.Commit(new LoginResponse { userEntity = user, token = token, authenticationType = "webauthn" })); } }
public async Task <ActionResult> MakeAssertion([ModelBinder(typeof(NewtonsoftJsonAdapter.Binder))] MakeAssertionRequest request) { try { // 1. Get the assertion options we sent the client var jsonOptions = TestUsers.FidoAttestationOptions[request.SessionId]; var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database var creds = TestUsers.FidoCredentials.Where(c => c.Descriptor.Id.SequenceEqual(request.RawResponse.Id)).FirstOrDefault(); if (creds == null) { return(BadRequest("unknown credentials")); } // 3. Get credential counter from database var storedCounter = creds.SignatureCounter; // 4. Create callback to check if userhandle owns the credentialId IsUserHandleOwnerOfCredentialIdAsync callback = async(args) => { var storedCreds = TestUsers.FidoCredentials.Where(c => c.UserHandle.SequenceEqual(args.UserHandle)); return(storedCreds.Any(c => c.Descriptor.Id.SequenceEqual(args.CredentialId))); }; // 5. Make the assertion var res = await _lib.MakeAssertionAsync( assertionResponse : request.RawResponse, originalOptions : options, storedPublicKey : creds.PublicKey, storedSignatureCounter : storedCounter, isUserHandleOwnerOfCredentialIdCallback : callback); // 6. Store the updated counter TestUsers.FidoCredentials.Where(c => c.Descriptor.Id.SequenceEqual(res.CredentialId)).FirstOrDefault().SignatureCounter = res.Counter; // 7. return OK to client if (string.Equals(res.Status, "ok", StringComparison.InvariantCultureIgnoreCase)) { //actually loging the user in var context = await _interaction.GetAuthorizationContextAsync(request.ReturnUrl); var dbUser = TestUsers.Users.FirstOrDefault(u => string.Equals(u.SubjectId, creds.SubjectId, StringComparison.InvariantCultureIgnoreCase)); AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && request.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; } ; var isuser = new IdentityServerUser(dbUser.SubjectId) { DisplayName = dbUser.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return(this.NewtonsoftJsonResult(new MakeAssertionResponse { Status = "ok", Response = res, ReturnUrl = request.ReturnUrl })); } // request for a local page if (Url.IsLocalUrl(request.ReturnUrl)) { return(this.NewtonsoftJsonResult(new MakeAssertionResponse { Status = "ok", Response = res, ReturnUrl = request.ReturnUrl })); } else if (string.IsNullOrEmpty(request.ReturnUrl)) { return(this.NewtonsoftJsonResult(new MakeAssertionResponse { Status = "ok", Response = res, ReturnUrl = "~/" })); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } else { return(BadRequest("not ok")); } } catch (Exception e) { return(BadRequest(new AssertionVerificationResult { Status = "error", ErrorMessage = FormatException(e) })); } }