I have developed a custom tool to send an email using token and office365.This tool has been integrated with Tibco Spotfire. It works well when I use it in Spotfire client(desktop) when I deploy to web , it does not acquire the token silently, I need to retrieve the cached token in web player since Spotfire does not allow pop-up and interactive mode in web is not an option.
I will select an account in interactive mode in desktop app and use it in web that is my requirement.
Here is code:
private async Task AccessTokenAsync() {
AuthenticationResult authToken = null;
// It's recommended to create a separate PublicClient Application for each tenant
// but only one CacheHelper object
var pca = CreatePublicClient("https://login.microsoftonline.com/organizations");
var cacheHelper = await CreateCacheHelperAsync().ConfigureAwait(false);
cacheHelper.RegisterCache(pca.UserTokenCache);
//To delete the accounts as requested by user from cached
if (!UseToken)
{
var accounts2Delete = await pca.GetAccountsAsync().ConfigureAwait(false);
foreach (var a2d in accounts2Delete)
{
// Console.WriteLine($"Removing account for {acc.Username}");
await pca.RemoveAsync(a2d).ConfigureAwait(false);
}
}
// IMPORTANT: you should ALWAYS try to get a token silently first
var accounts = await pca.GetAccountsAsync().ConfigureAwait(false);
if (accounts.Any()& (UseToken))
{
authToken = await pca.AcquireTokenSilent(Config.Scopes, accounts.FirstOrDefault())
.ExecuteAsync()
.ConfigureAwait(false);
}
else
{
UseToken = true;
authToken = await pca.AcquireTokenInteractive(Config.Scopes)
.ExecuteAsync()
.ConfigureAwait(false);
}
var oauth2 = new SaslMechanismOAuth2(authToken.Account.Username, authToken.AccessToken);
using (var client = new SmtpClient())
{
await client.ConnectAsync("outlook.office365.com", 587, SecureSocketOptions.StartTls);
await client.AuthenticateAsync(oauth2);
await client.SendAsync(emailMessage);
await client.DisconnectAsync(true);
}
}
private static IPublicClientApplication CreatePublicClient(string authority)
{
var appBuilder = PublicClientApplicationBuilder.Create(Config.ClientId)
.WithAuthority(authority)
.WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient");
// make sure to register this redirect URI for the interactive login to work
var app = appBuilder.Build();
return app;
}
private static async Task CreateCacheHelperAsync()
{
StorageCreationProperties storageProperties;
try
{
storageProperties = new StorageCreationPropertiesBuilder(
Config.CacheFileName,
Config.CacheDir)
.WithLinuxKeyring(
Config.LinuxKeyRingSchema,
Config.LinuxKeyRingCollection,
Config.LinuxKeyRingLabel,
Config.LinuxKeyRingAttr1,
Config.LinuxKeyRingAttr2)
.WithMacKeyChain(
Config.KeyChainServiceName,
Config.KeyChainAccountName)
.WithCacheChangedEvent( // do NOT use unless really necessary, high perf penalty!
Config.ClientId,
Config.Authority)
.Build();
var cacheHelper = await MsalCacheHelper.CreateAsync(
storageProperties).ConfigureAwait(false);
cacheHelper.VerifyPersistence();
return cacheHelper;
}
catch (MsalCachePersistenceException e)
{
// Console.WriteLine($"WARNING! Unable to encrypt tokens at rest." +
// $" Saving tokens in plaintext at {Path.Combine(Config.CacheDir, Config.CacheFileName)} ! Please protect this directory or delete the file after use");
Console.WriteLine($"Encryption exception: " + e);
storageProperties =
new StorageCreationPropertiesBuilder(
Config.CacheFileName + ".plaintext", // do not use the same file name so as not to overwrite the encypted version
Config.CacheDir)
.WithUnprotectedFile()
.Build();
var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties).ConfigureAwait(false);
cacheHelper.VerifyPersistence();
return cacheHelper;
}
}
My expectation is , once I registercached(cacheHelper.RegisterCache(pca.UserTokenCache) in desktop app , the token should be available further to use in silent mode in both windows and web application. When I open my application in browser I am getting this error
ATest.EmailHelper.EmailHelperException: Unable to send email ---> System.AggregateException: One or more errors occurred. ---> System.InvalidOperationException: Showing a modal dialog box or form when the application is not running in UserInteractive mode is not a valid operation. Specify the ServiceNotification or DefaultDesktopOnly style to display a notification from a service application. at Microsoft.Identity.Client.Platforms.Features.WinFormsLegacyWebUi.WebUI.d__20.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.AuthCodeRequestComponent.d__7.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.AuthCodeRequestComponent.d__4.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.d__11.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.d__9.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.Internal.Requests.RequestBase.d__12.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.ConfiguredTaskAwaitable1.ConfiguredTaskAwaiter.GetResult() at ATest.EmailHelper.EmailMessage.<AccessTokenAsync>d__28.MoveNext() --- End of inner exception stack trace --- at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken) at System.Threading.Tasks.Task.Wait() at ATest.EmailHelper.EmailMessage.Send() --- End of inner exception stack trace --- at ATest.EmailHelper.EmailMessage.Send() at Microsoft.Scripting.Interpreter.ActionCallInstruction
1.Run(InterpretedFrame frame) at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame) at Microsoft.Scripting.Interpreter.LightLambda.Run3[T0,T1,T2,TRet](T0 arg0, T1 arg1, T2 arg2) at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1) at Microsoft.Scripting.Interpreter.DynamicInstruction3.Run(InterpretedFrame frame) at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame) at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1) at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx) at Microsoft.Scripting.Hosting.ScriptSource.Execute(ScriptScope scope) at Spotfire.Dxp.Application.IronPython27.IronPythonScriptEngine.Execute(ScriptDefinition script, Dictionary
2 scope) at Spotfire.Dxp.Application.Scripting.ScriptService.Execute(ScriptDefinition script, Dictionary`2 scope, InternalLibraryManager internalLibraryManager, NotificationService notificationService)
Thanks in advance.
-Rajesh