My teammate uses the above code in C# (.NET) to extract email details from his inbox. If you notice it does not require any credential.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Exchange.WebServices.Data;
namespace ConsoleApplication1 {
class Program
{
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.AutodiscoverUrl("FirstName.LastName#company.com", RedirectionUrlValidationCallback);
if (service != null)
{
FindItemsResults<Item> resultout = service.FindItems(WellKnownFolderName.Inbox, new ItemView(10));
foreach (Item item in resultout.Items)
{
EmailMessage message = EmailMessage.Bind(service, item.Id);
String subject = message.Subject.ToString();
Console.Write(subject);
String fromwhom = message.From.Address.ToString();
Console.Write(fromwhom);
}
}
}
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
}
}
I need to perform the same steps but in Python. i.e read email details.
My attempt
from exchangelib import Account, Configuration, Credentials, DELEGATE, IMPERSONATION, NTLM
email = 'FirstName.LastName#company.com'
creds = Credentials(email, "")
account = Account(email, autodiscover=True, credentials = creds)
Error:
AutoDiscoverFailed: All steps in the autodiscover protocol failed
With Autodiscover false
from exchangelib import Account, Configuration, Credentials, DELEGATE, IMPERSONATION, NTLM
email = 'FirstName.LastName#company.com'
creds = Credentials(email, "")
config = Configuration(server = "domain.com", credentials=creds)
account = Account(email, autodiscover=False, config = config)
Error:
Wrong username or password for https;//domain.com/EWS/Exchange.asmx
I can access the https;//domain.com/EWS/Exchange.asmx via url, without entering any credential.
Note: I am fairly good in Python with no knowledge of C#.
I believe there are different ways of approaching authentication in Python; here's one.
I've worked on a similar project, where I try to print out an email [web automation], for which I used selenium and a webdriver to access a yahoo mail. This project included page inspection -- another approach. The code isn't optimised and the final output is not what I intended yet, but you could understand the idea at least.
Good Luck!
Related
I was able to succeed via a package I found called EAGetMail. Unfortunately, I realized soon after that they have a token system and this is not a free approach.
There are a couple other choices available, like using Outlook Mail REST API, and MimeKit, but I'm lost on how to achieve my end result because no "start to finish" code is available on either of these references that demonstrates how to parse an Inbox for an account.
I've started to write this with the help of Mimekit, but am not sure if this is the proper way at all.
I must imagine it looks something like:
using (var client = new SmtpClient ())
{
client.Connect("outlook.office365.com", 587);
client.Authenticate("myemail#office365account.com", "mypassword");
var message = MimeMessage.Load(stream);
}
I don't know how to setup the stream mentioned above, and I don't know if it's possible to do this with Mimekit and Office 365.
I'm open to seeing a solution for this in any other approach that's not through EAGetMail. If anyone has a lightweight solution ranging from actual establishing a connection, to pulling messages from the inbox, would be great to see!
I've got it using EWS (Exchange Web Services). Here's my code:
private static bool RedirectionUrlValidationCallback(string redirectionUrl)
{
// The default for the validation callback is to reject the URL.
bool result = false;
Uri redirectionUri = new Uri(redirectionUrl);
// Validate the contents of the redirection URL. In this simple validation
// callback, the redirection URL is considered valid if it is using HTTPS
// to encrypt the authentication credentials.
if (redirectionUri.Scheme == "https")
{
result = true;
}
return result;
}
static void Main(string[] args)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013_SP1);
service.Credentials = new WebCredentials("email#myemail.com", "myPassword");
service.AutodiscoverUrl("email#myemail.com", RedirectionUrlValidationCallback);
//creates an object that will represent the desired mailbox
Mailbox mb = new Mailbox(#"email#myemail.com");
//creates a folder object that will point to inbox folder
FolderId fid = new FolderId(WellKnownFolderName.Inbox, mb);
//this will bind the mailbox you're looking for using your service instance
Folder inbox = Folder.Bind(service, fid);
//load items from mailbox inbox folder
if (inbox != null)
{
FindItemsResults<Item> items = inbox.FindItems(new ItemView(100));
foreach (var item in items)
{
item.Load();
Console.WriteLine("Subject: " + item.Subject);
}
}
}
I am in the situation that I need to access a ASP.NET Web Api that is using ADFS for authentication. I can hit it reliably through my browser by going through the ADFS login portal and getting the relevant FedAuth cookie. Unfortunately I need to access it from outside of a dedicated browser for use in a mobile app. The project is pretty much a slightly modified version of the standard visual studio web api template set up for Work and School Authentication (on-premises) and set up for cookie authentication.
bit of code from Startup.Auth.cs:
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseWsFederationAuthentication(
new WsFederationAuthenticationOptions
{
Wtrealm = realm,
MetadataAddress = adfsMetadata
});
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = WsFederationAuthenticationDefaults.AuthenticationType
});
}
I can't seem to figure out where to start. I've tried requesting a access token from the ADFS and can get different versions of SAML assertions using relevant login info, but it gets rejected by the web API. Have I misunderstood how it's supposed to work?
From my understanding it's supposed to go like this:
How I think it's supposed to work
App requests a authentication token from the ADFS
ADFS gives the requestee an auth token if the information provided was correct
App makes request to the web API and sending the token along inside a cookie called FedAuth(by default anyway) as a base64 encoded string
Web Api sends the token to the ADFS to find out if the token is correct.
ADFS responds with some sort of success message
Web Api responds to the app either with a rejection or a piece of data depending on how authentication went.
This is what I have right now while trying to figure out how to get a hold of the correct tokens.
using System;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Tokens;
using System.Net;
using System.Net.Http;
using System.ServiceModel;
using System.ServiceModel.Security;
using Thinktecture.IdentityModel.Extensions;
using Thinktecture.IdentityModel.WSTrust;
namespace ConsoleApplication1
{
class Program
{
private const string UserName = "USERNAME";
private const string Password = "PASSWORD";
private const string Domain = "DOMAIN";
private const string ADFSEndpoint = "ADFS ENDPOINT";
private const string ApiBaseUri = "THE API";
private const string ApiEndPoint = "AN ENDPOINT";
static void Main(string[] args)
{
SecurityToken token = RequestSecurityToken(); // Obtain security token from ADFS.
CallApi(token); // Call api.
Console.ReadKey(); // Stop console from closing
}
private static SecurityToken RequestSecurityToken()
{
var trustChannelFactory =
new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
new EndpointAddress(new Uri(ADFSEndpoint)))
{
TrustVersion = TrustVersion.WSTrust13,
Credentials = { UserName = { UserName = UserName + "#" + Domain, Password = Password } },
};
var requestSecurityToken = new RequestSecurityToken
{
RequestType = RequestTypes.Issue,
KeyType = KeyTypes.Bearer,
AppliesTo = new EndpointReference(ApiBaseUri)
};
RequestSecurityTokenResponse response;
var securityToken = trustChannelFactory.CreateChannel().Issue(requestSecurityToken, out response);
return securityToken;
}
private static async void CallApi(SecurityToken securityToken)
{
using (var handler = new HttpClientHandler { CookieContainer = new CookieContainer() })
{
using (var client = new HttpClient(handler))
{
handler.CookieContainer.MaxCookieSize = 8000; // Trying to make sure I can fit it in the cookie
var cookie = new Cookie {
Name = "FedAuth",
Value = Base64Encode(securityToken.ToTokenXmlString()),
HttpOnly = true,
Secure = true
};
handler.CookieContainer.Add(new Uri(ApiBaseUri), cookie);
var response = client.GetAsync(new Uri(ApiBaseUri + ApiEndPoint)).Result;
string result = await response.Content.ReadAsStringAsync();
Console.WriteLine(result);
}
}
}
public static string Base64Encode(string plainText)
{
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
return System.Convert.ToBase64String(plainTextBytes);
}
}
}
I can't quite remember what code I based my example of, but if anyone can point me in the right direction or tell me where I fucked up I'd appreciate it.
Edit: Sorry, forgot to add what I am getting.
The Web Api vomits out a bunch of debug information because an exception was thrown, telling me that a SecurityContextToken is expected instead of a saml:Assertion that I am apparently getting. Maybe my googlefoo is not powerful enough, but I can't seem to figure out where to start with this. Can I setup the api to accept SAML assertions or do I need to request the token in a different way?
You can't use WS-Fed to call a web API. You need OpenID Connect / OAuth as in Calling a web API in a web app using Azure AD and OpenID Connect.
It's for Azure AD but it does illustrate the flow.
What version of ADFS?
If 2.0, there is no OAuth support.
If 3.0, web API only - refer Securing a Web API with ADFS on WS2012 R2 Got Even Easier.
If 4.0, you have the full stack.
I am writing an App server application in C# that needs to access Firebase Database. It uses REST protocol. To authentication i want to use an service account.
Unfortunately there is no library written in C#, so i am trying to put the bellow http Request to work.
I follow this steps:
To get the accesstoken i follow the https://github.com/google/google-api-dotnet-client-samples. The code prints the token so should be ok to that point.
Invoke GET web request passing the token in the access_token query parameter as documented at https://firebase.google.com/docs/reference/rest/database/user-auth.
I tried all variations i could remember, in headers, with apostrophe, APN request style, but always got 401 error or 403. Error code 403 should mean that the API recognize the user but denys access to the resource, but i am not sure if this works this way in this case.
The account is defined in the API console and it has project edit and owner profile, for the Firebase app.
The rules are set like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Can't figure out were i went wrong. I don't think i need to go written an JWT token if i use google API library. Rules should not apply to this account so i guess i am not passing the token correctly. By inspecting the token retrieved i can see that it is of type Bear, so i tried to pass it on header with no success too.
Test code:
using System;
using System.Security.Cryptography.X509Certificates;
using Google.Apis.Auth.OAuth2;
using System.Threading.Tasks;
using System.Net;
using System.IO;
namespace FirebaseAppServer
{
/// </summary>
public class Program
{
public static void Main(string[] args)
{
accessFirebase();
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
public async static Task accessFirebase()
{
String serviceAccountEmail = "serviceaccount1#myapp.iam.gserviceaccount.com";
var certificate = new X509Certificate2(#"App.p12", "notasecret", X509KeyStorageFlags.Exportable); //App2 is the certificate i downloaded from API console
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { "https://www.googleapis.com/auth/firebase.database" //from https://developers.google.com/identity/protocols/googlescopes
,"https://www.googleapis.com/auth/firebase"
,"https://www.googleapis.com/auth/cloud-platform"}
}.FromCertificate(certificate));
var task = await credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None);
Console.WriteLine("AccessToken " + credential.Token.AccessToken); //accessToken has a value, so guess is all good so far.
var request = (HttpWebRequest)WebRequest.Create("https://<Myapp>.firebaseio.com/.json?access_token=" + credential.Token.AccessToken);
request.Method = "GET";
request.ContentType = "application/json";
using (var response = (HttpWebResponse)request.GetResponse()) //Throw error 403 - forbidden
{
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.WriteLine("responseString " + responseString);
}
}
I'm trying to send a Authentication header through to a WSDL service that does not have
the authentication requirement specified on the WSDL.
How can I add the Auth header to the call for the web service ?
Code I have been working with:
using System;
using System.Web.Services.Protocols;
namespace TesteSoap
{
class MainClass
{
public static void Main (string[] args)
{
WSDLService Service = new WSDLService();
/* How can I add authentication to the call of this webservice ? */
Service.get();
Console.WriteLine ("Hello World!");
}
}
}
This problem is questioned in other link but in a different way related to the WSDL including the question for user/password.
link
They aren't the same question but the problem is related.
Michaelis.MockService is the Webservice library extracted you may see an example on how to do this in: link Mono project website.
Michaelis.MockService service = new Michaelis.MockService();
// Create the network credentials and assign
// them to the service credentials
NetworkCredential netCredential = new NetworkCredential("Inigo.Montoya", "Ykmfptd");
Uri uri = new Uri(service.Url);
ICredentials credentials = netCredential.GetCredential(uri, "Basic");
service.Credentials = credentials;
// Be sure to set PreAuthenticate to true or else
// authentication will not be sent.
service.PreAuthenticate = true;
// Make the web service call.
service.Method();
i try to use : Microsoft.Exchange.WebServices.dll to use outlook. but connection return error
Error return line:service.AutodiscoverUrl("myusernamek#xxxx.com");
The Autodiscover service could not be located. my codes:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mail;
using System.Net;
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Exchange.WebServices.Autodiscover;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
namespace test
{
class Program
{
static void Main(string[] args)
{
try
{
// Connect to Exchange Web Services as user1 at contoso.com.
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1);
service.Credentials = new WebCredentials("myusernamek#xxxx.com", "mypassword", "xxxx.com");
service.TraceEnabled = true;
service.AutodiscoverUrl("myusernamek#xxxx.com");
// Create the e-mail message, set its properties, and send it to user2#contoso.com, saving a copy to the Sent Items folder.
EmailMessage message = new EmailMessage(service);
message.Subject = "Interesting";
message.Body = "The proposition has been considered.";
message.ToRecipients.Add("recipientname#xxxx.aero");
message.SendAndSaveCopy();
// Write confirmation message to console window.
Console.WriteLine("Message sent!");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.ReadLine();
}
}
}
}
I know this is an old question, but recently wrestled with this and similar looking error (including ISA server). It was fixed with:
service.EnableScpLookup = false;
This was not required when working with an explicit URL, but was when using AutoDiscover
This is a common problem , Autodiscover service error is encountered when this autodiscover service by exchange is down
Resolution is to provide actual URL for exchange location , rather than autodiscovering it.
This solved my same issue.
The code suggest that you have an Exchange 2007 server... Is it properly configured for using the Autodiscover features? Confirm that you can ping autodiscover.XXXX.com and view https://autodiscover.XXXX.com in a web browser.
Alternately, you may need to use your internal domain name for autodiscovery and login. For example, in my office the external email addresses are on a domain like CompanyX.com, but the internal Active Directory domain is like CompanyX.local, and we do not have autodiscover on the open Internet, so my EWS needs to locate Autodiscover.CompanyX.local.
this is an old post but maybe someone will need it.
do not use auto discover, it is rly slow.
how to find your exchange url:
-open your outlook application and connect to your exchange
-hold Ctrl key and right click on the outlook icon in the system tray
-select "test e-mail auto configuration"
-click the test button
-look for the following line:
oh and to use the url you trop that extra line of code:
service.Url = new Uri("your url here");
try these concept:
private static ExchangeService getService(String userEmail, String login, String password, String hostName)
{
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010);
AutodiscoverService auservice = new AutodiscoverService(hostName);
if (auservice.ServerInfo != null)
{
try
{
service.AutodiscoverUrl(userEmail, RedirectionUrlValidationCallback);
}
catch (AutodiscoverRemoteException ex)
{
Console.WriteLine("Exception thrown: " + ex.Error.Message);
}
}
else
{
service.Url = new Uri("https://" + hostName + "/EWS/Exchange.asmx");
}
service.UseDefaultCredentials = true;
if (service.ServerInfo == null)
{
service.Credentials = new WebCredentials(login, password);
}
return service;
}