OData Authentication - wcf

I have been trying to implement an OData service based upon an entity framework model where the authentication is provided by Sql Azure. I provide row/column access within the database.
I want to be able to call this from LinqPad, Excel, etc. as a secure service.
I have tried the various schemes defined in the standard series but even though returning 401, neither Excel or LinqPad recall with the user name and password I've entered.
So, I thought I'd make the user name/password a query parameter (over SSL). But it turns out that's illegal as well (OData requires a well formed URL with no query parameters).
So then I thought, why not use WebGet to embed the user name and password in the URL but I can't get that to work with the OData source format in WCF:
<%# ServiceHost Language="C#" Factory="System.Data.Services.DataServiceHostFactory, System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" Service="WebApplication5.OData" %>
public class OData : DataService< MyEntities >
{
public static void InitializeService(DataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
}
protected override MyEntities CreateDataSource()
{
// create the OData source with the user name and password here
}
}
Has anyone actually got OData to work where the user name and password are passed into the source?

I've not used a username and password per-say but I have authenticated by an API key which is technically the same. See my answer here: WPF and WCF Data Services Authenticate at Query level? - I use HTTP headers to authenticate: Args.OperationContext.RequestHeaders["APIKey"]) but you can change this to Args.OperationContext.QueryString["APIKey"]) (not sure if QueryString is a property of the top of my head) to allow passing in ?APIKey=blah in the URL.

I had a similar problem with OData for a Silverlight App with C# in that you had to create a new Uri (Url) and add in the credentials, either here or you can create a login screen to populate them:
ServiceReference1.NAV nav = new ServiceReference1.NAV(new
Uri("http:...../OData/Company('company_name')/"));
nav.Credentials = new System.Net.NetworkCredential("user", "password",
"domain");

Related

Custom Auth request in ServiceStack for multi-tenancy

