I was able to solve/avoid the problem by re-writing my library. For the benefit of anyone else struggling with something similar, here's what I did, and the takeaway lesson from it.
My problem, fundamentally, was the result of trying to "keep trying to authenticate" in a base library by requesting new credentials from a derived library. The split was due to the fact that the base library is designed to be UI-agnostic. It neither knows nor cares what kind of UI framework it's being called from.
The derived library (and the ultimate app) are >>not<< UI-agnostic. In fact, they are specifically designed to work with WinUI3 aka the Windows App SDK.
The Windows App SDK encompasses an interesting mix of synchronous and asynchronous code (probably because it's descended from platforms that were developed long before the async/await conceptual framework existed). That mix enormously complicated handling my desire to "keep asking the user for credentials until they validate or give up".
The solution was recognizing "keep asking the user for credentials" should not start from within the UI-agnostic base library. It should simply focus on trying to authenticate and reporting the results. All of the logic for "keep asking" should be in the UI-aware library (and app), which should simply use the authentication process as a service.
Once I realized this I rewrote the code base, solved/sidestepped the problem, and got to throw away a bunch of complex code. Which is always, in my experience, a sign that the way you used to be doing something was "wrong".