I'm creating a web service to expose some data via publicly accessible APIs. At a high level, what mechanisms are people using to secure their APIs to ensure that a valid, authenticated user is making the call?
The service will be C#, the consumer could be anything (Facebook or iPhone app as well as a website) so Microsoft only solutions are out.
It's not a new problem so I assume there are some standard practices in place to deal with it but my google-fu is failing me on this one. Can the collective point me to any resources? Thanks.
You can still use Membership authentication: have a web service method Login(username, password), inside that method validate user:
[WebMethod]
public bool Login( string username, string password)
{
bool isValid = Membership.ValidateUser(username, password);
if (isValid)
{
FormsAuthentication.SetAuthCookie(username, true);
return true;
}
return false;
}
And that should do it - it will create a cookie that travels with requests and in each method you can check HttpContext.Current.User.IsAuthenticated.
void SomeWebMethodThatRequiresAuthentication(someparameter)
{
if (HttpContect.Current.User.IsAuthenticated)
{
... do whatever you need - user is logged in ...
}
else
{
.... optionally let user know he is not logged in ...
}
}
I believe it can work with different consumers that support cookies because all it needs to work is for consumer to send the auth cookie along with the request to your web server.
I see that ferequently in SaaS web services is used authentication by token key over SSL - we choose this simple method in our last project over OAuth and SAML protocols. Maybe this can be usefull - sometimes simple solutions make things more scalable and over control.
Try the answers in this similar question:
What is the best way to handle authentication in ASP.NET MVC with a Universe database?
We use the WS-Security. It's a published standard so any client (in theory) can use it to send authentication credentials.
Here's another SO question that covers using WS-Security with C#.
How to use WS-Security in C#?
Related
I want to implement a single sign on feature using my own API. Third party application(web application) will call this API and authenticate the users. For the communication between my API and other applications web request will be used. Below is the solution I provided for this,
I have created a API on my application and do the authentication based on request values. After successful authentication, I create the authentication cookie and add it to the response.
On the other app I used a HttpWebRequest and create CookieContainer. Then I get the cookies from response and assign those cookies to Response.
var response = (HttpWebResponse)http.GetResponse();
foreach (Cookie cook in response.Cookies)
{
Response.Cookies.Add(new System.Web.HttpCookie(cook.Name, cook.Value)
{
Domain = cook.Domain,
Expires = cook.Expires
});
}
In my test environment this works fine since both authentication API and other app are in same domain. But in customer testing phase this does not work due to domain mismatch. Because Authentication API is in different domain.
Is there any way to resolve this issue ?
I think it is impossible with cookies because they are domain bound and are not sent along with requests to domains. I guess you need see about other technology.
I hope this link will be useful
Good Luck
I have been trying to find an explanation, but are coming rather short, as to how exactly identity works with mvc core. I have found numerous guides on how to implement it and I have and it all works great, but I want to understand it under the hood at the very least on a high level.
My current understanding is that when a user passes their credentials, identity library will issue a token and that token will be written to the browser's cookies. Now all further requests will have the user identified based on that browser cookie. However, I can't seem to find explanations past that. Why is this safe, why can't I steal that cookie and use it as my own? I know there is more to this and potentially I have understood the above part wrong.
In any case, I'm either looking for a high-level explanation on how this works or a good reference that can delve more into details.
how exactly identity works with mvc core.
As #Chris Pratt says, what you're talking about is the security subsystem. Since you're talking about cookie, I'll take the authentication scheme of cookie as an example.
The built-in security could be mainly found in 4 projects:
HttpAbstractions: core interfaces and classes, such as authentication scheme, authentication handler, authentication ticket and so on.
Security: authentication middleware, cookie authentication, JWT Bearer authentication, OAuth2.0 authentication(Google/Facebook/Microsoft/...) and so on.
Identity : a scaffold project named as "Identity" that helps to manage user/roles/claims/etc.
DataProtection : Data Protection APIs for protecting and unprotecting data. You can treat it as an API to encrypt and decrypt.
The entry point to understand how authentication works is the AuthenticationMiddleware. This middleware will try to authenticate every request if possible:
public async Task Invoke(HttpContext context)
{
// ...
// Give any IAuthenticationRequestHandler schemes a chance to handle the request
var handlers = context.RequestServices.GetRequiredService<IAuthenticationHandlerProvider>();
foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
{
var handler = await handlers.GetHandlerAsync(context, scheme.Name) as IAuthenticationRequestHandler;
if (handler != null && await handler.HandleRequestAsync())
{
return;
}
}
// Use the default scheme to authenticate request
var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
if (defaultAuthenticate != null)
{
var result = await context.AuthenticateAsync(defaultAuthenticate.Name);
if (result?.Principal != null)
{
context.User = result.Principal;
}
}
await _next(context);
}
Usually, this middleware runs before other middlewares/mvc thus you can intercept requests as you need.
When you want to access an url protected by [Authorize] without login, it will ask you to sign in through some scheme. You can configure your services to use different schemes as you like, such as Jwt Bearer, cookies, and so on.
If you're using the cookie scheme,
CookieAuthenticationHandler will do the heavy lifting :
Signin: will issue a new cookie when you think you have validated the user principal.
Authenticate: validate the cookie sent by client
Signout : delete the cookie
Note all these are done by Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler, i.e. a handler defined in aspnet/Security, not the aspnet/Identity library.
why can't I steal that cookie and use it as my own?
Of course you can steal someone's cookie and use it as your own. Actually, if Alice's cookie is stolen by Bob (let's say through XSS or sniffering), Bob will be treated as Alice. ASP.NET Core (and other technologies such as PHP/Python/Java) cannot prevent this and there're quite a lot to do to prevent the stealing :
The website should use HTTPS rather than HTTP
encode characters like <,>,<img onclick='javascript:' and so on to prevent XSS
...
Also, you don't need to steal someone's cookie sometimes. By CSRF, you simply "borrow" his cookie.
Why is this safe
Typically, even if it's possible to steal someone's cookie or borrow someone's cookie in theory, it will only happen when you're developing your app in a wrong way or deploy them in an insecure way.
Another thing is that you could hardly fake a cookie on client side.
For a given application I have a .aspx login form backed by a C# code behind file.
In the code behind I am using the following "home grown" method:
private bool AuthenticateUser(String username, String password)
{
bool validated = false;
try
{
PrincipalContext pc = new PrincipalContext(ContextType.Domain, "domnet.domad.com", "dc=domnet,dc=domad,dc=com");
IdentityType ADElement;
UserPrincipal up;
//Try first with no #DOM.COM - this should work for SamAccountName values:
username = username.ToUpper().Replace("#DOM.COM", "");
ADElement = IdentityType.SamAccountName;
up = UserPrincipal.FindByIdentity(pc, ADElement, username);
validated = pc.ValidateCredentials(username, password, ContextOptions.Negotiate);
//If SamAccountName fails try UserPrincipalName with #DOM.COM
if (!validated)
{
username = username + "#DOM.COM";
ADElement = IdentityType.UserPrincipalName;
up = UserPrincipal.FindByIdentity(pc, ADElement, username);
validated = pc.ValidateCredentials(username, password, ContextOptions.Negotiate);
}
//Put username into session
if (validated)
{
Session["Username"] = username.Replace("#DOM.COM", "");
}
}
catch (Exception) //login failure...
{
validated = false;
}
return validated;
}
This works fine for the application but I have other applications that need authentication too.
I really don't want to copy / paste the login files into ever application.
So my most basic question is what are my options to centralize the authentication code between applications?
In the future I will also be looking to:
Verify not only username/password but also AD group membership.
Once user is authenticated no more log in screens between apps. (SSO)
It seems to me I am not the first person to run into this problem.
I would prefer to use an out of the box solution vs. developing my own if possible.
You could:
Enable Windows authentication in IIS and set your Web.Config to use Window authentication http://weblogs.asp.net/scottgu/archive/2006/07/12/Recipe_3A00_-Enabling-Windows-Authentication-within-an-Intranet-ASP.NET-Web-application.aspx
Setup an ADFS claims authentication server and have all your applications use claims based authentication http://blogs.msdn.com/b/alextch/archive/2011/06/27/building-a-test-claims-aware-asp-net-application-and-integrating-it-with-adfs-2-0-security-token-service-sts.aspx
Have one application that all others redirect to for authentication and set an encrypted cookie that the other applications can read to verify authentication Encrypt cookies in ASP.NET
One approach would be to create a Core project (.dll/library) that contains the common parts that you wish to share between your applications, and then to reference that project in your applications.
Ie: Say that you have 2 applications: A and B you would create three projects A, B and Core. In project A and B simply add a project reference to the Core library. Now you can access any method in core from both A and B.
This approach works well with SVN and similar version control systems and you will find it is a very flexible way of working. The hard part is to identify what is really common code and and to make as general as possible.
#Baxter Not sure if my answer comes a bit late as this question was posted a few days ago, but am looking into the same problem of implementing centralized session and authentication management in my MVC 3 application, and I believe the following link would be of great interest to you:
http://www.codeproject.com/Articles/246631/ASP-NET-MVC3-Form-Authentication
The author of the article corresponding to the link above, factors out the authentication functionality into a separate DLL and uses dependency injection to use application context to utilize the external 'security' DLL. I am planning to use this approach to centralize the security mechanism and reuse it in 3 different MVC 3 web applications, so it is still research in progress, and will update this answer accordingly with what i find :)
You can refactor this method out into a separate project (meaning a different dll) and reference that project from any web application where you want to use this code.
An alternative if you are using Windows Authentication is to grab their SID, query AD for a piece of information that is shared between AD and the application's user table (we use the email address) and check to see if the user table has an entry with that email address.
This way, by logging onto their workstation, they are essentially pre-logged into any application using this authentication method. You just have to make sure that when you create a new user account (at the application level) you capture the info that you want to check for authentication (this is why we use the email address - everyone knows their company email).
This works really well with the Core library method suggested by Avada Kedavra. This method also allows you to have each application maintain its own user base (although it will also work well with a central user database).
I have a custom MembershipProvider as shown below. It validate user name and password against Active Directory. I would like to make this as an “authentication service”. This should work even if the client uses forms authentication or windows authentication.
There is a WCF “HR Service” which is providing employee information. The “HR UI” website is using “HR Service” WCF service. Now we need to ensure that any client using the “HR Service” should be authenticated using “authentication service” before accessing the operation of “HR Service”. If the client application is authenticated once, next time onwards it should not be validated again (till the application is closed). When a new instance of the client application is opened it need to be authenticated from beginning.
How do we achive it? Do we have any code samples for the end to end flow demonstration?
Note: I should be able to test it using self hosted services.
Note: The client can be of any platform (e.g. Java).
namespace LijosCustomValidation
{
public sealed class LijoMembershipProvider : MembershipProvider
{
public override bool ValidateUser(string username, string password)
{
bool isValid = true;
//my logic to validate the user name and password
return isValid;
}
//other implementations of Abstract Methods from MembershipProvider
}
Your auth service should return a token if the auth is successful. This token in turn should then be presented to the HR service.
You have a couple of options as to what the HR service does at this point. It can either know the secret to validate the token, or it needs to call the auth service to validate the token.
The token should be some value that can be validated if you know the secret, so it could something, say the users id, that is symmetrically encrypted. Ideally it should have a time component in it to prevent replay attacks.
I'd suggest some something like
<hash value>|<token issue time>|<user id>
The hash value should be hash (sha1, md5, etc) of everything after the first pipe. You can then base64 encode the result and pass it around. Validating the token could then check the issue date was within a certain time-frame.
You also have the option of storing the token in the client in a cookie and passing as a cookie to the services, or making it a parameter on your services. There may be other options, depending on your client architecture & how you want to structure your services.
I'm working on a WCF Service that is called by our other softwars to send bug information to our database. The problem is that, since it is an online service, it isn't safe, so I was wondering if it's possible to the service to request a password (i.e. when we call the service, we have to configure the password or something like that).
I googled about it, but it all seemed so complex for such a simple thing ... can you guys help me out?
Edit:
The idea is to authenticate through my software, without the need of a user login.
Another option is to implement your own security. Here's a basic example.
WCF Service
In your service, change it's ServiceBehavior's InstanceContextMode to PerSession and ConcurrencyMode to Single
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
public class SomeService : ISomeService
{
// ...
}
Add a Username and Password property in your service.
public string UserName { [OperationContract] get; [OperationContract] set; }
public string Password { [OperationContract] get; [OperationContract] set; }
Add a private method for checking a security.
public void CheckSecurity()
{
if ((this.UserName == null || this.Password == null) ||
this.UserName == "username" && this.Password == "password"))
{
throw new FaultException("Unknown username or incorrect password.");
}
}
Then call the CheckSecurity method in each of your service class constructor method.
public SomeServiceMethod1()
{
this.CheckSecurity();
// some method codes
}
Client Application
In your client application code, set the service username and password for every instance, or create a static class that will do this for you.
You might also try to use encryption in the username and password to add security.
Note that this is just to add another option for you that might fit your needs but you should always try to use the Microsoft way of doing things.
You can use the ASP.NET Membership provider to authenicate clients. There is an article on MSDN describing how to achieve that.
Unfrotunately security is never simple. For your requirements, have a look at UserNamePasswordClientCredential and UserNamePasswordServiceCredential. They might be sufficient.
All of the answers below are good, but it will depend on who's consuming your WCF service.
For example, I know a WCF service that's consumed by a iPhone application via REST, and some of the schemes above would be very difficult if not impossible to implement. It used something similar to stefan's solution of a session key passed in the message header.
There are many ways to handle this scenario.
Fredrik and stefan's answers together could yield an authentication method and authorization (via token) but you would have to write the login page, db, code, checks, enforcement, token creation, token embedding and token validation logic yourself.
PaulF's suggestion is probably less work because you can use features built into WCF to carry the credentials in the message transport. Could skip the token creation, token embedding and token validation logic pieces.
If you plan on supporting many different devices I would recommend looking into OpenID, Windows Identity Foundation, Azure Authentication Service, etc.
- These are a bit more complicated to setup but provide a solid, standards based and flexible method for passing around credentials. Passive Federation is a good keyword if you want to go that route.
Unfortunately, as Paul said, security is never simple. You have to think about how you authenticate, what you need to know about the person in the service/app, how you pass those credentials other apps/services securely, how they know you are really you and vica versa... Once you get federated authentication setup you no longer have to worry about a lot of those things for this service and others going forward...
Authenticating the software which is generating requests to a service is inherently a very difficult problem, far harder than authenticating a human user. The problem arises from the need to store secret information to be used to construct the credentials sent as the basis for authentication.
When authenticating a human user you can rely on them being able to store a secret (such as a PIN or password) in a data store (the wet grey matter between their ears) which only they have access to.
When you are trying to authenticate some body of software executing in an environment over which you have no control, there is no equivalent storage place for a secret which gives you any assurance that only your software has access to it. So in general there is no way for your software to generate a request which reliably proves that it was generated by your software and not something else.
Your only options are:
Find some way to provide a secret store to the software at runtime, such as a PIN-protected smart card which has to be used in conjunction with your client application.
Rely on security by obscurity, accepting that this can only make spoofing a bit harder and more time-consuming for an attacker, but will not defeat someone determined to break your authentication scheme.
What you need is to implement custom password validation.
Here are good articles about that:
UserNamePasswordValidator and CodeProject.
We use same thing in our project, but API key is used instead of Username. Password can be anything in this case.