diff --git a/sdk/identity/Azure.Identity/CHANGELOG.md b/sdk/identity/Azure.Identity/CHANGELOG.md
index c54656371a62..996201d289e5 100644
--- a/sdk/identity/Azure.Identity/CHANGELOG.md
+++ b/sdk/identity/Azure.Identity/CHANGELOG.md
@@ -4,6 +4,7 @@
### New Features
- Restoring Application Authentication APIs from 1.2.0-preview.6
- Added `IncludeX5CClaimHeader` to `ClientCertificateCredentialOptions` to enable subject name / issuer authentication with the `ClientCertificateCredential`.
+- Added `RedirectUri` to `InteractiveBrowserCredentialOptions` to enable authentication with user specified application with a custom redirect url.
### Fixes and improvements
- Fixed issue with non GUID Client Ids (Issue [#14585](https://github.com/Azure/azure-sdk-for-net/issues/14585))
diff --git a/sdk/identity/Azure.Identity/api/Azure.Identity.netstandard2.0.cs b/sdk/identity/Azure.Identity/api/Azure.Identity.netstandard2.0.cs
index a1696148f5fd..efb4966ab56f 100644
--- a/sdk/identity/Azure.Identity/api/Azure.Identity.netstandard2.0.cs
+++ b/sdk/identity/Azure.Identity/api/Azure.Identity.netstandard2.0.cs
@@ -178,6 +178,7 @@ public InteractiveBrowserCredentialOptions() { }
public string ClientId { get { throw null; } set { } }
public bool DisableAutomaticAuthentication { get { throw null; } set { } }
public bool EnablePersistentCache { get { throw null; } set { } }
+ public System.Uri RedirectUri { get { throw null; } set { } }
public string TenantId { get { throw null; } set { } }
}
public partial class ManagedIdentityCredential : Azure.Core.TokenCredential
diff --git a/sdk/identity/Azure.Identity/src/Constants.cs b/sdk/identity/Azure.Identity/src/Constants.cs
index 88ae55df539c..d1239a09afb3 100644
--- a/sdk/identity/Azure.Identity/src/Constants.cs
+++ b/sdk/identity/Azure.Identity/src/Constants.cs
@@ -20,6 +20,8 @@ internal class Constants
public static readonly TimeSpan SharedTokenCacheAccessRetryDelay = TimeSpan.FromMilliseconds(600);
+ public const string DefaultRedirectUrl = "http://localhost";
+
public static readonly string DefaultMsalTokenCacheDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ".IdentityService");
public const string DefaultMsalTokenCacheKeychainService = "Microsoft.Developer.IdentityService";
diff --git a/sdk/identity/Azure.Identity/src/InteractiveBrowserCredential.cs b/sdk/identity/Azure.Identity/src/InteractiveBrowserCredential.cs
index 96fa97d49d94..ccb5b6b5efc8 100644
--- a/sdk/identity/Azure.Identity/src/InteractiveBrowserCredential.cs
+++ b/sdk/identity/Azure.Identity/src/InteractiveBrowserCredential.cs
@@ -78,7 +78,9 @@ internal InteractiveBrowserCredential(string tenantId, string clientId, TokenCre
Pipeline = pipeline ?? CredentialPipeline.GetInstance(options);
- Client = client ?? new MsalPublicClient(Pipeline, tenantId, clientId, "http://localhost", options as ITokenCacheOptions);
+ var redirectUrl = (options as InteractiveBrowserCredentialOptions)?.RedirectUri?.AbsoluteUri ?? Constants.DefaultRedirectUrl;
+
+ Client = client ?? new MsalPublicClient(Pipeline, tenantId, clientId, redirectUrl, options as ITokenCacheOptions);
}
///
diff --git a/sdk/identity/Azure.Identity/src/InteractiveBrowserCredentialOptions.cs b/sdk/identity/Azure.Identity/src/InteractiveBrowserCredentialOptions.cs
index cb79ea1f5e0c..d972f70d2ed8 100644
--- a/sdk/identity/Azure.Identity/src/InteractiveBrowserCredentialOptions.cs
+++ b/sdk/identity/Azure.Identity/src/InteractiveBrowserCredentialOptions.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using System;
using System.Threading;
namespace Azure.Identity
@@ -36,6 +37,12 @@ public class InteractiveBrowserCredentialOptions : TokenCredentialOptions, IToke
///
public bool AllowUnencryptedCache { get; set; }
+ ///
+ /// Uri where the STS will call back the application with the security token. This parameter is not required if the caller is not using a custom . In
+ /// the case that the caller is using their own the value must match the redirect url specified when creating the application registration.
+ ///
+ public Uri RedirectUri { get; set; }
+
///
/// The captured from a previous authentication.
///
diff --git a/sdk/identity/Azure.Identity/src/MsalPublicClient.cs b/sdk/identity/Azure.Identity/src/MsalPublicClient.cs
index b1b628259d67..8841f476ed92 100644
--- a/sdk/identity/Azure.Identity/src/MsalPublicClient.cs
+++ b/sdk/identity/Azure.Identity/src/MsalPublicClient.cs
@@ -13,7 +13,7 @@ namespace Azure.Identity
{
internal class MsalPublicClient : MsalClientBase
{
- private readonly string _redirectUrl;
+ internal string RedirectUrl { get; }
protected MsalPublicClient()
{
@@ -22,7 +22,7 @@ protected MsalPublicClient()
public MsalPublicClient(CredentialPipeline pipeline, string tenantId, string clientId, string redirectUrl, ITokenCacheOptions cacheOptions)
: base(pipeline, tenantId, clientId, cacheOptions)
{
- _redirectUrl = redirectUrl;
+ RedirectUrl = redirectUrl;
}
protected override ValueTask CreateClientAsync(bool async, CancellationToken cancellationToken)
@@ -33,9 +33,9 @@ protected override ValueTask CreateClientAsync(bool as
PublicClientApplicationBuilder pubAppBuilder = PublicClientApplicationBuilder.Create(ClientId).WithAuthority(authorityUri).WithHttpClientFactory(new HttpPipelineClientFactory(Pipeline.HttpPipeline));
- if (!string.IsNullOrEmpty(_redirectUrl))
+ if (!string.IsNullOrEmpty(RedirectUrl))
{
- pubAppBuilder = pubAppBuilder.WithRedirectUri(_redirectUrl);
+ pubAppBuilder = pubAppBuilder.WithRedirectUri(RedirectUrl);
}
return new ValueTask(pubAppBuilder.Build());
diff --git a/sdk/identity/Azure.Identity/tests/InteractiveBrowserCredentialCtorTests.cs b/sdk/identity/Azure.Identity/tests/InteractiveBrowserCredentialCtorTests.cs
index 63979f6eacb8..62da55872a0f 100644
--- a/sdk/identity/Azure.Identity/tests/InteractiveBrowserCredentialCtorTests.cs
+++ b/sdk/identity/Azure.Identity/tests/InteractiveBrowserCredentialCtorTests.cs
@@ -38,7 +38,8 @@ public void ValidateConstructorOverload1()
DisableAutomaticAuthentication = true,
EnablePersistentCache = true,
AllowUnencryptedCache = true,
- AuthenticationRecord = new AuthenticationRecord()
+ AuthenticationRecord = new AuthenticationRecord(),
+ RedirectUri = new Uri("https://localhost:8080"),
};
credential = new InteractiveBrowserCredential(options);
@@ -118,6 +119,15 @@ public void AssertOptionsHonored(InteractiveBrowserCredentialOptions options, In
Assert.AreEqual(options.EnablePersistentCache, credential.Client.EnablePersistentCache);
Assert.AreEqual(options.AllowUnencryptedCache, credential.Client.AllowUnencryptedCache);
Assert.AreEqual(options.AuthenticationRecord, credential.Record);
+
+ if (options.RedirectUri != null)
+ {
+ Assert.AreEqual(options.RedirectUri, new Uri(credential.Client.RedirectUrl));
+ }
+ else
+ {
+ Assert.AreEqual(Constants.DefaultRedirectUrl, credential.Client.RedirectUrl);
+ }
}
}