I am already using a custom authentication provider in my ServiceStack based web services application.
I'm overriding the Authenticate method, and validating my user against one of multiple backend tenant databases. I currently determine the tenant database by matching an API key to a database string.
public override object Authenticate(
IServiceBase authService,
IAuthSession session,
Auth request) // <- custom object here, MyCustomAuth request
{
// ...
}
This works when each application is for a single tenant (a tenant/customer can build their own application and use that API key). Moving forward I want to build a multi-tenant mobile application. Thus the API key method cannot be used because I can't expect each user to type it in, hence I can't determine which tenant is using the application.
I wanted to alter the Auth object so that I could include the TenantId (provided by the user on login). However, I can't see how I can customize that object.
Is there anyway to customize that Auth object, or do I have to find an alternative solution?
You can't modify the built-in Authenticate Request DTO used, but you can use its Dictionary<string, string> Meta property to send additional metadata with the Authenticate request, e.g:
client.Post(new Authenticate {
...
Meta = new Dictionary<string,string> {
{"TenantId", tenantId},
}
}
Alternatively you can send additional info in the QueryString or HTTP Headers and access the IRequest with:
var tenantId = authService.Request.QueryString["TenantId"];

I cannot figure out how to use the SimpleMembershipProvider.GetUserNameByEmail Method

This is a MVC 4 Internet application. I have set the Role Provider and Role Manager to SimpleRoleProvider and SimpleMembershipProvider in the Web.config file, but I continue to get the "You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class" exception despite initializing WebSecurity.InitializeDatabaseConnection in the Global,asax.cs file. I know this was initialized properly because the Roles property in the Authorize Attribute I have created and assign programmatically work perfect. All I want to do is retrieve a Users UserName and email it to them if they forget and cannot login. Advice appreciated.
[HttpPost]
[CaptchaVerify("Captcha is not valid")]
[AllowAnonymous]
public ActionResult ForgotUserNameOrPassword(UserProfile model, FormCollection collection)
{
if (!ModelState.IsValid)
{
ViewBag.Error = "The Captcha answer is incorrect";
return View();
}
else
{
SimpleMembershipProvider mySMP = new SimpleMembershipProvider();
int outRecs;
dynamic email = new Postal.Email("UserNameEmail");
MembershipUserCollection myUserCol =mySMP.FindUsersByEmail(model.UserEmail, 0, 0, out outRecs);
email.Username = myUserCol;
email.To = model.UserEmail;
email.From = model.UserEmail;
email.Send();
return View("../Account/Login");
}
}
Even if you did call InitializeDatabaseConnection properly it would not work for SimpleMembershipProvider.FindUsersByEmail. Here is a note in the documentation for this method.
If the SimpleMembershipProvider class has been initialized using a call to the WebSecurity.InitializeDatabaseConnection() method, this method is not supported and will throw a NotSupportedException exception. However, if the WebSecurity.InitializeDatabaseConnection() method has not been called, and if you have configured your site to use the standard ASP.NET membership provider, this method is passed through to the standard membership provider. For more information, see the SimpleMembershipProvider class overview.
What you are experiencing does not make any sense given the documentation. Where exactly is the exception being thrown? When you call FindUsersByEmail?
Updated 6/28/13
SimpleMembershipProvider does not implement all of the standard provider methods. If a method is missing you have a couple of options. First you can create your own custom SimpleMembershipProvider that is derived from the original that has the methods you need. Or you can extend the WebSecurity class to include the methods you need. Take a look at the SimpleSecurity open source project which decouples SimpleMembership from the ASP.NET MVC application. This article describes how to extend the WebSecurity class and queries the database directly. You can do something similar and query for a particular user by their email address.
But SimpleMembership does not support storing the users email address out-of-the-box. Take a look at this article on how to customize SimpleMembership to include the email address.
Also keep in mind that the reason that the base membership provider returns multiple users for an email address is that the schema does not restrict a user from opening multiple accounts with the same email address, unless the email address is used as the username.

Basic authentication in web api

I started working on Web Api and just want to create a simple basic authentication. I want to know how to do that?
I tried with the given MSDN link but no step wise tutorial is given on MSDN.
http://www.asp.net/web-api/overview/security/basic-authentication
The link you gave provides much of the detail you need, I hope this fills in the blanks.
Note: If using Web.API 2, Microsoft are suggesting a different approach using authentication filters.
Set up https on your server
This is quite important if you need real security otherwise passwords can be gleaned by snooping parties. How you do this depends a entirely on your setup, which you don't detail, but if you're working on an Azure WebRole there's a pretty good step-by-step guide to setting up SSL from Microsoft.
This isn’t required for the next steps, but should be done before you release your code. I mention it first because this part usually involves getting other people involved (sysadmin for server config, finance to purchase the certificate, etc) and it’s good to give them lots of warning.
Write (or steal) a custom IHttpModule to do your authentication
This is the big block of C# code in your link - it parses the values sent by the browser and sets HttpContext.Current.User to the authenticated user. Just copy and paste the meat into a class in your own application and we’ll come back to it later. You’ll need the following using statements in your code.
using System; using System.Net.Http.Headers; using System.Security.Principal;
using System.Text; using System.Threading; using System.Web;
Associate that module with your application
Add a new module to your web.config file (note system.webServer probably already exists)
<system.webServer>
<modules>
<add name="BasicAuth" type="Full.ClassName.Path.BasicAuth, Assembly.Name"/>
</modules>
</system.webServer>
Restrict access to the relevant parts of your site
You can block specific actions by adding the [Authorize] attribute before the action definition. Block a whole controller by adding it before your controller class.
[Authorize] // Restricts access to whole controller
public class StockController : ApiController {
[Authorize] // Restricts access to this action - not necessary if whole controller restricted.
public IEnumerable<StockLevel> Get() {
Or in your App_Start\WebApiConfig.cs file you can add config.Filters.Add(new AuthorizeAttribute()); and it will lock everything down.
Something to watch out for - there’s also a System.Web.Mvc.AuthorizeAttribute so if you have that namespace included you can get confusing results.
At this point you can try it out - user: "user", pass: "password".
Customize your user validation
Go back to the class we stole from the link and you'll see the following block of code:
// TODO: Here is where you would validate the username and password.
private static bool CheckPassword(string username, string password)
Alter this to return true if the username and password are valid. If you're rolling your own you may want to investigate bcrypt (do you trust the implementation you downloaded off the net?), PBKDF2 or the Crypto class (simple but not terribly secure) but there's probably something better from Microsoft as there are lot of concerns around storing passwords properly.
I had to add a few lines of code to the MSDN example to get it to work. Specifically, in OnApplicationAuthenticateRequest(), I set the response status code to 401 if the user could not be validated:
private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var request = HttpContext.Current.Request;
var authHeader = request.Headers["Authorization"];
bool validated = false;
if (authHeader != null)
{
var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);
// RFC 2617 sec 1.2, "scheme" name is case-insensitive
if (authHeaderVal.Scheme.Equals("basic",
StringComparison.OrdinalIgnoreCase) &&
authHeaderVal.Parameter != null)
{
validated = AuthenticateUser(authHeaderVal.Parameter);
}
}
if (!validated)
{
HttpContext.Current.Response.StatusCode = 401;
}
}
Once I did that, it worked fine. There's probably better ways to structure the logic, but this is about the smallest change from the example that does the job.
To selectively enable basic authentication on a per-controller or per-method basis, you can derive from AuthorizeAttribute as described in this question.

How to access a Service Reference from JavaScript?

(WCFDS = WCF Data Services 5,backed by Entity Framework, using oData v3, formatted as JSON, served up via IIS7 and protected by Windows authentication.)
The crux is accessing the WCFDS in an authenticated manner from an AJAX call.
To this end, I have a client as an ASP.Net Web Application with Windows authentication set in Web.config and a Service Reference pointing to the WCFDS.
I want to use client-side JavaScript to access the Service Reference. How can I do this?
I thought about creating an aspx page, hosting in the client and direct calls from JavaScript code to this page, which would then retrieve data through the Service Reference - but I'm at a loss over how to expose the full functionality of the Service Reference in this manner (there are dozens of entities).
Can anyone help with advice?
The Windows authorization settings in web.config are not directly related to WCF Data Services, so you probably won't need to set anything there. You WILL need to set your settings up properly in IIS.
There are a number of good articles out there about using Windows authorization over WCF Data Services; in a nutshell you have a wide degree of freedom in how you choose to expose authorization (ranging from filtering out individual entities from a feed to throwing 401/403s).
A couple of good articles to read through:
http://msdn.microsoft.com/en-us/data/gg192997
http://blogs.msdn.com/b/astoriateam/archive/2010/07/21/odata-and-authentication-part-7-forms-authentication.aspx (yes, I know that's forms auth but the auth part of this is entirely orthogonal to what the code looks like in your WCF Data Services)
http://blogs.msdn.com/b/astoriateam/archive/2010/07/19/odata-and-authentication-part-5-custom-httpmodules.aspx
The simplest code you could possibly write would be something along the lines of:
namespace Scratch.Web
{
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
public class ScratchService : DataService<ScratchContext>
{
[QueryInterceptor("Products")]
public Expression<Func<Product, bool>> ProductsAuthorization()
{
if (!HttpContext.Current.Request.IsAuthenticated)
{
return (p) => false;
}
return (p) => HttpContext.Current.User.IsInRole("AllowAccessToProducts");
}
// ...rest of service code...
}
}
Note that everything on the client side is controlled by the browser, so you don't really need to do anything there (the biggest step might be to add the site to Trusted Sites so it doesn't prompt you for your credentials if you're on a domain-joined machine).

Silverlight 4 - authentiation / authorization against custom wcf service

I have a wcf service in front of an AzMan store that passes roles and operations to clients using the following interface:
[OperationContract]
bool AuthenticateUser(string password, string appName);
[OperationContract]
string[] GetRoles(string storelocation, string appName);
[OperationContract]
string[] GetOperations(string storeLocation, string appName, string selectedRole);
Clients connect to this service using windows authentication (but users must send their password through to reaffirm their identity). Ultimately the service delivers an array of operations that each client can perform based on their selected role.
I've opened a new Silverlight Business Application and tried to understand how authentication/authorization works in this template, as well as scoured the web to find examples to how to hook my webservice to the login box already created in the template, but I am completely at a loss as how to do this!
Can anyone offer any advice?
The Business application template has an AuthenticationService, that is based on the User object and the AuthenticationBase class. AuthenticationBase has virtual methods that you can override to use your own security mechanisms.
For example, there is a Login method, based on a username and a password. This method returns a IUser that has a name and roles.
After looking at your interface, I'd create a sub-interface of IUser to include the list of allowed operations and change the generated User class to implement this sub-interface. And I'd override the Login and related methods in AuthenticationService to use your existing Azman-based code.