I am creating a .NET class library which will allow local applications to access the accounts of users registered on my website, using an API. I would like the library to handle all authentication of users, so that any app I create an simply call the library, and be returned a token for the API. I'm not sure how to do this authentication.
There are a couple of ways I have considered doing this, however they are not ideal. The first would be to simply create a login form within the library which asks users to enter their login then calls the API. The second method would be to have a webpage where the user logs in and is then given the token which they enter into the app.
The ideal scenario for this situation is that the user does not see their token, and the actual login process is delegated to the website if possible. Both of the above ways lose out on one of those conditions.
The ideal way I would like to do this is inspired from an app I use where if the user is not logged in, they must press a 'Sign In' button, which opens a webpage where they log in. Once they have done so successfully the app automatically detects this and they are signed into the app. The downfall of this solution is that I have no idea how I might do that myself.
Essentially what I'm asking is, is the third solution viable, and how could I do it, or if not are there any better solutions I've overlooked.
FYI the website and API run ASP.NET MVC and WebAPI respectively and the library will use .NET framework.
Edit:
From the comment below it seems likely that you'll want to implement an authentication provider using something like OAuth. The .NET reference libraries can be found here and there's a similar answer already on StackOverflow that may also shed some light.
Welcome to Stack Overflow!
Personally, I would keep the Web API as the authority on authenticating a user and just consume this HTTP endpoint on all platforms (web, desktop, mobile etc) whenever you want to validate a user's credentials.
At a high level the process would be along the lines of:
Have your "clients" (desktop, mobile, web applications) submit HTTP requests to an API route (something like /authenticate) when the user first logs in.
Run your authentication logic
If successful return a token (and cache this this for use in subsequent requests)
Otherwise return a 401 response
Every client will now get a standardised response they can use for determining if they should redirect the user to some protected area, or show them an error message.
This also allows you to design login screens that are native to the platform they're running on (which is a smoother user experience). I wouldn't recommend having a library return a pre-built login page to the user - you'll find that becomes a real pain to maintain!
The third solution you proposed is also a valid way of doing things - but it does have the side effect of redirecting the user's focus away from the application they're using - which you may not want depending on your use case. It's also a bit trickier to implement than just calling the API directly, so unless you have a specific requirement to do it this way I'd not recommend it.
Hopefully this makes some sense. If you are unsure on how to implement cross application authentication then I'd recommend taking a look at some existing answers on Stack Overflow such as:
Basic token checking
OAuth
Related
Is there a way for a native/WPF application to get Identity Server 4 to issue a token without the need for a user interface in the way that Microsoft's MSAL library allows you to - https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Integrated-Windows-Authentication.
The MSAL library only works where you have a Windows user that is backed up in the Azure AD tenant specified by the authority property.
Using the .NET HttpClient class, I was hoping that if I set the UseDefaultCredentials in the HttpClientHandler and setup Identity Server so it uses Windows Authentication as its "provider" that this would be possible.
However when trying it I get a 401 Unauthorized when it tries to authenticate the user at this point in the Quick Start example. I suspect I'm getting Windows Auth problems with the HttpClient class even before we get into Identity Server specifics.
If I got past this stage I would also need to be able to handle the callback to deal with the code/access token. Is there another way to do this?
I realise that a browser window can be shown in WPF to handle everything but for complicated and political reasons within the company we would like to avoid this if possible.
So the proper way to do this is use Auth Code with PKCE, you can configure this to give you a refresh token, so you can argue that you only see the browser once when initially logging in. (I advise this)
If you can't get a refresh token, you can do silent refreshes with headless browser.
If you don't want the browser window you can again have a headless browser and just interact with it via the UI, and simulate the flow without anyone seeing it. (I do not advise this)
If you don't want the browser at all, you can use Resource Owner Password Credentials flow. Please refer to Scott Brady's blog post about weather you should or shouldn't use it.
Many members of the OAuth Working Group now consider the ROPC grant type as deprecated.
And as for
I realise that a browser window can be shown in WPF to handle everything but for complicated and political reasons within the company we would like to avoid this if possible.
Tell them it's impossible to avoid this.
https://identityserver4.readthedocs.io/en/release/intro/support.html
I currently issue tokens myself in my web api with JwtSecurityToken and I use standard ASP.NET Core middleware calling AddJwtBearer to verify the tokens. It works fine.
What advantage will give me using OpenID Connect (through IdentityServer4) over the approach described above? How to answer myself question "Do I need OpenID Connect?"
From my basic understanding about OpenID Connect, it is used to allow third parties to access your API. But I make API for myself and not for third parties and I don't know why should I favor IdentityServer/OpenIddict over my simple approach.
I read that if I want Single sign-on I should use this, but JWTs itself aren't bound to any specific domain and I can use single sign-on with just pure JWTs(they're self-contained)
I understand it implements some kind of standard for issuing tokens. (protocol). It might be good if I ever wish to expose some API to third parties. But for internal APIs? Is it worth using it?
This is my current auth flow (from https://jonhilton.net/2017/10/11/secure-your-asp.net-core-2.0-api-part-1---issuing-a-jwt/)
What I really want to implement to secure my Web API:
Login
Logout (invalidate token?)
No consent screen (want to have API only for myself), auth happens in the background in my native desktop, mobile, web app (no redirection)
Remember me feature (refresh tokens?)
Could someone clear out the fuzzy picture of OIDC/OAuth2 for me? i.e. give me some disadvantages going my own way (implementing my own flow) and advantages of using OIDC in place of my own flow.
What will it save me from doing later on (on the client-side for example), and what will not. And most particularly, is it good to start every project using standard flows like OIDC? Will it somehow benefit me in the future?
In any case you will implement OAuth2. Think of Oidc as an extension of OAuth2. The most important thing to keep in mind is seperation of concerns.
Forget Oidc, Identity Server 4 is all about authentication: "who is the user"? Consider Google login. When a user logs in for the first time, the application doesn't know the user, it only knows that Google does.
Authorization takes place on a different level and isn't really a concern of IdentityServer. For that you could take a look at PolicyServer.
So you'll need to keep the user database seperated from the application database. This doesn't mean you need another database, just don't mix contexts. If you have a relation from the "business context" to e.g. the user table in the "Identity context" then you are going to have a problem eventually.
In your setup your web api is both the resource and the identity provider. This means that every new web api you create has to be implemented as both resource and identity provider. For maintainability you could create a seperate web api that acts as an identity provider, while the web api is a resource only. You can implement something like that as long as all apps can read the token.
The same counts for the front. Why should the front have anything to do with the user? All it needs to do is pass the token in order to get the user authorized. In case of IdentityServer, the app contacts it to verify the user and receives a token. It knows nothing about credentials. This is more secure. The client app can be compromised. The credentials can be intercepted.
Having single apps with a specific concern makes things more maintainable. And it is quite easy to add a new resource without having to code when you use IdentityServer. Just add the configuration. It also allows you to add other flows in the future that are not needed at this time. And as a side note, the consent screen is optional.
The bonus is that you can implement SSO, where in your setup that could be harder, if not impossible.
So you don't have to use IdentityServer, nor Oidc. Your setup may be just fine. But if you build something, keep seperation of concerns in mind.
The question is really that how can an auth server that serves JWT be used by multiple websites of same company or domain (with the websites as sub-domains), for example? Not something for the public.
Already, I'm thinking of asymmetric JWT. Also, I don't want to implement OAuth 2.0 in order to avoid complexity and because the auth server would only serve web apps that are sub-domains of a same root domain.
seeking for less complex solutions based on the current description in this post
If you want a reliable and secure way to share resources between different sites, you might want to look at IdentityServer.
In a nutshell, you basically redirect anonymous user to identity server to login. After successful login, it will return a token to the user. Then the user uses that token to access resources from different sites.
Look at the basic workflow and screenshots at my GitHub sample project.
Ok so here is the deal.
Can multiple web apps access the same database (identity db or otherwise)? Of course! Now if you are using Entity Framework (and I assume you are though it wasn't stated) then this can get tricky as far as migrations etc. Personally I use Dapper so I never have to worry about that :-)
Yes the apps can each access the db but that is WAY different from from SingleSignOn which is really what you are talking about. You want a user to log in to one site and that identity to persist to other completely different sites. That's not nearly as simple as simply accessing a db. IdentityServer is virtually the standard for this kind of thing for many reasons.
No, the ajax approach will not work because when the user logs in at site1 the cookies are for site1. If he goes to site2 the browser does not have any cookies associated with site2 even though you sent credentials via ajax. The user was on site1 when this all happened so all cookies are site1 cookies, totally separate from site2 cookies. Even if you can find a way to make this work it would pose a serious security risk.
You could conceivably do something like this using hidden IFrames instead of ajax because you can set the iframe's site's cookies while you are there. But I don't recommend you do this as there are security risks involved.
You need to separate the idea of "Authentication" from the ideas of "Authorization" and even "User Management".
Authentication---- Am I who I say I am? (check my usename and password and maybe even additional form like text message etc)
Authorization---- Ok you know who I am, but what can I do on your site? This can vary from site to site. Maybe I am an admin on one of your sites but just a regular user on another. My individual site cookies will includes roles etc and they are different for each site.
User Management---- Can I change my name/email/etc?
The best way to handle this is to use a separate server app running IdentityServer. This handles the authentication and builds out the cookies for all of your sites at once. Ideally you should also use this for any user management but that can be a pain and isn't as vital. Here are a few sample apps for IdentityServer4.
Response to your Update 2----
Not exactly... Here is the basic flow: User goes to site1 and clicks "login". This fires a "challenge" which redirects them to website-auth. On website-auth the use submits their credentials (username/pw) via form post. This logs them in to the website-auth but then also redirects the user back to the original calling app (site1 in this case) with everything they need. Let's say the user now goes to site2, they are already logged in!!! Using IdentityServer4, the user will become logged in to all of the sites sorta automatically. You won't have to do extra stuff they way you described, just plug in the necessary stuff and let IdentityServer4 handle the rest.
Look, I understand that IdentityServer4 probably looks a bit intimidating, it did to me until I began working with it. Truth is, all of the hard stuff is handled for you. There is still a decent amount of configuration involved in getting it set up but it really is the best solution for what you are looking for.
Check out these quickstarts: https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts
Response to Update3------
I understand the concern of relying on a third party and how that can seem like a questionable practice, especially when it comes to security. My response is this:
These guys are the EXPERTS in the field. So much so that IdentityServer has become the defacto security solution even in the basic templates Macrosoft provides.
Any home grown solution you will come up with will have more security holes that what IdentityServer has. This is not a slight on you at all. These guys know what they are doing. They have been doing it for years.
Why reinvent the wheel? You will spend 10x (at least) as many man-hours trying to come up with an alternative that, in the end, will still not be as good.
If what you were doing was a single website cookie based authentication then using identity really isn't necessary. Identity can do that, but there are other simple alternatives. But when it comes to multiple sites and SSO, and I really can't emphasize this enough, IdentityServer is the way to go.
The answer is a microservices auth serve that generates RSA/Asymmetric JWTs with a private key and the other servers each have the same counterpart public key to validate the JWT and retrieve the user claims.
But that solution doesn't not cater to the situation where each of those other servers need a different set of claims about about a user.
It is also not a single sign-on approach. So, I'll be back.
But OAuth 2.0 seems to be the answer, but it is too complex for my liking.
I have created a MVC 4 Web Api using Entity Framework Code-First and a WPF-Application as a client in VS 2012.
The problem I'm struggling with at the moment is that I have to enable Authentication from the client and authorize users for access to the Api (for example: Every authenticated user can access GET methods but only admins can use POST or DELETE). I used MVC 4 Internet Application because of Forms Authentication already being included, which worked fine until I tried to login from my client application. After spending some time researching my problem about I realised that Forms Authentication is mainly supposed to work with webbrowsers. I even found some possible solutions to get it working with HttpClient using CookieContainers (e.g.: How do I set a cookie on HttpClient's HttpRequestMessage), but nothing works for me.
After spending some hours researching ways to accomplish what I'm trying to do I feel completly stuck..
I've read about SimpleMembershipprovider, BasicAuthentication, OAuth and someone mentioned Thinktecture.Identitymodel, but wasn't really able to decide which would work best.
If there is really no way to use Forms Authentication when connecting with a client other than a webbrowser then what is the best Authentication/Authorization method to take?
I would be very happy if anyone could provide me with a hint on what works best in this case, because after researching for hours I only get more and more confused.
Thanks in advance for any help!
You should be able to do this easily enough, but you haven't said what your problem actually is. Are you
unable to get access to your web api actions because you aren't logged in; or
unable to make it enforce authorisation (i.e. you can get anonymous access to actions)
For the second scenario:
There is a very good overview of using Authentication and Authorization in ASP.NET Web API on the server side, and the various ways you can enforce different roles on Actions.
There is also another approach that is appropriate for machine-to-machine (i.e. where you don't have a user who will type their login details into an appliation dialog box) in Making your ASP.NET Web API’s secure, but it doesn't focus on using SimpleMembershipProvider. Instead of using the framework auth&auth components it uses tokens (take care with this approach - rolling your own security is very very hard to get right).
For the first scenario:
From the client side, it sounds like you have some a C# application that user's interact with. If this is the case (rather than the machine-to-machine scenario) then your Forms-based approach is still suitable, and you are doing the right thing with your cookies. The approach would be:
Ask the client to type their username and password in to your application
Send a request to your LogIn action on your AccountsController, this will return your authentication cookie, session cookie etc.
Store the cookies that are returned from this (successful) login (or notify the client if the response was "login failed"
Then use those cookies in the request to the web api
As you are already talking about using HttpClient, I'm guessing you know what you are doing for this, so haven't provided code samples. I wouldn't use HttpClient, for what it's worth, but HttpWebRequest which allows you to keep a common CookieContainer through the HttpWebRequest.CookieContainer property.
I'm currently toying around with the Clarity .NET Facebook API but am finding certain situations with authentication to be kind of limiting. I keep going through the tutorials but always end up hitting a brick wall with what I want to do. Perhaps I just cannot do it?
I want to make a Web Service that takes in the require credentials (APIKey, SecretKey, UsersId (or Session Key?) and whatever else I would need), and then do various tasks: Post to users wall, add events etc.
The problem I am having is this: The current documentation, examples and support provide a way to do this within the context of a Web site. Within this context, the required "connect" popup can be initiated and allow the user to authenticate and and connect the application. From that point on the Web can go on with its business to do what it needs to do.
If I close the browser and come back to the page, I have to push the connect button again. Except this time, since I was already logged into facebook, I don't have to go through the whole connection process.
But still ... How do applications like Tweetdeck get around this? They seemingly have you connect once, when you install their application, and you don't have to do it again. I would assume that this same idea would have to applied towards making a web service because: You don't know what context the user is in when making the Web service call. The web service methods being called could be coming from a Windows Form app, or code behind in a workflow.
I would advise you to try Steve's blog and starter kit
you can also find a reference to his post in this so question
Good luck
Edited/Added below
You cannot store the facebook login credentials for users and pass them to FB.
(terms & conditions)
but you can use: offline_access, to access some usage without flagging the user as logged in.
Cool tip from this forum:
Use the following URL with your API Key
http://www.facebook.com/login.php?api_key=YOURAPIKEY&connect_display=popup&v=1.0&next=http://www.facebook.com/connect/login_success.html&cancel_url=http://www.facebook.com/connect/login_failure.html&fbconnect=true&return_session=true&req_perms=offline_access
Register for the Facebook Developer
App on your facebook profile.
Create a new app Web App(By default)
through your "Facebook Developer
App". Change the Application Type to
"Desktop App". Note down the API Key
& Secret Key for you app.
Authorize the app & authenticate the user and
then get the permanent session_key.
Authentication
For a fully logged in session, you are asked to use (again terms and conditions) login.php and you can login via JavaScript
HTH
Ric