How to maintain connectivity to Office365 with Mailkit without reauthenticating user each email sent - asp.net-core

Some background information which could be helpful to other potential solutions. My organization uses my windows login to also access other software applications such as office365 (outlook online and outlook desktop app). This password is required to be updated every so often and all other applications update together all at once so they are all interconnected. I am assuming there is some identity server or something which is tying it all together for authentication. So I am building a ASP.NET Core application which a user logs in via different username and password that is stored in a SQL Server. My web application will soon require email functionality so the users can send emails after doing some function in the application. Therefore I have put together the following code from examples online which does in fact work and let me send emails through our office365 email accounts using our organization official username(assigned email address for the mailkit below) and passwords.
public void SendEmail()
{
// create email message
var email = new MimeMessage();
email.From.Add(MailboxAddress.Parse("myEmail#something.org"));
email.To.Add(MailboxAddress.Parse("recipient#something.org"));
email.Subject = "my Subject";
email.Body = new TextPart(TextFormat.Plain) { Text = "Email sending has been integrated!" };
//send email
using var smtp = new SmtpClient();
//office 365
//Office 365 does not support NTML (windows authentication) so we need some sort of interation with an API to not store usernames and passwords.
smtp.Connect("smtp.office365.com", 587, SecureSocketOptions.StartTls);
smtp.Authenticate("myEmail#something.org", "passwordDuh");
smtp.Send(email);
smtp.Disconnect(true);
}
Since my web application does not connect to this identity server for authentication (really do not have much information on this internal service for my organization and my web application is more of a side project for my team) I have to manually insert the username and password into the code above. For testing purposes I have just added them in but I need to automate this or improve the code to be up to best practices. Therefore, I have thought of the following solutions but I am not sure of the feasibility of some of them or if there is a better/more appropriate solution:
Forgo my current username and password setup in the SQL DB and authenticate users at login with this identity server indirectly by using the current line below for mailkit and use in my login class to check the credentials against office365 (which probably uses our organizations identity server since the passwords are all linked with it):
smtp.Authenticate("myEmail#something.org", "passwordDuh")
And if it succeeds to authenticate then I know the office365 credentials are correct and I will create my authentication token as usual that I send for the users frontend to log into the site and stay logged in. My issue with this is I am not sure how I could stay connected to office365 so that I could send emails later on in the application use. It is unclear if this connection and authentication technique expires after some time if the user hasnt sent an email for an hour or so.
Use a similar approach to 1 but if I cannot keep the connection open I can take the user password if authenticated with office365 and encrypt the password with a salt then store in session a session variable. From everything I have read on stack answers seem to advice against storing passwords in plaintext in session and say to encrypt it but the responses tend to also give the sense this is still not adviced to keep even an encrypted password in session.
Figure out some way to get a token from office365 when a user logs into my site and authenticates with office365. Reading some solutions regarding getting an access token from office365 seems to indicate you need to have the application registered in azure and approved by an admin. This seems more like an api token to access their api for my application itself and not using a users username and password. I am less familiar with this solution so forgive me for my ignorance but I have tried researching this quite a bit and seems to not lead me to anything I can use unless I am missing something.

My organization uses my windows login to also access other software applications such as office365 (outlook online and outlook desktop app).
Oof. That's not a good practice.
Forgo my current username and password setup in the SQL DB and authenticate users at login with this identity server indirectly by using the current line below for mailkit and use in my login class to check the credentials against office365 (which probably uses our organizations identity server since the passwords are all linked with it):
I'm not sure I understand how this would work. Are you saying that when a user connects to your web service, they would have to provide their login credentials that you would then send to office365 to test to see if authentication is successful?
>_<
That seems like a hack that I doubt security experts would approve of since it requires passing along naked passwords.
A better approach (although I am no security expert, so take this with a grain of salt) might be to use an OAuth2 code flow to authenticate with office365 which would give you an access token that could then be used later for sending mails via MailKit's SmtpClient.
My issue with this is I am not sure how I could stay connected to office365 so that I could send emails later on in the application use. It is unclear if this connection and authentication technique expires after some time if the user hasnt sent an email for an hour or so.
You'd have to spawn a thread or something that would periodically call SmtpClient's NoOp() method (or NoOpAsync()). to keep the connection alive, although even this could eventually fail to keep that connection open eventually, so you'd have to be able to deal with a situation where the connection gets dropped.
Use a similar approach to 1 but if I cannot keep the connection open I can take the user password if authenticated with office365 and encrypt the password with a salt then store in session a session variable. From everything I have read on stack answers seem to advice against storing passwords in plaintext in session and say to encrypt it but the responses tend to also give the sense this is still not adviced to keep even an encrypted password in session.
Yea, not a great approach. You'd obviously still have to have a way to decrypt the password again and therein lies the weakness.
Figure out some way to get a token from office365 when a user logs into my site and authenticates with office365. Reading some solutions regarding getting an access token from office365 seems to indicate you need to have the application registered in azure and approved by an admin. This seems more like an api token to access their api for my application itself and not using a users username and password. I am less familiar with this solution so forgive me for my ignorance but I have tried researching this quite a bit and seems to not lead me to anything I can use unless I am missing something.
They are talking about an OAuth2 access token and this is most likely the correct way to go.
I wrote up some documentation on how to do this with a native desktop app, but I never got around to documenting this for an ASP.NET (or ASP.NET Core) web app. You can find the docs for the desktop app here: https://github.com/jstedfast/MailKit/blob/master/ExchangeOAuth2.md
Someone recently submitted ASP.NET web app docs for similar GMail authentication which can be found here: https://github.com/jstedfast/MailKit/blob/master/GMailOAuth2.md#authenticating-an-aspnet-web-app-with-the-oauth2-client-id-and-secret
You'll likely need to read the docs and find some samples for ASP.NET Core usage, but it's possible to do.

