Send Office365 e-mail using AzureAD authenticated user - asp.net-core-mvc

I have a .Net Core MVC Web application that authenticates the user using AzureAD. At some stage I need to send an e-mail on behalf of that user.
I searched for some options and apparently I can do that using Microsoft Exchange Service or Office365 but for both options I need to get the user's credential.
An example using Office365 is below however I do not know how to user the signed in info to pass to the SMTP server.
My (partial) HomeController:
private readonly ClaimsPrincipal _principal;
public HomeController(IPrincipal principal){
_principal = principal as ClaimsPrincipal;
}
I can use _principal.FindFirst(ClaimTypes.Upn).Value to get the user's e-mail address but how do I get the password?
Am I in the right path or am I missing anything?
public static void SendEmail(string toAddress, string fromAddress, string subject, string content)
{
SmtpClient client = new SmtpClient();
client.Credentials = new NetworkCredential("", "");
client.Port = 587;
client.Host = "smtp.office365.com";
client.EnableSsl = true;
MailMessage newMail = new MailMessage();
newMail.To.Add(toAddress);
newMail.From = new MailAddress(fromAddress);
newMail.Subject = subject;
newMail.IsBodyHtml = true;
newMail.Body = "<html><body>" + content + "</body></html>";
client.Send(newMail);
}
I'm sorry if this is a broad question but I really need some light on how to achieve this. I'm happy to provide more details if necessary.

At some stage I need to send an e-mail on behalf of that user.
Per my understanding, you could use the cloud-based email service SendGrid to handle email delivery on behalf of your authenticated user's email address. The code for sending the email to test02#example.com for the user test01#example.com would look like this:
var client = new SendGridClient("<SendGrid-ApiKey>");
var msg = new SendGridMessage()
{
From = new EmailAddress("test01#example.com", "test01"),
Subject = "Hello World from the SendGrid CSharp SDK!",
PlainTextContent = "Hello, Email!",
HtmlContent = "<strong>Hello, Email!</strong>"
};
msg.AddTo(new EmailAddress("test02#example.com", "Test02"));
var response = await client.SendEmailAsync(msg);
Detailed tutorial, you could follow sendgrid-csharp.

Related

How to authenticate and create a Push notification Subscription to get Outlook email #mentions?

