Authentication for BasicHttpBinding using Request Hearder - c#

I have a BasicHttpBinding WCF service. I want to get user name and password in request header. I searched in in the internet for this but I see just WSHttpBinding. I want to have something like this:
//WCF client call
WCFTestService.ServiceClient myService = new
WCFTestService.ServiceClient();
myService.ClientCredentials.UserName.UserName = "username";
myService.ClientCredentials.UserName.Password = "p#ssw0rd";
MessageBox.Show(myService.GetData(123));
myService.Close();
but I don't know what should I write for server side?
Thanks

You could create a custom Authorization Class by inheriting the ServiceAuthorizationManager class and pull out the credentials from the request header.
Your code could be similar to the following:
public class CustomAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
//Extract the Authorization header, and parse out the credentials converting the Base64 string:
var authHeader = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
if ((authHeader != null) && (authHeader != string.Empty))
{
var svcCredentials = System.Text.Encoding.ASCII
.GetString(Convert.FromBase64String(authHeader.Substring(6)))
.Split(':');
var user = new
{
Name = svcCredentials[0],
Password = svcCredentials[1]
};
if ((user.Name == "username" && user.Password == "p#ssw0rd"))
{
//User is authorized and originating call will proceed
return true;
}
else
{
//not authorized
return false;
}
}
else
{
//No authorization header was provided, so challenge the client to provide before proceeding:
WebOperationContext.Current.OutgoingResponse.Headers.Add("WWW-Authenticate: Basic realm=\"YourNameSpace\"");
//Throw an exception with the associated HTTP status code equivalent to HTTP status 401
throw new WebFaultException(HttpStatusCode.Unauthorized);
}
}
}
In addition to that, you need to set the serviceAuthorizationManagerType attribute of the serviceAuthorization element to your custom class in the web.config file.
Something similar to this:
<serviceAuthorization serviceAuthorizationManagerType="YourNameSpace.CustomAuthorizationManager, YourAssemblyName"/>
In the client side, you also need to add the credentials to the request headers.
HttpRequestMessageProperty httpReqProp = new HttpRequestMessageProperty();
httpReqProp.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("username"+ ":" + "p#ssw0rd"));
Security note:
Keep in mind that in Basic Authentication, the username and password will be sent as non-encrypted text in the request header. You should only implement this with SSL.

Related

Missing Authorization header in Basic authentication

I used following code to implement Basic Authentication filter in my ASP.Net MVC app. everything is working good in local machine while it's not working in production server and it keeps prompting login box because Request.Headers["Authorization"] is null.
I used fiddler to get headers for this request and Authorization header was there with expected values. I have no idea why Request.Headers["Authorization"] is always null :|
I also created a new project only with this filter and one controller and published in server, guess what !? it's working...
public class RequireBasicAuthenticationAttribute : ActionFilterAttribute
{
public string BasicRealm { get; set; }
protected string Username { get; set; }
protected string Password { get; set; }
public RequireBasicAuthenticationAttribute()
{
this.Username = System.Configuration.ConfigurationManager.AppSettings["ProtectedUsername"];
this.Password = System.Configuration.ConfigurationManager.AppSettings["ProtectedPassword"];
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.HttpContext.Request;
var auth = req.Headers["Authorization"];
auth.LogText();
if (!string.IsNullOrEmpty(auth))
{
var cred = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(auth.Substring(6))).Split(':');
var user = new { Name = cred[0], Pass = cred[1] };
if (Username.Equals(user.Name, StringComparison.InvariantCultureIgnoreCase) && Password.Equals(user.Pass)) return;
}
var res = filterContext.HttpContext.Response;
res.StatusCode = 401;
res.AddHeader("WWW-Authenticate", String.Format("Basic realm=\"{0}\"", BasicRealm ?? "bimeh-takmili"));
res.End();
}
}
Just looking at your code I don't see how it runs at all, production or otherwise.
I would suggest it's throwing an error that your code is swallowing since the code below closes the response and then tries to call the base method.
public override void ExecuteResult(ControllerContext context)
{
if (context == null) throw new ArgumentNullException("context");
// this is really the key to bringing up the basic authentication login prompt.
// this header is what tells the client we need basic authentication
var res = context.HttpContext.Response;
res.StatusCode = 401;
res.AddHeader("WWW-Authenticate", "Basic");
res.End();
base.ExecuteResult(context);
}
You cant do this, the code will throw an error:
Server cannot set status after HTTP headers have been sent.
And since it's throwing an error (i think) and being bounced around, it might not be output a 401 status response. The "WWW-Authenticate" header is still being sent however which is why your getting a dialog.
The credentials dialog is popped a when "WWW-Authenticate" is detected but it will only send back an Authorization header in the request if it received a 401 status from the last response.
So if you drop:
base.ExecuteResult(context);
from your code, what happens?
Edit:
Actually dropping
res.End();
would be the way to go. Duh.

