exchange web services x509 Certivicate - x509

I am trying to connect to Exchange Web Services to send an email on behalf of a user through my own Web Service (ASP/WCF). My code works fine when running on a desktop PC able to connect to the exchange server but when operating over the internet the exchange server can not be accessed, thus I am trying to connect through my web server instead.
I am looking for ways to login as another user without using Exchange Web Services impersonation (as I have been told to not use that unless there is absolutely no other way) and without the user providing their password.
I have the following code:
Dim service As New Microsoft.Exchange.WebServices.Data.ExchangeService(Microsoft.Exchange.WebServices.Data.ExchangeVersion.Exchange2007_SP1)
Dim emailAddress As String = "example#example.com"
System.Net.ServicePointManager.ServerCertificateValidationCallback = AddressOf CertificateValidationCallBack
Dim cert As New System.Security.Cryptography.X509Certificates.X509Certificate2(HttpContext.Current.Request.ClientCertificate.Certificate)
service.Credentials = New Microsoft.Exchange.WebServices.Data.WebCredentials(New System.Security.Cryptography.X509Certificates.X509Certificate())
service.UseDefaultCredentials = False
But this does not work because of an Unable to cast object of type 'System.Security.Cryptography.X509Certificates.X509Certificate' to type 'System.Net.ICredentials'. exception.
Can anyone direct me on how I might use an X509 certificate to authenticate against Exchange Web Services rather than using network credentials or username/password

I have been using this without error
ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

Related

OUTLOOK SMTP - Mail not sending on production using rails [duplicate]

