Comments (22)
Thank you for your feedback. Tagging and routing to the team member best able to assist.
from azure-sdk-for-net.
Hi @WolfgangHG
Yes, it seems MSAL did make a change, which only affects apps targeting netx.x--windows
- it is described in their changelog here
Would you mind trying to use our Broker package, which uses the Web Account Manager? I think it will work with the embedded browser option.
After adding the reference, you would change your options code to this:
// get the window handle of the form
IntPtr hwnd = this.Handle;
InteractiveBrowserCredentialBrokerOptions options = new InteractiveBrowserCredentialBrokerOptions(parentWindowHandle: hwnd)
{
TenantId = this.textBoxTenant.Text,
ClientId = this.textBoxClientID.Text,
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
RedirectUri = new Uri("http://localhost"),
BrowserCustomization = customizationOptions
};
from azure-sdk-for-net.
Hi @WolfgangHG. Thank you for opening this issue and giving us the opportunity to assist. To help our team better understand your issue and the details of your scenario please provide a response to the question asked above or the information requested above. This will help us more accurately address your issue.
from azure-sdk-for-net.
@christothes I got it working after having added a new redirect URI in Azure (specifying "http://localhost" in InteractiveBrowserCredentialBrokerOptions does not seem to have an effect) :
AADSTS50011: The redirect URI 'ms-appx-web://Microsoft.AAD.BrokerPlugin/<my_app_id>' specified in the request does not match the redirect URIs configured for the application '<my_app_id>'. Make sure the redirect URI sent in the request matches one added to your application in the Azure portal. Navigate to https://aka.ms/redirectUriMismatchError to learn more about how to fix this.
Just for the records: https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity.Broker/README.md#redirect-uris
This is bad, as we would have to tell all of our customers to add a new redirect uri to their azure configuration - and the url is specific for each customer.
The "BrowserCustomizationOptions.UseEmbeddedWebView" option does not seem to have any effect, it is always a login dialog. But it does not show the list of previously used logins (and probably does not allow selecting an already logged in user). This worked with Azure.Identity 1.11.3.
The login form does not seem to be really modal to my sample, though I set the window handle argument - at least while debugging. When switching to a different application while the login dialog is shown, I cannot click my app in taskbar to activate the login form again.
from azure-sdk-for-net.
Glad you got it working.
The "BrowserCustomizationOptions.UseEmbeddedWebView" option does not seem to have any effect, it is always a login dialog. But it does not show the list of previously used logins (and probably does not allow selecting an already logged in user). This worked with Azure.Identity 1.11.3. The login form does not seem to be really modal to my sample, though I set the window handle argument - at least while debugging. When switching to a different application while the login dialog is shown, I cannot click my app in taskbar to activate the login form again.
Yes, you are correct. In the case of the broker based login, the window is owned by the operating system. The window handle is only used to ensure it appears above the window related to the handle.
from azure-sdk-for-net.
Hi @WolfgangHG. Thank you for opening this issue and giving us the opportunity to assist. We believe that this has been addressed. If you feel that further discussion is needed, please add a comment with the text "/unresolve" to remove the "issue-addressed" label and continue the conversation.
from azure-sdk-for-net.
@christothes Do you internally use a "PublicClientApplicationBuilder"? If yes: is there any chance to use the "PublicClientApplicationBuilder.WithWindowsEmbeddedBrowserSupport" extension method approach suggested by Microsoft.Identity.Client? I hope that this one causes less trouble than the Broker approach (change to the redirect url required, clunky modality)?
You set the status to "issue-addressed", but currently we have only found a workaround that I don't like ;-).
from azure-sdk-for-net.
/unresolve
from azure-sdk-for-net.
@christothes Do you internally use a "PublicClientApplicationBuilder"? If yes: is there any chance to use the "PublicClientApplicationBuilder.WithWindowsEmbeddedBrowserSupport" extension method approach suggested by Microsoft.Identity.Client? I hope that this one causes less trouble than the Broker approach (change to the redirect url required, clunky modality)?
We don't intend to expose the functionality for WithWindowsEmbeddedBrowserSupport
for the same reason this behavior changed in MSAL - it requires a dependency on Windows Forms to work properly, and we don't want everyone to require that.
from azure-sdk-for-net.
Hi @WolfgangHG. Thank you for opening this issue and giving us the opportunity to assist. To help our team better understand your issue and the details of your scenario please provide a response to the question asked above or the information requested above. This will help us more accurately address your issue.
from azure-sdk-for-net.
@christothes I took a look at the code. It seems I cannot extend InteractiveBrowserCredential
and call PublicClientApplicationBuilder.WithWindowsEmbeddedBrowserSupport
myself. Or do you see a change to do so?
It might work if the interface Azure.Identity.IMsalPublicClientInitializerOptions
was public and the BeforeBuildClient
property would be a Func
instead of an action. Then I could subclass InteractiveBrowserCredential and additionally implement this interface.
Attached is a diff of the necessary changes to Azure.Identity. What do you think?
diff.txt
With this code, I could create my own options subclass:
public class MyMsalPublicClientInitializerOptions : InteractiveBrowserCredentialOptions, IMsalPublicClientInitializerOptions
{
Func<PublicClientApplicationBuilder, PublicClientApplicationBuilder> IMsalPublicClientInitializerOptions.BeforeBuildClient
{
get
{
return new Func<PublicClientApplicationBuilder, PublicClientApplicationBuilder>(builder => builder.WithWindowsEmbeddedBrowserSupport());
}
}
bool IMsalPublicClientInitializerOptions.UseDefaultBrokerAccount { get; set; }
}
But I assume the interface "IMsalPublicClientInitializerOptions" is more of an internal hack for some testing purposes, so I am not sure whether this is the proper place to extend Azure.Identity.
I would prefer to move "BeforeBuildClient" to "InteractiveBrowserCredentialOptions", so that "IMsalPublicClientInitializerOptions" could be kept internal.
from azure-sdk-for-net.
Another option could be to subclass Azure.Identity.MsalPublicClient
(also not public) and enhance InteractiveBrowserCredential
so that the Client
can be specified in some constructor. But this seems to be a lot more code changes.
from azure-sdk-for-net.
Hi @WolfgangHG -
We avoid exposing any MSAL types in our public API surface, so I'm afraid there is no way for us to expose this functionality. If you require full control of the underlying MSAL APIs, I'd recommend using Microsoft.Identity.Client
directly in your application.
from azure-sdk-for-net.
@christothes This is unfortunately not possible I fear. I start from the Microsoft Graph .NET Client Library, and the https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/dev/src/Microsoft.Graph/GraphServiceClient.cs has only constructors that accept TokenCredential or IAuthenticationProvider.
They suggest in their doc to use Azure.Identity.
Do you have a suggestion or a sample how to create a winforms login without Azure.Identity? I actually don't want to copy a lot of lines of code to call builder.WithWindowsEmbeddedBrowserSupport
.
What about my suggestion to move the "BeforeBuildClient" property to "InteractiveBrowserCredentialOptions", so that "IMsalPublicClientInitializerOptions" could be kept internal? This does not expose MSAL classes I think.
from azure-sdk-for-net.
One option would be to create your own implementation of TokenCredential
and in the implementation, use the PublicClientApplication, similar to how the InteractiveBrowserCredential does in Azure.Identity. See this example
from azure-sdk-for-net.
Well, I got it to work, but I don't like the solution:
string[] scopes = new string[] { "Calendars.Read" };
var app = PublicClientApplicationBuilder.Create(this.textBoxClientID.Text)
.WithDefaultRedirectUri()
.WithTenantId(this.textBoxTenant.Text)
.WithWindowsEmbeddedBrowserSupport()
.Build();
AuthenticationResult result = await app.AcquireTokenInteractive(scopes).ExecuteAsync();
MyTokenCredential mtc = new MyTokenCredential(result);
GraphServiceClient graphClient = new GraphServiceClient(mtc);
Calendar cal = await graphClient.Me.Calendar.GetAsync();
MessageBox.Show(this, "Calendar ID: " + cal.Id);
Class "MyTokenCredential" is simple:
public class MyTokenCredential : TokenCredential
{
AuthenticationResult result;
public MyTokenCredential(AuthenticationResult result)
{
this.result = result;
}
public override ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
AccessToken token = new AccessToken(this.result.AccessToken, this.result.ExpiresOn);
return new ValueTask<AccessToken>(token);
}
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
Could you comment on whether this is reasonable? Fortunately, our app does not need features like token refresh, as we use the interactive login only for short term operations and get a fresh token before each operation (at least I hope our usage works like this).
But with this code, we probably loose all the features of Azure.Identity.
from azure-sdk-for-net.
Your MyTokenCredential
implementation should be acquiring the token from the PublicClientApplication
inside GetTokenAsync
, and initializing the builder in the constructor. I wouldn't recommend just passing the AuthenticationResult
to MyTokenCredential
and having it use that static value.
I'd also recommend you acquire the token similar to how the example linked in my previous response does it so that you only get prompted for credentials the first time GetTokenAsync
is called. MSAL will handle caching and token refresh automatically.
from azure-sdk-for-net.
You mean something like this?
In the form button click:
private MyTokenCredential mtc;
private async void buttonDoSomething_Click(object sender, EventArgs e)
{
if (this.mtc == null)
{
mtc = new MyTokenCredential(this.textBoxTenant.Text, this.textBoxClientID.Text);
}
GraphServiceClient graphClient = new GraphServiceClient(mtc);
Calendar cal = await graphClient.Me.Calendar.GetAsync();
MessageBox.Show(this, "Calendar ID: " + cal.Id);
}
Class "MyTokenCredential" looks like this:
public class MyTokenCredential : TokenCredential
{
private IPublicClientApplication clientApp;
public MyTokenCredential(string _tenant, string _clientId)
{
this.clientApp = PublicClientApplicationBuilder.Create(_clientId)
.WithDefaultRedirectUri()
.WithTenantId(_tenant)
.WithWindowsEmbeddedBrowserSupport()
.Build();
}
public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
string[] scopes = new string[] { "Calendars.Read" };
AuthenticationResult result;
try
{
var accounts = await this.clientApp.GetAccountsAsync();
result = await this.clientApp.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
result = await this.clientApp.AcquireTokenInteractive(scopes).ExecuteAsync();
}
AccessToken token = new AccessToken(result.AccessToken, result.ExpiresOn);
return token;
}
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
from azure-sdk-for-net.
@christothes May I send a little reminder about my latest workaround suggestion?
from azure-sdk-for-net.
Hi @WolfgangHG - yes, that looks better.
from azure-sdk-for-net.
Thanks for the feedback.
Here is a slightly improved code snippet:
-as "GraphServiceClient" has a scopes parameter, I could use the "TokenRequestContext.Scopes"
-calling "WithParentActivityOrWindow" makes the login screen a perfect modal dialog.
In the form button click:
private MyTokenCredential mtc;
private async void buttonDoSomething_Click(object sender, EventArgs e)
{
if (this.mtc == null)
{
mtc = new MyTokenCredential(this.textBoxTenant.Text, this.textBoxClientID.Text, this);
}
string[] scopes = new string[] { "Calendars.Read" };
GraphServiceClient graphClient = new GraphServiceClient(mtc, scopes);
Calendar cal = await graphClient.Me.Calendar.GetAsync();
MessageBox.Show(this, "Calendar ID: " + cal.Id);
}
Class "MyTokenCredential" looks like this:
public class MyTokenCredential : TokenCredential
{
private IPublicClientApplication clientApp;
public MyTokenCredential(string _tenant, string _clientId, Form _formParent)
{
this.clientApp = PublicClientApplicationBuilder.Create(_clientId)
.WithDefaultRedirectUri()
.WithTenantId(_tenant)
.WithWindowsEmbeddedBrowserSupport()
.WithParentActivityOrWindow ( () => _formParent.Handle)
.Build();
}
public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
AuthenticationResult result;
try
{
var accounts = await this.clientApp.GetAccountsAsync();
result = await this.clientApp.AcquireTokenSilent(requestContext.Scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
catch (MsalUiRequiredException)
{
result = await this.clientApp.AcquireTokenInteractive(requestContext.Scopes).ExecuteAsync();
}
AccessToken token = new AccessToken(result.AccessToken, result.ExpiresOn);
return token;
}
public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
from azure-sdk-for-net.
@christothes I try one last time to convince you to add some callback that allows customization of the "IPublicClientApplication" that is generated internally in Azure.Identity ;-). Is there any chance that you add e.g. a "BeforeBuild" func to InteractiveBrowserCredentialOptions or InteractiveBrowserCredential so that I could create a "IPublicClientApplication" with "WithWindowsEmbeddedBrowserSupport"?
As I wrote before, my workaround works, but I loose all the smart code that you added in Azure.Identity and that I probably don't even have knowledge of.
If this is not possible, I suggest to add the findings of this ticket to some wiki page, as others might be affected by the fact that Azure.Identity does not use the embedded webview since 1.11.4. But Readme.md does not seem the proper place to do so. I found https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/dev/docs/tokencredentials.md in the GraphSDK project which has sample snippets.
from azure-sdk-for-net.
Related Issues (20)
- [BUG] - ACA Jobs - Stopping Job .Net SDK throwing The requested operation requires an element of type 'Object', but the target element has type 'String'. HOT 8
- [BUG] Azure Blob Storage upload threw InvalidQueryParameterValue when using SDK HOT 2
- Valid values for the ImageReference Properties HOT 1
- Type forward system events from Azure.Messaging.EventGrid to Azure.Messaging.EventGrid.SystemEvents
- My version is not in this enum HOT 7
- Add sample demonstrating publishing CNCF CloudEvent to Event Grid namespace topic
- Regarding misleading AuthenticationFailedException - The current credential is not configured to acquire tokens for tenant.... HOT 10
- [QUERY] Will the Azure Content Safety Prompt Shield API support Managed Service Identity (MSI) after General Availability (GA)? HOT 1
- [BUG] token retrieval intermittently stuck in BearerTokenAuthenticationPolicy in Azure.Core 1.40.0 HOT 9
- [BUG] Deserializing `null` BoundingBox throws an exception HOT 11
- [BUG] GetProperties method does not return metadata even thougvh the summary says so HOT 2
- [FEATURE REQ] PublicNetworkAccess.SecuredByPerimeterValue does not exist but is supported by the Storage API HOT 3
- [BUG] Storage operations are hanging HOT 4
- [QUERY] Multiple questions about beta version of package Azure.Maps.Routing HOT 3
- [FEATURE REQ] How to set UserAccountId in AppInsights with Azure.Monitor.OpenTelemetry.Exporter HOT 3
- Update the doc of InteractiveBrowserCred and DeviceCodeCred
- Ali HOT 1
- [FEATURE REQ] RunCommandInput.CommandId Property should support a type in addition to/instead of a string HOT 3
- [QUERY] QueueTrigger and UpdateMessage (visibilityTimeout) HOT 1
- [BUG] Azure OpenAI Assistants (C#) do not work as expected (mixing up roles etc) HOT 5
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from azure-sdk-for-net.