Related

What is the difference between the two use cases of using OpenID Connect in Keycloak? (Client vs Application)

I am very new to the concepts of SSO and Keycloak. I am trying to read the official documentation of Keycloak. In the "Supported Protocols" part (https://www.keycloak.org/docs/latest/securing_apps/index.html), the documentation talks about the two use cases of using OIDC like this:
"The first is an application that asks the Keycloak server to authenticate a user for them. After a successful login, the application will receive an identity token and an access token. The identity token contains information about the user such as username, email, and other profile information. The access token is digitally signed by the realm and contains access information (like user role mappings) that the application can use to determine what resources the user is allowed to access on the application.
The second type of use cases is that of a client that wants to gain access to remote services. In this case, the client asks Keycloak to obtain an access token it can use to invoke on other remote services on behalf of the user. Keycloak authenticates the user then asks the user for consent to grant access to the client requesting it. The client then receives the access token. This access token is digitally signed by the realm. The client can make REST invocations on remote services using this access token. The REST service extracts the access token, verifies the signature of the token, then decides based on access information within the token whether or not to process the request."
What I do not understand is this: In the first paragraph it talks about an application making a request and in the second one it talks about a client. But aren't applications counted as clients? Why the specific differentiation? And can anyone given an example of the remote services that is talked about in the second part?
Thank you.
But aren't applications counted as clients? Why the specific differentiation? And can anyone given an example of the remote services that is talked about in the second part?
Yes exactly it. The reason for the differentiation is because there could be many applications more than just this one client. And the client, that the user is authed against may want to access all those other applications' data.
For example take the google ecosystem. Does google email have access to drive, and photos, etc... While it could out the box, it doesn't. You need to explicitly allow email "offline access" to those other applications, even though they are all part of the same platform.
Keycloak understands this and provides that terminology. But it is a bit confusing because this isn't the best way to think about it. Instead a better explanation is that there is just the user and service clients. The service clients all talk to each other and ask for a user's data. While a user may want their data by going straight to one application, other applications may want that user's data too.
Assuming you want to actually allow one service to ask for user data from another service, you want to be using something that supports authorization as a service and not just authentication. There are some examples of this, such as PolicyServer and Authress.

Which form of Authentication should I use?

I'm writing a Web API for my users to access their information on my site. However, I'm not sure what type of authentication I should use for such a task.
Here are my intentions:
One of my users develops an app that needs information on their account from my server. He sends me a key and I verify that he's a valid user.
There shouldn't be any 'middle-man' apps that are doing anything on the user's behalf. Just the owner of the account. Currently, I'm using a system where I hash a GUID and the user sends that string to me. I decrypt it, and check my database for that key.
I'm not sure what this method is called, but it seems to work. Does anyone have a better solution that I could use that (probably) better uses web standards?
This question screams: OAuth.
http://oauth.net/2/
More details:
OAuth is an authentication protocol that allows you to approve one
application interacting with another on your behalf without giving
away your password.
Taken from: http://blog.varonis.com/introduction-to-oauth/

Google Drive API username + password authentication

I'm developing an application where Google Drive will be used to manage some documents. The idea is to create a document with some initial template data and provide the users access by adding them as collaborators of the document.
I'm familiar with the OAuth authentication process, I used it in another part of the system to manage the users Calendar...
But in this case these documents will be stored in a generic account of the company, so I can't have the approval prompt for authentication, since users won't have the password of the account.
I'd like to directly authenticate in this account, could be with the username and password hardcoded in the Java code.
Problem that this method of authentication was depreacated and I didn't found a relpacement.
Any ideas?
Thanks in advance,
Phillip
There are 2 ways that comes to mind:
Service accounts: best suited for server side OAuth with traditional backend
Regular Account owned by the application : similar to the process already in place for client side Oauth that you are already familiar with; Auth, store the refresh, ask new token if the AuthCode is expired, and so on.
I personally use and prefer the second solution more as I feel is more flexible to adapt in the future for Oauth Client Side get the tokens and use them server side.

WCF Authentication -- Authenticate user/pass one time, then authenticate some other way afterwards?

Basically, I have the following scenario and information:
We're using HTTPS.
We want to authenticate a user by user/pass when they first log in.
After they are authenticated, I want any future calls to OTHER services (not the login service) to use the username and some sort of session (in case the password changes in the middle of a session).
I want to make sure my sessions can timeout and control them in a way that if a user tries to call a service and they don't have a session they get an error (cause they haven't logged in). Not sure if there's a WCF built-in way to do sessions this way or if I'll have to do something customized with a database.
I think we want to use WSHttpBinding (not BasicHttpBinding), 90% sure on this.
I just can't seem to figure out how to do this. Often time's I'll find information on the client code doing client.ClientCredentials.UserName.UserName = username and client.ClientCredentials.UserName.Password = password. But, that just doesn't work because what is my server checking against? I'm trying to grab that info and validate it against a database of user/passes. I'm not looking to use Windows Authentication or that sort (because I don't care who is logged into the computer, just who is logging into the app).
You want to use a Secure Token Service (STS) to authenticate and get a Security Token (maybe SAML) back that identifies the user which can then be passed to your other services and they can just use the identity information to identify and authorize because they trust the STS has verified the user's identity up front.
This is a large subject to discuss, so I suggest searching for WCF STS and doing some more research, but that's definitely the direction I'd recommend going. If you're going to build your own STS implementation, I also recommend looking into using the Windows Identity Foundation (WIF) components to ease your development efforts.
Here's the download link for WIF v1.0 which is the latest version at the time of this answer.

Best way for a remote web app to authenticate users in my current web app?

So a bit of background, I'm working on an existing web application which has a set of users, who are able to log in via a traditional login screen with a user name and password, etc.
Recently we've managed to score a client (who have their own Intranet site), who are wanting to be able to have their users log into their Intranet site, and then have their users click a link on their Intranet which redirects to our application and logs them into it automatically.
I've had two suggestions on how to implement this so far:
Create a URL which takes 2 parameters (which are "username" and "password") and have the Intranet site pass those parameters to us (our connection is via SSL/TLS so it's all encrypted). This would work fine, but it seems a little "hacky", and also means that the logins and passwords have to be the same on both systems (and having to write some kind of web service which can update the passwords for users - which also seems a bit insecure)
Provide a token to the Intranet, so when the client clicks on a link on the Intranet, it sends the token to us, along with the user name (and no password) which means they're authenticated. Again, this sounds a bit hacky as isn't that essentially the same as providing everyone with the same password to log in?
So to summarise, I'm after the following things:
A way for the users who are already authenticated on the Intranet to log into our system without too much messing around, and without using an external system to authenticate, i.e. LDAP / Kerberos
Something which isn't too specific to this client, and can easily be implemented by other Intranets to log in
Both of your suggested options are insecure, even if you use SSL. Never pass credentials on a URL, put them in the HTTP request by using POST.
There is a standard called SAML and this can be used to solve your problem. The challenge is choosing which version to implement. I would choose SAML 2.0.
Google Apps implements a flavor of SAML 2.0 and allow you to authenticate using your intranet credentials. In the case of your application, you would be the service provider and your client would be the identity provider. As long as you implement the standard correctly you should be able to support any new client (identity provider). Here is a list of SAML implementations you might want to take a look at. If you need the client to pass over information in addition to the authentication information then SAML can facilitate this with metadata.
You will still need to implement SSL to encrypt network traffic.
I hate to answer my own question, but I hate even more a question with no answer. In the end we went with a very similar implementation of SalesForce's delegated authentication SSO implementation.
http://wiki.developerforce.com/page/How_to_Implement_Single_Sign-On_with_Force.com
Essentially the solution has a trusted site, known as the delegated authentication authority, who has a list of users who are logged into the company intranet.
When the user logs into the company intranet, and they click a link to our application, the company intranet will pass the user name and a generated token (which expires after a set amount of time) to our application.
Our application will then check if the user name is on our site, and if so, send the username / token (along with the source IP and a few other parameters) to the delegated authentication authority. If all those items match on the delegated authentication authority, it returns true and the user can log in. If it returns false the user is denied access.
We've found this system to work quite well, and even implemented a couple of extra security features like SSL, client side certificates, VPN tunnel, and even restricting the IP addresses which can access the site and the delegated authentication authority.
I know it's bad form to answer your own question but I hope this helps someone else who might be having the same problem ...