I'm trying to send an email via smtp.live.com using an OAuth token generated via https://login.microsoftonline.com/common/oauth2/v2.0/… but I'm receiving the following error:
535 5.7.3 Authentication unsuccessful
I can make it work by obtaining an auth token via https://login.live.com/oauth20_authorize.srf (a legacy API) but that only works for free Microsoft accounts (I need to support business/Outlook365 accounts too).
The fact I can make it work using that legacy endpoint seems to validate that:
The server supports XOAUTH2 (which I also validated via Telnet EHLO)
That the encoding is correct (I'm using this process)
That I'm meant to pass the full access token via XOAUTH2 and not something odd like pass it in via LOGIN, or decode the JWT and pass in some component etc.
The access token seems valid (it works for other calls, including sending an email via the Graph API).
I've requested the following scopes:
[
'offline_access', 'User.Read', 'MailboxSettings.ReadWrite',
'Mail.Send', 'Mail.ReadWrite', 'Mail.Read', 'Mail.ReadBasic', 'Contacts.ReadWrite',
'Mail.Read.Shared', 'Mail.ReadWrite.Shared', 'Mail.Send.Shared', 'MailboxSettings.Read',
'profile', 'openid', 'email', 'Calendars.ReadWrite',
'EAS.AccessAsUser.All', 'EWS.AccessAsUser.All',
];
For the legacy token (the one which works), the scopes are different:
[
'wl.emails', 'wl.offline_access', 'wl.imap'
];
(I've tried including these scopes when requesting the v2 token too, but it's invalid).
Other things I've tried:
Using smtp.office365.com instead of smtp.live.com
Obtaining fresh access tokens
Ensuring app settings/permissions are configured correctly, as per this article
Enable Exchange SMTP Auth
Office 365 SMTP servers do not support OAuth authentication as of this time.
Office 365 SMTP servers is supporting OAuth authentication only through Code Authorization Flow not with the other mechanisms like Client Credentials flow e.t.c

AuthenticationContext.AcquireTokenAsync without creds: only works in Console App

I have a test console app that successfully retrieves a cert from the local computer Cert store and use this cert to get a token from AAD.
However, when I run this inside of a Windows service, AcquireTokenAsync() does not run and breaks the execution, although the cert is retrieved from the store.
I did notice a private key error : PrivateKey = '_certCred.Certificate.PrivateKey' threw an exception of type 'System.Security.Cryptography.CryptographicException'
Any advice would be helpful
It would be good if we have a source code and information about the accounts you are using so we can see where the certificate is stored, but based on your description:
It is possible that the user on which the service account is running does not have access to the certificate you are trying to access.
One possibility is to configure the service to run as System Account and then select the 'Allow the service to interact with Desktop'.

Identity Server Windows and Username Password

I have identity server 3 setup and working with Windows Authentication, I have the Identity Server instance and the Windows Auth in 2 separate projects, I store custom claims in the database so I add these claims to the token in order to not have to make Web Api use windows and have to check user claims on every request since I have a separate Javascript client that uses the service. Great all works.
How do I add other authencation options to the identity server instance other type of clients? For Windows users in the domain I want to use Windows Auth and for users outside the domain I want to be able to show the login with username password but I don't want the windows users inside the domain to see the Identity Server page with the windows button.
I'm a little confused on how to setup this line:
factory.UserService = new Registration<IUserService>(typeof(ExternalRegistrationUserService));
var options = new IdentityServerOptions
{
SigningCertificate = Certificate.Load(),
Factory = factory,
AuthenticationOptions = new AuthenticationOptions
{
EnableLocalLogin = false,
IdentityProviders = ConfigureIdentityProviders,
}
};
Do I need to seutp multiple Identity Servers or can one Instance Support Muliple AuthenticationOptions?
You don't need multiple identity servers for that. Are you using identityserver 3 or 4? IdentityServer4 has a page in the documentation explaining how to do it with WebListener or Kestrel: http://docs.identityserver.io/en/release/topics/windows.html

SSO from ADAL in WPF Client to ADFS 3.0 on Windows Server 2012 R2

I am trying to write an WPF client which uses ADAL to authenticate against ADFS on a Windows Server 2012 R2. I have successfully implemented this using "Forms Authentication" where the user is prompted for the domain username and password. However, I want to take advantage of SSO and use the currently logged on domain user to authenticate against the ADFS.
Unfortunately, I'm only getting an error message saying:
This method overload is not supported by '< ADFS servername>'
I have done a lot of searching, but find some of the information contradictive:
This SO post about a Windows Store App (any differences to WPF?) where Vittorio Bertocci links to a blog on how to achieve it:
ADAL for Windows Store SSO
This SO post where Vittorio Bertocci claims that silent authentication is only possible with Windows Server 2016:
Authenticate with ADFS inside Console App silently
Is it a fact that Windows Server 2016 is required to perform SSO in conjunction with ADAL?
Is there any other way to do it?
EDIT:
After upgrading to the latest alpha of ADAL (3.9.302111717-alpha) I'm getting the more detailed error message
MSIS9611: The authorization server does not support the requested 'grant_type'. The authorization server only supports 'authorization_code' or 'refresh_token' as the grant type.
The code I'm executing is this:
string authority = "https://myServer.com/adfs";
string resourceURI = "http://myApp/";
string clientId = "XXXX-XXX-XXXX-XXXX-XXXXX";
string clientReturnUri = "http://anarbitraryreturnuri/";
var ac = new AuthenticationContext(authority, false);
var token = await ac.AcquireTokenAsync(resourceURI, clientId,new UserCredential());
I was able to implement a working solution using WS-Trust, so I am confident that the machine I'm running on has the privacy settings to enable the app to find the currently logged on user.
The Oauth2 endpoint looks as follows:
Also, my global authentication policy is set up like this:
Windows Server 2016 is only required for the password grant - in which you provide raw username and password. Your question seems to suggest you want to sign on with the currently signed in user, which would leverage Kerberos instead.
Kerberos based authentication should work with ADFS "3" and ADFS 2016 indifferently - as long as your client is connected to the domain network, the local machine does not have privacy settings that prevent your app from finding out the domain user currently logged in and the correct endpoints are enabled on the ADFS instance.

Powershell Remoting: Transport Exception

Here's the code I'm using:
const string username = "domain\\user";
const string password = "password";
var credentials = new PSCredential(username, password.ToSecureString());
var connInfo = new WSManConnectionInfo(new Uri("https://server.domain.com/powershell"), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credentials)
{AuthenticationMechanism = AuthenticationMechanism.Negotiate};
var rS = RunspaceFactory.CreateRunspace(connInfo);
rS.Open();
Here's the Exception:
Connecting to remote server server.domain.com failed with the following error message: The WinRM client cannot process the request. The authentication mechanism requested by the client is not supported by the server or unencrypted traffic is disabled in the service configuration. Verify the unencrypted traffic setting in the service configuration or specify one of the authentication mechanisms supported by the server. To use Kerberos, specify the computer name as the remote destination. Also verify that the client computer and the destination computer are joined to a domain. To use Basic, specify the computer name as the remote destination, specify Basic authentication and provide user name and password. Possible authentication mechanisms reported by server: For more information, see the about_Remote_Troubleshooting Help topic.
So here's my confusion.
I checked the WSMan:\localhost\Client on the client computer, and made sure AllowUnencrypted was $true.
I checked WSMan:\localhost\Service on the server and made sure AllowUnencrypted was $true.
WSMan:\localhost\Service\Auth has Negotiate as well as Kerberos set to $true as well.
What else can I check to get rid of the exception?
I have no way to test this, but after looking at another example at an MSDN Blog, it seems that you need to update your connInfo line to add the servername, no matter which credentials you choose to use.
var connInfo = new WSManConnectionInfo(new Uri("https://server.domain.com/powershell", ADD_SERVER_NAME_HERE), "http://schemas.microsoft.com/powershell/Microsoft.Exchange", credentials) { AuthenticationMechanism = AuthenticationMechanism.Negotiate };