I'm trying to retrieve and read emails from my Outlook mail. Unfortunately my mailbox uses Authenticity, which I need to deal with. I have tried a mailbox that does not use Authenticity and the code works. I followed the instructions here https://www.emailarchitect.net/eagetmail/ex/c/22.aspx
(I used the library to read a mailbox that does not use OAuth). So I registered my application on Microsoft Azure as instructed (except for authentication, which was the last step). Unfortunately I get this error System.ComponentModel.Win32Exception
HResult=0x80004005 Message=System cannot find the specified file.
Source=System.Diagnostics.Process
I also tried another library
https://afterlogic.com/mailbee-net/docs/OAuth2MicrosoftRegularAccountsInstalledApps.html
But with the same result
It is larger project, so I will post method where I am getting the error. I will paste more code, if you will need it.
Feel free to ask.
Thanks for any advice.(The documentation is great, so I didn't want to change it)
const string clientID = "Client ID";
const string clientSecret = "client Secret";
const string scope = "https://outlook.office.com/IMAP.AccessAsUser.All%20https://outlook.office.com/POP.AccessAsUser.All%20offline_access%20email%20openid";
const string authUri = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize";
const string tokenUri = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
static int GetRandomUnusedPort()
{
var listener = new TcpListener(IPAddress.Loopback, 0);
listener.Start();
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
listener.Stop();
return port;
}
async void DoOauthAndRetrieveEmail()
{
// Creates a redirect URI using an available port on the loopback address.
string redirectUri = string.Format("http://127.0.0.1:{0}/", GetRandomUnusedPort());
Console.WriteLine("redirect URI: " + redirectUri);
// Creates an HttpListener to listen for requests on that redirect URI.
var http = new HttpListener();
http.Prefixes.Add(redirectUri);
Console.WriteLine("Listening ...");
http.Start();
// Creates the OAuth 2.0 authorization request.
string authorizationRequest = string.Format("{0}?response_type=code&scope={1}&redirect_uri={2}&client_id={3}&prompt=login",
authUri,
scope,
Uri.EscapeDataString(redirectUri),
clientID
);
// Opens request in the browser.
//There is issue
System.Diagnostics.Process.Start(authorizationRequest);
// Waits for the OAuth authorization response.
var context = await http.GetContextAsync();
// Brings the Console to Focus.
BringConsoleToFront();
// Sends an HTTP response to the browser.
var response = context.Response;
string responseString = string.Format("<html><head></head><body>Please return to the app and close current window.</body></html>");
var buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
var responseOutput = response.OutputStream;
Task responseTask = responseOutput.WriteAsync(buffer, 0, buffer.Length).ContinueWith((task) =>
{
responseOutput.Close();
http.Stop();
Console.WriteLine("HTTP server stopped.");
});
// Checks for errors.
if (context.Request.QueryString.Get("error") != null)
{
Console.WriteLine(string.Format("OAuth authorization error: {0}.", context.Request.QueryString.Get("error")));
return;
}
if (context.Request.QueryString.Get("code") == null)
{
Console.WriteLine("Malformed authorization response. " + context.Request.QueryString);
return;
}
// extracts the code
var code = context.Request.QueryString.Get("code");
Console.WriteLine("Authorization code: " + code);
string responseText = await RequestAccessToken(code, redirectUri);
Console.WriteLine(responseText);
OAuthResponseParser parser = new OAuthResponseParser();
parser.Load(responseText);
var user = parser.EmailInIdToken;
var accessToken = parser.AccessToken;
Console.WriteLine("User: {0}", user);
Console.WriteLine("AccessToken: {0}", accessToken);
RetrieveMailWithXOAUTH2(user, accessToken);
}
Related
A thousand apologies if this question has been posted already, but I am getting a "The remote server returned an error: (401) Unauthorized" error when attempting to upload a string to a remote server via a web request in SSIS (Visual Studio) by means of a script task - the external company to whom I am sending the data are certain that my public IP and the user credentials I am using to authenticate all have access to the server.
My code looks like this;
public void Main()
{
string proxyServer = (string)Dts.Variables["$Project::ProxyServer"].Value;
int proxyPort = (int)Dts.Variables["$Project::ProxyPort"].Value;
string username = Convert.ToString(Dts.Variables["$Project::UserName"].Value);
string password = Convert.ToString(Dts.Variables["$Project::Password"].GetSensitiveValue());
Uri TargetSite = new Uri((string)Dts.Variables["$Project::TargetSite"].Value);
string datatosend = (string)Dts.Variables["User::RequestBody"].Value;
if (datatosend != "")
{
string result;
using (WebClient webClient = new WebClient())
{
webClient.Proxy = new WebProxy(proxyServer, proxyPort); // Connect via the proxy
var bytes = Encoding.UTF8.GetBytes(username + ":" + password);// Build a means to authenticate
var auth = "Basic " + Convert.ToBase64String(bytes);
NetworkCredential myCreds = new NetworkCredential(username, password);
webClient.Credentials = myCreds;
webClient.Headers[HttpRequestHeader.Authorization] = string.Format("Basic ", credentials);
//webClient.Headers[HttpRequestHeader.Authorization] = auth; // Add the authorization header
webClient.Headers[HttpRequestHeader.ContentType] = "text/json"; // Add information about the content
ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
try
{
result = webClient.UploadString(TargetSite, datatosend); // Issue the request
}
catch (WebException ex)
{
Dts.Events.FireError(0, "", "UnableToSendData: " + ex.Message.ToString() + ex.StackTrace, string.Empty, 0);
return;
}
}
}
Dts.TaskResult = (int)ScriptResults.Success;
}
Everything works until the request is issued - that's when I am hit with the 401 error and the response from the Uri is null;
I am at a loss because I am told my public IP is on their permissions list, so not sure why the request fails.
Any assistance would be greatly appreciated.
Many thanks in advance.
I am disconnecting with xero as per the xero documentation that is revocation of the connection and deleting the organization which are connected and both the apis are working fine. But when I disconnect and click on connect again it still remembers the previous organization connected and without showing the xero page of organization it redirects me back to my application and I am connected now.
But I want the xero to show its page where I can choose the organization I want. For now I am deleting the XeroIdentity cookie from my browser. Is it a good approach as it shows me the login page now? if not please
do suggest me a way for this.
Here is my code to disconnect
public async Task<JsonResult> RemoveConnection(string token, string Id)
{
try
{
if (Id == null)
{
return Json("Please connect with Xero first");
}
else
{
var accessToken1 = "";
var tenantId1 = "";
var token1 = await _tokenStore.GetAccessTokenAsync(Id);
accessToken1 = token1.AccessToken;
var restclient1 = new RestClient("https://identity.xero.com/connect/revocation");
var request1 = new RestRequest(Method.POST);
string encoded = base64encode(Configuration["xero:clientid"] + ":" + Configuration["xero:clientsecret"]);
request1.AddHeader("authorization", string.Format("basic {0}", encoded));
request1.AddHeader("content-type", "application/x-www-form-urlencoded");
request1.AddParameter("application/json",string.Format("token={0}", accessToken1),ParameterType.RequestBody);
var response1 = restclient1.Execute(request1);
var connections1 = await _xeroClient.GetConnectionsAsync(token1);
foreach (var connection in connections1)
{
accessToken1 = token1.AccessToken;
tenantId1 = connection.id.ToString();
var restClient = new RestClient("https://api.xero.com/connections/" + tenantId1 + "");
var request = new RestRequest(Method.DELETE);
request.AddHeader("Authorization", string.Format("Bearer {0}", accessToken1));
var response = restClient.Execute(request);
var x = response.Content;
var connections12 = await _xeroClient.GetConnectionsAsync(token1);
}
var details = new { Result = "Disconnected", Status = "Ok" };
return Json(details);
}
}
catch (Exception ex)
{
return Json(ex.Message);
}
}
I need to read the gmail inbox feed using Oauth2.0. Simulating in the postman,
Auth URL : https://accounts.google.com/o/oauth2/auth
Access Token URL : https://accounts.google.com/o/oauth2/token
Client ID : XXXXX.apps.googleusercontent.com
Client Secret : XXXXX
Scope : https://mail.google.com/mail/feed/atom
GrantType: Authorization Code
I requested the token and used it on the header
Authorization - Bearer XXXXXXXXXX.
And I made the request via GET right in my scope and got my email feeds. Works!!!
The postman generates a code in C #, but the token expires.
var client = new RestClient("https://mail.google.com/mail/feed/atom/");
var request = new RestRequest(Method.GET);
request.AddHeader("postman-token", "d48cac24-bd3e-07b5-c616-XXXXXXXX");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("authorization", "Bearer ya29.a0AfH6SMDZlUmw0xLHAoYIJuIfTkXXXXXXXXQSPP17GmXT26fJEfWB9w8UiwQ2YF32-nOp6zY9H_lwJEEXXXXXXXXXXXYK4e0tcZkieGbBl5Eow2M-7Gxp20kfDtXXXXXVjiXymLXyMkYEI");
IRestResponse response = client.Execute(request);
I'm trying to do it via Google.Api, using GoogleAuthorizationCodeFlow and already using token refresh.
With the code below, I got authorization from the application, but I can't read the xml atom feed
GoogleAuthorizationCodeFlow flow;
var assembly = Assembly.GetExecutingAssembly();
var clientfile = #"client_secrets.json";
using (var stream = new FileStream(clientfile, FileMode.Open, FileAccess.Read))
{
flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
DataStore = new FileDataStore("StoreTest"),
ClientSecretsStream = stream,
Scopes = new[] { "https://mail.google.com/mail/feed/atom/" }
});
}
var uri = Request.Url.ToString();
var code = Request["code"];
if (code != null)
{
var token = flow.ExchangeCodeForTokenAsync(UserId, code,
uri.Substring(0, uri.IndexOf("?")), CancellationToken.None).Result;
// Extract the right state.
var oauthState = AuthWebUtility.ExtracRedirectFromState(
flow.DataStore, UserId, Request["state"]).Result;
Response.Redirect(oauthState);
}
else
{
var result = new AuthorizationCodeWebApp(flow, uri, uri).AuthorizeAsync(UserId,
CancellationToken.None).Result;
if (result.RedirectUri != null)
{
// Redirect the user to the authorization server.
Response.Redirect(result.RedirectUri);
}
else
{
// The data store contains the user credential, so the user has been already authenticated.
var gmailfeed = new GmailService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "GetFeed",
});
var inboxlistRequest = gmailfeed.Users.Messages.List("me");
inboxlistRequest.LabelIds = "Label_19780355190759038";
inboxlistRequest.IncludeSpamTrash = false;
var emailListResponse = inboxlistRequest.Execute();
foreach (var mail in emailListResponse.Messages)
{
var mailId = mail.Id;
var threadId = mail.ThreadId;
Message message = gmailfeed.Users.Messages.Get("me", mailId).Execute();
Console.WriteLine((message.Snippet));
}
}
}
I got to read the email, but I need the xml atom feed.
Could someone help me how I make this call to get the atom feed, using the granted token. If there is an easier way to do it too, it would be cool to share.
Thank you
Resolved using respsharp, restclient!!
tks
I have to send push notifications to specific iOS devices with my .Net Core WebAPI that will be executed on a Windows 2008 Server R2. The server itself should not be the problem because it is working with a node.js library. But I want it to work with an WepAPI in ASP .Net Core 2.1 which is self hosted with the inbuilt Kestrel Server. Maybe you've got an idea how to solve this problem.
My Code:
// This will encode the jason web token apns needs for the authorization
// get the base64 private key of the .p8 file from apple
string p8File = System.IO.File.ReadAllText(Settings.Apn.PrivateKey);
p8File = p8File.Replace("-----BEGIN PRIVATE KEY-----", string.Empty);
p8File = p8File.Replace("-----END PRIVATE KEY-----", string.Empty);
p8File = p8File.Replace(" ", string.Empty);
byte[] keyData = Convert.FromBase64String(p8File);
ECDsa key = new ECDsaCng(CngKey.Import(keyData, CngKeyBlobFormat.Pkcs8PrivateBlob));
ECDsaSecurityKey securityKey = new ECDsaSecurityKey(key) { KeyId = Settings.Apn.KeyId };
SigningCredentials credentials = new SigningCredentials(securityKey, "ES256");
SecurityTokenDescriptor descriptor =
new SecurityTokenDescriptor
{
IssuedAt = DateTime.Now,
Issuer = Settings.Apn.TeamId,
SigningCredentials = credentials
};
JwtSecurityTokenHandler jwtHandler = new JwtSecurityTokenHandler();
string encodedToken = jwtHandler.CreateEncodedJwt(descriptor);
this.log?.LogInformation($"Created JWT: {encodedToken}");
// The hostname is: https://api.development.push.apple.com:443
HttpClient client = new HttpClient { BaseAddress = new Uri(Settings.Apn.Hostname) };
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
this.log?.LogInformation("Initialized new HttpClient.");
// payload content for the apns
JObject payloadData = new JObject
{
new JProperty("alert", data.Message),
new JProperty("badge", 2),
new JProperty("sound", "default")
};
JObject payload = new JObject
{
new JProperty("aps", payloadData)
};
this.log?.LogInformation($"Setup payload: {payload}");
// HttpRequestMessage that should be send
HttpRequestMessage request = new HttpRequestMessage(
HttpMethod.Post,
$"{Settings.Apn.Hostname}/3/device/{data.DeviceId}")
{
Content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json")
};
this.log?.LogInformation("Setup HttpRequestMessage.");
// Setup the header
request.Headers.Add("Authorization", $"Bearer {encodedToken}");
request.Headers.Add("apns-id", Guid.NewGuid().ToString());
request.Headers.Add("apns-expiration", DateTime.Now.AddDays(1).ToString(CultureInfo.InvariantCulture));
request.Headers.Add("apns-priority", "10");
request.Headers.Add("apns-topic", "de.gefasoft-engineering.FabChat");
// Debug logging
this.log.LogDebug(request.ToString());
this.log.LogDebug(await request.Content.ReadAsStringAsync());
this.log.LogDebug(request.RequestUri.Host + request.RequestUri.Port);
// Send request
var result = await client.SendAsync(request);
this.log?.LogInformation("Sent request.");
this.log?.LogInformation(await result.Content.ReadAsStringAsync());
I always get following Exception thrown:
System.Net.Http.HttpRequestException: The SSL connection could not be
established, see inner exception. --->
System.Security.Authentication.AuthenticationException: Authentication
failed, see inner exception. --->
System.ComponentModel.Win32Exception: The message received was
unexpected or badly formatted --- End of inner exception stack
trace ---
Use CorePush lib
It's very lightweight. I use it across all my projects to send Firebase Android/WebPush and Apple iOS push notifications. Useful links:
NuGet package
Documentation
The interface is very simple and minimalistic:
Send APN message:
var apn = new ApnSender(settings, httpClient);
await apn.SendAsync(notification, deviceToken);
It can also send Android FCM message if needed:
var fcm = new FcmSender(settings, httpClient);
await fcm.SendAsync(deviceToken, notification);
can you try adding version information to your request after the apns-topic line as below? It ran to completion and I got a "bad device token" error for the first time after adding the following line.
request.Version = new Version(2, 0);
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
I saw the version setting command at the post below.
How to implement apple token based push notifications (using p8 file) in C#?
I've already commented on the answer from #civilator. But I think, that some people read over it, so I'm posting it again.
This is the code that worked for me. Sorry for the late answer!
private readonly string hostname = "gateway.sandbox.push.apple.com";
private readonly int port = 2195;
public async Task<RestResult<JObject>> SendPushNotification(string deviceToken, string message)
{
this.log?.LogInformation("Trying to send push notification.");
X509Certificate2Collection certificatesCollection;
// Setup and read the certificate
// NOTE: You should get the certificate from your apple developer account.
try
{
string certificatePath = Settings.Apn.Certificate;
X509Certificate2 clientCertificate = new X509Certificate2(
File.ReadAllBytes(certificatePath),
Settings.Apn.Password);
certificatesCollection = new X509Certificate2Collection(clientCertificate);
this.log?.LogInformation("Setup certificates.");
}
catch (Exception e)
{
this.log?.LogError(e.ToString());
return new RestResult<JObject> { Result = "exception", Message = "Failed to setup certificates." };
}
// Setup a tcp connection to the apns
TcpClient client = new TcpClient(AddressFamily.InterNetwork);
this.log?.LogInformation("Created new TcpClient.");
try
{
IPHostEntry host = Dns.GetHostEntry(this.hostname);
await client.ConnectAsync(host.AddressList[0], this.port);
this.log?.LogInformation($"Opened connection to {this.hostname}:{this.port}.");
}
catch (Exception e)
{
this.log?.LogError("Failed to open tcp connection to the apns.");
this.log?.LogError(e.ToString());
}
// Validate the Certificate you get from the APN (for more information read the documentation:
// https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1).
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(this.ValidateServerCertificate),
null);
try
{
await sslStream.AuthenticateAsClientAsync(this.hostname, certificatesCollection, SslProtocols.Tls, false);
MemoryStream memoryStream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(memoryStream);
writer.Write((byte)0);
writer.Write((byte)0);
writer.Write((byte)32);
writer.Write(HexStringToByteArray(deviceToken.ToUpper()));
// Creating an payload object to send key values to the apns
JObject aps = new JObject
{
new JProperty("alert", message),
new JProperty("badge", 0),
new JProperty("sound", "default")
};
JObject payload = new JObject
{
new JProperty("aps", aps)
};
string payloadString = JsonConvert.SerializeObject(payload);
writer.Write((byte)0);
writer.Write((byte)payloadString.Length);
byte[] b1 = System.Text.Encoding.UTF8.GetBytes(payloadString);
writer.Write(b1);
writer.Flush();
byte[] array = memoryStream.ToArray();
sslStream.Write(array);
sslStream.Flush();
client.Dispose();
}
catch (AuthenticationException ex)
{
this.log?.LogError(ex.ToString());
client.Dispose();
return new RestResult<JObject> { Result = "exception", Message = "Authentication Exception." };
}
catch (Exception e)
{
this.log?.LogError(e.ToString());
client.Dispose();
return new RestResult<JObject> { Result = "exception", Message = "Exception was thrown." };
}
this.log?.LogInformation("Notification sent.");
return new RestResult<JObject> { Result = "success", Message = "Notification sent. Check your device." };
}
#region Helper methods
private static byte[] HexStringToByteArray(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
// The following method is invoked by the RemoteCertificateValidationDelegate.
private bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
{
this.log?.LogInformation("Server Certificate validated.");
return true;
}
this.log?.LogError($"Server Certificate error: {sslPolicyErrors}");
// Do not allow this client to communicate with unauthenticated servers.
return false;
}
#endregion
My bot prompts the user for an attachment in a dialog and is suppose to receive an image from the user and save the content name, url and type of that file to variables. However, the file I am receiving is not the file the user sent. Instead its retrieving a file named 'blob' (no extension) from this link:
https://webchat.botframework.com/attachments/GkLhiqJcvH019mP0iGBKvo/0000052/0/blob?t=X5ICiUrhFas.dAA.RwBrAEwAaABpAHEASgBjAHYASAAwADEAOQBtAFAAMABpAEcAQgBLAHYAbwAtADAAMAAwADAAMAA1ADIA.mcEiHxuH0gE.VMcp6Yduqgc.4xT1ZTOvCX-B7A0nLto6eZNFrFi-0xSzGk5AKmA-EPE
That file contains:
{"type":"message","from":{"id":"9n0I1ZqSrLF","name":"You"},"locale":"en-US","timestamp":"2017-02-14T21:12:32.074Z","channelData":{"clientActivityId":"1487106689866.9060198022610071.8"}}
Here is the code for the attachment prompt. This prompt is within a dialog:
private async Task getAttach(IDialogContext context, IAwaitable<IEnumerable<Attachment>> result)
{
IEnumerable<Attachment> list = await result;
Attachment attachment = list.FirstOrDefault();
string filename = attachment.Name;
string url = attachment.ContentUrl;
string contentType = attachment.ContentType;
//Set attachmentFileNames
if (attachmentFileNames == null)
{
attachmentFileNames = "";
}
attachmentFileNames += contentType;
attachmentFileNames += ",";
numberOfFiles++;
//Set attachmentFileURLs
if (attachmentFileURLs == null)
{
attachmentFileURLs = "";
}
attachmentFileURLs += url;
attachmentFileURLs += ",";
attachmentHasBeenAdded = true;
await FirstMessageReceivedAsync(context, result);
}
Here is how I am handling attachments in the message controller. This is within the Post Task:
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
if (activity.Attachments != null && activity.Attachments.Any())
{
hasaAttachment = true;
var attachment = activity.Attachments.First();
using (HttpClient httpClient = new HttpClient())
{
// Skype & MS Teams attachment URLs are secured by a JwtToken, so we need to pass the token from our bot.
if ((activity.ChannelId.Equals("skype", StringComparison.InvariantCultureIgnoreCase) || activity.ChannelId.Equals("msteams", StringComparison.InvariantCultureIgnoreCase))
&& new Uri(attachment.ContentUrl).Host.EndsWith("skype.com"))
{
var token = await new MicrosoftAppCredentials().GetTokenAsync();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
var responseMessage = await httpClient.GetAsync(attachment.ContentUrl);
var contentLenghtBytes = responseMessage.Content.Headers.ContentLength;
Activity reply = activity.CreateReply($"Attachment of {attachment.ContentType} type and size of {contentLenghtBytes} bytes received. URL: {attachment.ContentUrl}");
await connector.Conversations.ReplyToActivityAsync(reply);
}
}
I am following this example:
Receive Attachment Bot Sample
In the emulator it works correctly but the published version on azure does not. The code is very similar to the example so I don't see why the bot is not detecting the user's files.
How do I receive the correct file (the file the user replied with)?
The attachments will be in elements 1 onwards. E.g.
Attachment attachment = list.ElementAt(1);
The webchat (and maybe other platforms) has a weird quirk where it adds a blob file at the start of IEnumerable 'result' when prompting and getting attachments from the user.
private async Task getAttach(IDialogContext context, IAwaitable<IEnumerable<Attachment>> result)
{...}