Send Cookie in Post Response WebAPI

I am trying to send the user a cookie after I authenticate him. everything works perfect, the response is being constructed in my code, but even after the client got the response, There is no cookie saved in the browser (checking it via chrome F12 -> Resources).
Note: I can see the response being sent in fiddler with my cookie:
I wonder what is going wrong and why the browser doesn't save the cookie.
Here is the WebAPI function that handles the Post request:
public HttpResponseMessage Post([FromBody]User user)
{
IDal dal = new ProGamersDal();
var currentUser = dal.GetUser(user.Username, user.Password);
if (currentUser == null)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Bad request.");
}
else
{
var res = new HttpResponseMessage();
var cookie = new CookieHeaderValue("user",JsonConvert.SerializeObject(new ReponseUser(){username = currentUser.Username, role = currentUser.Role}));
cookie.Expires = DateTimeOffset.Now.AddDays(1);
cookie.Domain = Request.RequestUri.Host;
cookie.Path = "/";
res.Headers.AddCookies(new CookieHeaderValue[] { cookie });
return res;
}
}
I've found out what the problem is since in Firefox the cookie was saved.
In chrome you cannot set a cookie with the domain of 'localhost' since it is considered as invalid domain (valid domain must contain two dots in it) - and therefore the cookie is invalid.
In order to solve it, in case of localhost, you should either:
set the domain to null.
set the domain to '' (empty)
set the domain to '127.0.0.1'
This is the fix in my code:
cookie.Domain = Request.RequestUri.Host == "localhost" ? null : Request.RequestUri.Host;

Adding and Retrieving data from request context