I am trying to authenticate in order to use Outlook REST API to subscription to Outlook emails via Push Subscription. I am using this documentation for reference: https://learn.microsoft.com/en-us/previous-versions/office/office-365-api/api/version-2.0/notify-rest-operations
I created an app in portal.azure.com and provided Required permission on "Read user mail" under "Office 365 Exchange Online" api.
Grant Required permission of Read user mail Screenshot
I am using the following code to get a Bearer token using "Microsoft.Identity.Client" nuget package. But I am still not able to subscribe to the Outlook Push Notification REST API and I am getting 401 Unauthorized error.
static async Task<AuthenticationResult> AuthorizeAsync(string clientId)
{
var authority = $"https://login.microsoftonline.com/{tenantName}.onmicrosoft.com";
var app = new PublicClientApplication(clientId, authority);
string[] scopes = new string[] { "Mail.Read" };
var accounts = await app.GetAccountsAsync();
AuthenticationResult authenticationResult = null;
if (accounts.Any())
{
authenticationResult = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
}
else
{
try
{
var username = $"{Environment.UserName}#microsoft.com";
authenticationResult = await app.AcquireTokenByIntegratedWindowsAuthAsync(scopes, username);
}
Has anyone worked on a similar issue to authenticate and create a Push notification Subscription to get Outlook emails and can please help?

Proactive Bot Messaging - CreateDirectConversation - unauthorized exception

I am creating a bot to proactively start a conversation with an account I have never had a previous conversation with. I have created another controller that I am posting to and doing the following steps:
public class OutboundController : ApiController {
public HttpResponseMessage Post([FromUri] int id, [FromBody] OutboundData outboundData) {
MicrosoftAppCredentials.TrustServiceUrl(outboundData.ServiceUrl);
//create conversation
var connector = new ConnectorClient(new Uri(outboundData.ServiceUrl));
var botAccount = new ChannelAccount { Id = outboundData.FromAccountId, Name = outboundData.FromAccountName };
var toAccount = new ChannelAccount { Id = outboundData.ToAccountId, Name = outboundData.ToAccountName };
if(!MicrosoftAppCredentials.IsTrustedServiceUrl(outboundData.ServiceUrl)) {
throw new Exception("service URL is not trusted!");
}
var conversationResponse = connector.Conversations.CreateDirectConversation(botAccount, toAccount);
var client = new BuslogicClient();
var confirmData = client.GetOutboundData(id);
var greetingMessage = CreateGreetingMessage(confirmData);
var convoMessage = Activity.CreateMessageActivity();
convoMessage.Text = greetingMessage;
convoMessage.From = botAccount;
convoMessage.Recipient = toAccount;
convoMessage.Conversation = new ConversationAccount(id: conversationResponse.Id);
convoMessage.Locale = "en-Us";
connector.Conversations.SendToConversationAsync((Activity)convoMessage);
string message = string.Format("I received correlationid:{0} and started conversationId:{1}", id, conversationResponse.Id);
var response = Request.CreateResponse(HttpStatusCode.OK, message);
return response;
}
When I call connector.Conversations.CreateDirectConversation I am getting the following exception: Additional information: Authorization for Microsoft App ID [ID] failed with status code Unauthorized and reason phrase 'Unauthorized'. If I do this with appId and password blank everything works fine in the channel emulator. I've tried providing the MicrosoftAppCredentials to the constructor of the ConnectorClient, but that has no affect. I've read on other threads that the service URL must be trusted so I used MicrosoftAppCredentials.TrustServiceUrl.
versions I am using:
BotBuilder 3.5.3
Channel Emulator 3.0.0.59
The use-case for my bot is to post to the outbound controller with some user info to create a proactive message to be sent out (specifically SMS). If the user responds to my message it will be intercepted by the messages controller and passed to my dialogs for further processing and conversation responses on that same channel.
I've also taken a look at: https://github.com/Microsoft/BotBuilder/issues/2155 but don't quite understand solution described in the comments or if it even pertains to the issue I'm trying to solve.
Any suggestions or help would be appreciated!
You need to pass credentials explicitly to connector:
var credentials = new MicrosoftAppCredentials("YoursMicrosoftAppId", "YoursMicrosoftAppPassword");
var connector = new ConnectorClient(serviceUrl, credentials);

Sending message from bot to a Skype User using Botframework Version 3

Updated
I am developing a Skype bot with 1:1 conversation with Bot Framework.
In that I have a WebHook method which will call from an external service and sends message to my bot, then my bot will send that message to a skype user.
The following code is for v1 in message controller along with api/messages post method
public async Task<Message> Post([FromBody]Message message){}
[Route("~/api/messages/hook")]
[HttpPost]
public async Task<IHttpActionResult> WebHook([FromBody]WebHookMessage message)
{
if (message.Type == "EmotionUpdate")
{
const string fromBotAddress = "<Skype Bot ID here>";
const string toBotAddress = "<Destination Skype name here>";
var text = resolveEmoji(message.Data);
using (var client = new ConnectorClient())
{
var outMessage = new Message
{
To = new ChannelAccount("skype", address: toBotAddress , isBot: false),
From = new ChannelAccount("skype", address: $"8:{fromBotAddress}", isBot: true),
Text = text,
Language = "en",
};
await client.Messages.SendMessageAsync(outMessage);
}
}
return Ok();
}
I will call above WebHook from another service, so that my bot will send messages to the respective skype user.
Can anyone please help me how can I achieve the same in V3 bot framework?
I tried the following but not working
const string fromBotAddress = "Microsoft App ID of my bot";
const string toBotAddress = "skype username";
WebHookMessage processedData = JsonConvert.DeserializeObject<WebHookMessage>(message);
var text = resolveEmoji(processedData.Data);
using (var client = new ConnectorClient(new Uri("https://botname.azurewebsites.net/")
, "Bot Microsoft App Id", "Bot Microsoft App secret",null))
{
var outMessage = new Activity
{
ReplyToId = toBotAddress,
From = new ChannelAccount("skype", $"8:{fromBotAddress}"),
Text = text
};
await client.Conversations.SendToConversationAsync(outMessage);
}
But it is not working, finally what I want to achieve is I want my bot send a message to a user any time how we will send message to a person in skype.
The following code works, but there are some things that are not that obvious that I figured out (tested on Skype channel)
When a user interacts with the bot the user is allocated an id that can only be used from a specific bot..for example: I have multiple bots each using a skype channel. When I send a message from my skype user to bot A the id is different than for bot B. In the previous version of the bot framework I could just send a message to my real skype user id, but not anymore. In a way it simplifies the whole process because you only need the recipient's id and the framework takes care of the rest, so you don't have to specify a sender or bot Id (I guessed all that is linked behind the scenes)
[Route("OutboundMessages/Skype")]
[HttpPost]
public async Task<HttpResponseMessage> SendSkypeMessage(SkypePayload payload)
{
using (var client = new ConnectorClient(new Uri("https://skype.botframework.com")))
{
var conversation = await client.Conversations.CreateDirectConversationAsync(new ChannelAccount(), new ChannelAccount(payload.ToSkypeId));
IMessageActivity message = Activity.CreateMessageActivity();
message.From = new ChannelAccount();
message.Recipient = new ChannelAccount(payload.ToSkypeId);
message.Conversation = new ConversationAccount { Id= conversation.Id };
message.Text = payload.MessageBody;
await client.Conversations.SendToConversationAsync((Activity)message);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
I'm not sure I understand what you're trying to do. If you'd like to answer a message (activity), try something like this:
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
var reply = activity.createReply(text, "en");
await connector.Conversations.ReplyToActivityAsync(reply);
Activity.createReply switches the From and Recipient fields from the incoming activity. You can also try setting these field manually.
UPDATE
You need to create a ConnectorClient to the Skype Connector Service, not to your bot! So try with the Uri http://skype.botframework.com it might work.
However, I don't think you can message a user on Skype without receiving a message from it in the first place (i.e. your bot needs to be added to the user's contacts). Once you have an incoming message from the user, you can use it the create replies, just as described above.
WebHookMessage processedData = JsonConvert.DeserializeObject<WebHookMessage>(message);
var text = resolveEmoji(processedData.Data);
var client = new ConnectorClient(new Uri(activity.serviceUrl));
var outMessage = activity.createReply(text);
await client.Conversations.SendToConversationAsync(outMessage);
activity is a message received from the given user earlier. In this case, activity.serviceUrl should be http://skype.botframework.com, but generally you should not rely on this.
You can try to create the activity (outMessage) manually; for that, I'd recommend inspecting the From and Recipient fields of a message coming from a Skype user and setting these fields accordingly. However, as mentioned before, your bot needs to be added to the user's contacts, so at this point it will have received a message from the user.

Create mail in Exchange Online inbox programatically

Have been facing an issue since days about EWS. So my scenario is;
I was to programmatically sync GMAIL and EXCHANGE ONLINE. So here is what I have done;
Connect to Gmail using Gmail API
Fetch mail from gmail get the email body, to, from, attachment and
all other thing
connect to Exchange online using EWS 2.0
Now the problem is here, how can I create an email in Inbox which looks like incoming mail from the sender;
Here is the code I have done;
_service = new ExchangeService(ExchangeVersion.Exchange2013);
_service.TraceEnabled = true;
_service.Credentials = new WebCredentials("admin#xyz.onmicrosoft.com", "password");
_service.Url = new Uri("https://outlook.office365.com/EWS/Exchange.asmx");
_service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "xyz#xyz.onmicrosoft.com");
EmailMessage message = new EmailMessage(_service);
Random r = new Random();
message.Subject = "Email Message";
message.From = new EmailAddress("xyz#gmail.com");
message.Sender = new EmailAddress("xyz#gmail.com");
message.Body = new MessageBody(BodyType.HTML, "<HTML><body><h1>This is a voice mail.</h1></BODY></HTML>");
message.ToRecipients.Add(new EmailAddress(""));
message.Save(WellKnownFolderName.Inbox);
This way its creating an email in inbox but it shows as a draft mail. I dont want it, I want it to look as RECEIVED mail.
Am I doing anything wrong?
You need to set a couple of properties before saving the message.
// Set a delivery time
ExtendedPropertyDefinition PidTagMessageDeliveryTime =
new ExtendedPropertyDefinition(0x0E06, MapiPropertyType.SystemTime);
DateTime deliveryTime = DateTime.Now; // Or whatever deliver time you want
message.SetExtendedProperty(PidTagMessageDeliveryTime, deliveryTime);
// Indicate that this email is not a draft. Otherwise, the email will appear as a
// draft to clients.
ExtendedPropertyDefinition PR_MESSAGE_FLAGS_msgflag_read = new ExtendedPropertyDefinition(3591, MapiPropertyType.Integer);
message.SetExtendedProperty(PR_MESSAGE_FLAGS_msgflag_read, 1);
These properties aren't settable after the items is saved, so it's important to do it before the first Save call.

Google AUTH API Application Type, how important is it?

I've been doing a lot tinkering around with the authentication stuff using the .NET libraries provided by Google.
We have both a desktop and web-app side, and what we want to achieve is to authenticate ONCE, either on the desktop or the web side, and store the refresh token, and reuse it both on the web side and the desktop side.
So the situation is like so, on the desktop side, when there's no saved existing AccessToken's and RefreshToken's, we will ask the user to authenticate via this code:
using (var stream = new FileStream("client_secrets_desktop.json", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
new[] { GmailService.Scope.GmailReadonly, GmailService.Scope.GmailCompose },
"someemail#gmail.com", CancellationToken.None);
}
In this case the Client ID and Secret is of an Application type Installed Application.
On the web-application side, if there's also no refresh token yet then I'm using DotNetOpenAuth to trigger the authentication, here's the code snippet:
const string clientID = "someclientid";
const string clientSecret = "somesecret";
const string redirectUri = "http://localhost/Home/oauth2callback";
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://accounts.google.com/o/oauth2/auth"),
TokenEndpoint = new Uri("https://accounts.google.com/o/oauth2/token"),
ProtocolVersion = ProtocolVersion.V20
};
public ActionResult AuthenticateMe()
{
List<string> scope = new List<string>
{
GmailService.Scope.GmailCompose,
GmailService.Scope.GmailReadonly,
GmailService.Scope.GmailModify
};
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
// Here redirect to authorization site occurs
OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
scope, new Uri(redirectUri));
response.Headers["Location"] += "&access_type=offline&approval_prompt=force";
return response.AsActionResult();
}
public void oauth2callback()
{
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
consumer.ClientCredentialApplicator =
ClientCredentialApplicator.PostParameter(clientSecret);
IAuthorizationState grantedAccess = consumer.ProcessUserAuthorization(null);
string accessToken = grantedAccess.AccessToken;
}
Here is where I want to confirm my suspicions.
When there is a RefreshToken that exists, we use the following code snippet to call the Gmail API's
UserCredential uc = new UserCredential(flow, "someemail#gmail.com", new TokenResponse()
{
AccessToken = "lastaccesstoken",
TokenType = "Bearer",
RefreshToken = "supersecretrefreshtoken"
});
var refreshState = await uc.RefreshTokenAsync(CancellationToken.None);
var svc = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = uc,
ApplicationName = "Gmail Test",
});
Here's the thing I noticed is that, for me to be able to use the refresh token to refresh from either the desktop or the web side, the refresh token needs to be generated through the same client ID/secret combination. I've tested it and it seems like it's fine if we use Installed application as the application type for the Client ID for both the desktop and the web, but my question I guess is, these application type's for the client IDs, do they matter so much?
Am I doing anything wrong to do it this way?
Thanks in advance

Resources