I'm trying to attach an api key to the OperationContext outgoing message header as follows:
public static void AddApikeyToHeader(string apikey, IContextChannel channel, string address)
{
using (OperationContextScope scope = new OperationContextScope(channel))
{
MessageHeader header = MessageHeader.CreateHeader("apikey", address, apikey);
OperationContext.Current.OutgoingMessageHeaders.Add(header);
}
}
but then I have no idea how to retrieve the header on the server side. I'm using a Service authorisation manager and I get the current operating context and try to retrieve the header like this:
public string GetApiKey(OperationContext operationContext)
{
var request = operationContext.RequestContext.RequestMessage;
var prop = (HttpRequestMessageProperty)request.Properties[HttpRequestMessageProperty.Name];
return prop.Headers["apikey"];
}
but there is no apikey header attached there. Also, on debugging when I inspect the operationContext I cant seem to see my apikey header anywhere. Can anyone see where I'm going wrong?
You can add custom header by this way :
using (ChannelFactory<IMyServiceChannel> factory =
new ChannelFactory<IMyServiceChannel>(new NetTcpBinding()))
{
using (IMyServiceChannel proxy = factory.CreateChannel(...))
{
using ( OperationContextScope scope = new OperationContextScope(proxy) )
{
Guid apiKey = Guid.NewGuid();
MessageHeader<Guid> mhg = new MessageHeader<Guid>(apiKey);
MessageHeader untyped = mhg.GetUntypedHeader("apiKey", "ns");
OperationContext.Current.OutgoingMessageHeaders.Add(untyped);
proxy.DoOperation(...);
}
}
}
And service side, you can get header like :
Guid apiKey =
OperationContext.Current.IncomingMessageHeaders.GetHeader<Guid>("apiKey", "ns");
I'm assuming that you trying to consume your service using some Http Protocol based transport (SOAP, REST etc). I'm also assuming that what you want is to authorize the caller using the supplied API key. If both of those conditions apply to your question, you can read on.
I recently had to tackle a similar problem only that I did not pass an API key but a username/password hash combination using some HTTP custom headers. I ultimately solved it by implementing a custom authorization policy that once configured in Web.config hooked nicely into the WCF Pipeline.
The snippet below should be enough to get you started. You probably would have to replace the x-ms-credentials-XXX headers by a single one representing your API key.
internal class RESTAuthorizationPolicy : IAuthorizationPolicy
{
public RESTAuthorizationPolicy()
{
Id = Guid.NewGuid().ToString();
Issuer = ClaimSet.System;
}
public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
const String HttpRequestKey = "httpRequest";
const String UsernameHeaderKey = "x-ms-credentials-username";
const String PasswordHeaderKey = "x-ms-credentials-password";
const String IdentitiesKey = "Identities";
const String PrincipalKey = "Principal";
// Check if the properties of the context has the identities list
if (evaluationContext.Properties.Count > 0 ||
evaluationContext.Properties.ContainsKey(IdentitiesKey) ||
!OperationContext.Current.IncomingMessageProperties.ContainsKey(HttpRequestKey))
return false;
// get http request
var httpRequest = (HttpRequestMessageProperty)OperationContext.Current.IncomingMessageProperties[HttpRequestKey];
// extract credentials
var username = httpRequest.Headers[UsernameHeaderKey];
var password = httpRequest.Headers[PasswordHeaderKey];
// verify credentials complete
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
return false;
// Get or create the identities list
if (!evaluationContext.Properties.ContainsKey(IdentitiesKey))
evaluationContext.Properties[IdentitiesKey] = new List<IIdentity>();
var identities = (List<IIdentity>) evaluationContext.Properties[IdentitiesKey];
// lookup user
using (var con = ServiceLocator.Current.GetInstance<IDbConnection>())
{
using (var userDao = ServiceLocator.Current.GetDao<IUserDao>(con))
{
var user = userDao.GetUserByUsernamePassword(username, password);
...
Did you take a look at this question: How to add a custom HTTP header to every WCF call? ? It may contain your solution.

Getting information with Token. OAuth

I am creating an app to get information from Fitbit.com using OAuth.
protected void btnConnect_Click(object sender, EventArgs e)
{
// Create OAuthService object, containing oauth consumer configuration
OAuthService service = OAuthService.Create(
new EndPoint(RequestTokenUrl, "POST"), // requestTokenEndPoint
new Uri(AuthorizationUrl), // authorizationUri
new EndPoint(AccessTokenUrl, "POST"), // accessTokenEndPoint
true, // useAuthorizationHeader
"http://app.fitbit.com", // realm
"HMAC-SHA1", // signatureMethod
"1.0", // oauthVersion
new OAuthConsumer(ConsumerKey, ConsumerSecret) // consumer
);
try
{
var personRepository = new PersonRepository();
var person = personRepository.GetPersonById(int.Parse(personSelect.SelectedItem.Value));
OAuthRequest request = OAuthRequest.Create(
new EndPoint(ProfileUrl, "GET"),
service,
this.Context.Request.Url,
//this.Context.Session.SessionID);
person.FitbitAuthAccessToken,
);
request.VerificationHandler = AspNetOAuthRequest.HandleVerification;
OAuthResponse response = request.GetResource();
// Check if OAuthResponse object has protected resource
if (!response.HasProtectedResource)
{
var token = new OAuthToken(TokenType.Request, person.FitbitAuthAccessToken,
person.FitbitAuthSecret, ConsumerKey);
// If not we are not authorized yet, build authorization URL and redirect to it
string authorizationUrl = service.BuildAuthorizationUrl(response.Token).AbsoluteUri;
Response.Redirect(authorizationUrl);
}
person.FitbitAuthAccessToken = response.Token.Token;
person.FitbitAuthSecret = response.Token.Secret;
person.PersonEncodedId = Doc["result"]["user"]["encodedId"].InnerText;
personRepository.Update(person);
// Store the access token in session variable
Session["access_token"] = response.Token;
}
catch (WebException ex)
{
Response.Write(ex.Message);
Response.Close();
}
catch (OAuthRequestException ex)
{
Response.Write(ex.Message);
Response.Close();
}
}
I save Fitbit Access Token and Secret in database.
How can I get information using just Access token and secret, without authorizing every time?
This would assume that the FitBit api was robust enough to not quire authentication every single time. I have seen API's implementing OAuth where you have an authentication process, then from there most of your calls simply require the AccessToken or secret. I would look at the method signatures for the service and see what types of parameters they are requiring.
If you look at the FitBit API about authentication and accessing resources, you will see that you just need to request the data you are interested in and add in the oAuth header with the access token. Here is what it should look like (from the API page):
GET /1/user/-/activities/date/2010-04-02.json HTTP/1.1
Host: api.fitbit.com
Authorization: OAuth realm="api.fitbit.com",
oauth_consumer_key="fitbit-example-client-application",
oauth_token="8d3221fb072f31b5ef1b3bcfc5d8a27a",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1270248088",
oauth_nonce="515379974",
oauth_signature="Gf5NUq1Pvg3DrtxHJyVaMXq4Foo%3D"
oauth_version="1.0"`
The base signature string will look like:
GET&http%3A%2F%2Fapi.fitbit.com%2F1%2Fuser%2F-%2Factivities%2Fdate%2F2010-04-02.json&oauth_consumer_key%3Dfitbit-example-client-application%26oauth_nonce%3D515379974%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1270248088%26oauth_token%3D8d3221fb072f31b5ef1b3bcfc5d8a27a%26oauth_version%3D1.0
I figured I'd offer my VerifyAuthenticationCore that is part of my FitbitClient that inherits from OAuthClient. It took me a while to get this working but I found that I was missing HttpDeliveryMethods.AuthorizationHeaderRequest when I was creating the web request. Adding this allowed the call to stop returning bad request (400) error messages.
The code below is basically using the user id and the access token to get the user profile information. All calls should basically work this way. All you would need to do is change the url and provide the id and token.
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
string username;
var accessToken = response.AccessToken;
var userId = response.ExtraData["encoded_user_id"];
var httpWebRequest = WebWorker.PrepareAuthorizedRequest(new MessageReceivingEndpoint(new Uri("http://api.fitbit.com/1/user/" + userId + "/profile.json"), HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest), accessToken);
var dictionary = new Dictionary<string, string>();
dictionary.Add("accesstoken", accessToken);
dictionary.Add("link", "http://www.fitbit.com/user/" + userId);
using (var webResponse = httpWebRequest.GetResponse())
{
using (var stream = webResponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
var profile = JObject.Parse(reader.ReadToEnd())["user"];
dictionary.AddItemIfNotEmpty("name", profile["displayName"]);
dictionary.AddItemIfNotEmpty("pictureUrl", profile["avatar"]);
username = dictionary["name"];
}
}
return new AuthenticationResult(true, ProviderName, userId, username, dictionary);
}

Reading username and password sent from and Android app into my WCF REST Service?

My current WCF REST Method is defined as:
[OperationContract]
[WebGet(UriTemplate = "{username}/{password}", ResponseFormat =
WebMessageFormat.Json)]
string Login(string username, string password);
An android client app is going to connect to the service, let's say it is http://service.com/login.svc/login...
But I don't want the username and password to be passed in the url like I have specified in the UriTemplate. How can I receive the username and password from the android app into my service, or better yet, how can I change my login method to retrieve the username and password in some POST parameters that I can process in my login function and validate the user against a sql membership database.
We have done this via using the "Authorization" header. The clients pass along an encrypted set of credentials and we generate a token for them on our side. Here is an example of the BeginRequest method of an HttpModule that handles authentication. We use a custom principal to handle the token:
private void BeginRequest(Object source, EventArgs e)
{
if (null == HttpContext.Current || String.IsNullOrEmpty(HttpContext.Current.Request.Headers["Authorization"]))
{
HttpContext.Current.Response.StatusCode = (Int32)HttpStatusCode.Unauthorized;
HttpContext.Current.Response.End();
}
HttpContext context = HttpContext.Current;
Regex matcher = new Regex(WfmConfigurationManager.GetAppSetting("AuthenticationPath"));
if (!matcher.IsMatch(context.Request.Url.ToString(),0))
{
String authHeader = context.Request.Headers["Authorization"];
IIdentity tokenIdentity = new TokenIdentity(authHeader);
if (!tokenIdentity.IsAuthenticated)
{
HttpContext.Current.Response.StatusCode = (Int32)HttpStatusCode.Unauthorized;
HttpContext.Current.Response.End();
}
IPrincipal tokenPrincipal = new TokenPrincipal(tokenIdentity, TokenAuthentication.GetRolesForUser(tokenIdentity));
HttpContext.Current.User = tokenPrincipal;
}
}

Categories