I am developing a web site using the following technologies:
MVC 4
EF 5
Web Api
Future - possible Windows Phone/Windows 8 application.
I am using Web API so that I have a developed api that I can use on other clients.
However, I will need to authorise the user each time a request is made to the API. My initial thought was to do this via the HTTP headers. However, I'm just wondering if I should just use MVC Controllers instead of Web API for the MVC application and create a RESTful api if I was to develop a phone/win 8 application, again the user would need to be authenticated. So the originally problem still exists.
What are people's thoughts? Can any one point me to a tutorial on how I could securely pass the authenticated users details over the HTTP Header, also something that's a step by step tutorial as I'm going into this from scratch and need to understand it.
I use basic authentication to pass the credentials for authorization. This puts the credentials in the header. To do this is pretty straight forward by using the beforeSend event handler of the JQuery ajax function. Here is an example of how to do this.
getAuthorizationHeader = function (username, password) {
var authType;
var up = $.base64.encode(username + ":" + password);
authType = "Basic " + up;
};
return authType;
};
$.ajax({
url: _url,
data: _data,
type: _type,
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization", getAuthorizationHeader(username, password));
},
success: ajaxSuccessHandler,
error: ajaxErrHandler
});
This encodes the username/password that is sent in the header. Note that this is not enough security to rely on just the encoding as it is easy to decode. You still want to use HTTPS/SSL to make sure the information sent over the wire is secure.
On the Web API side you can make a custom AuthorizeAttribute that gets the credentials from the header, decodes them, and performs your authorization process. There is a separate AuthorizeAttribute used by the Web API as opposed to the controller. Be sure to use System.Web.Http.AuthorizeAttribute as your base class when creating your custom AuthorizeAttribute. They have different behaviors. The one for the controller will want to redirect to the logon page whereas the one for the Web API returns an HTTP code indicating success or failure. I return an HTTP code of Forbidden if authorization fails to distinguish a failure due to authorization as opposed to authentication so the client can react accordingly.
Here is an example method for getting the credentials from the header that can be used in the custom AuthorizeAttribute.
private bool GetUserNameAndPassword(HttpActionContext actionContext, out string username, out string password)
{
bool gotIt = false;
username = string.Empty;
password = string.Empty;
IEnumerable<string> headerVals;
if (actionContext.Request.Headers.TryGetValues("Authorization", out headerVals))
{
try
{
string authHeader = headerVals.FirstOrDefault();
char[] delims = { ' ' };
string[] authHeaderTokens = authHeader.Split(new char[] { ' ' });
if (authHeaderTokens[0].Contains("Basic"))
{
string decodedStr = SecurityHelper.DecodeFrom64(authHeaderTokens[1]);
string[] unpw = decodedStr.Split(new char[] { ':' });
username = unpw[0];
password = unpw[1];
}
gotIt = true;
}
catch { gotIt = false; }
}
return gotIt;
}
And here is the code for decoding the header data that is used in this method.
public static string DecodeFrom64(string encodedData)
{
byte[] encodedDataAsBytes
= System.Convert.FromBase64String(encodedData);
string returnValue =
System.Text.Encoding.ASCII.GetString(encodedDataAsBytes);
return returnValue;
}
Once you have the username and password you can perform your authorization process and return the appropriate HTTP code to the client for handling.
Updated 3/8/2013
I wrote a blog post that goes into more details on how to implement this with SimpleMembership, the default membership provider for MVC 4 Internet Applications. It also includes a downloadable VS 2012 project that implements this.
Related
I have a scenario and any suggestions in implementing that will be of great help. I have a servlet created on publish that will have POST requests coming from a lot of other third party applications. This servlet just stores the incoming posted data in JCR. I have successfully created this servlet but now the requirement is to make this servlet secured so that only applications hitting this servlet with particular username and password should be entertained.
What can I do to achieve this?
The way I would go for it:
Ask those 3rd party applications to send you the username and password so you can validate them on your servlet, then decide if you will allow or reject the request.
from the servlet calling (the 3rd party application)
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// ...
request.setAttribute("username", "a_valid_user");
request.setAttribute("password", "a_valid_password");
request.getRequestDispatcher("yourApp/YourServlet").forward(req, resp);
}
On your servlet:
String username = request.getParameter("username");
String password = request.getParameter("password");
if("a_valid_user".equals(username) && "a_valid_password".equals(password) {
// validate and go further
} else {
// do not process the request
}
The above example is valid just in case you can validate them on your side.
If this sample doesn't answer to your question, please provide more information about those 3rd party applications and the way you want to validate them.
You might consider using Google Client Library. I used it for authentication of users in an AEM publish instance. After the third party server is authenticated, you could use a separate AEM service account to handle POST processing.
Here' a SO post I made about integrating those libraries into AEM.
Google Client API in OSGI
With this you should be able set up authentication of the third party service account... as discussed here
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
I haven't actually done server to server auth in AEM, but it should be possible. But in a separate project (non AEM) I've used the Google Client Library for authenticating Service Accounts.
I recommend to use a two step process:
Step 1: Authentication and generate a token, you can use 3rd party service also to generate token.
Step 2: Call your servlet with this token, the servlet will validate token first and then use post data.
Thanks everyone for your replies. In the end I implemented the below code for authentication in cq :
final String authorization = request.getHeader("Authorization");
if (authorization != null && authorization.startsWith("Basic")) {
StringTokenizer st = new StringTokenizer(authorization);
if (st.hasMoreTokens()) {
String basic = st.nextToken();
if (basic.equalsIgnoreCase("Basic")) {
String decodedStr = Base64.decode(st.nextToken());
LOGGER.info("Credentials: " + decodedStr);
int p = decodedStr.indexOf(":");
if (p != -1) {
String login = decodedStr.substring(0, p).trim();
String password = decodedStr.substring(p + 1).trim();
Credentials credentials = new SimpleCredentials(login, password.toCharArray());
adminSession = repository.login(credentials);
if (null != adminSession) {
// means authenticated and do your stuff here
}
}
}
}
}
Also in the webservice code which is calling the servlet of publish, below is the code on how I am supplying the credentials in auth headers :
String authStr = usrname+":"+password;
// encode data on your side using BASE64
byte[] bytesEncoded = Base64.encodeBase64(authStr.getBytes());
String authEncoded = new String(bytesEncoded);
connection.setRequestProperty("Authorization", "Basic "+authEncoded);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream());
writer.write("jsondata={sample:jsoncontent}");
writer.close();
I'm trying to implement OAuth security for a WCF SOAP service. I could find samples online which talks about OAUTH and REST service. Is there any best approach to use OAuth with WCF SOAP service. If it is possible to secure WCF SOAP usig OAUth, I also would like to know whether I could use claims based authorization in this case.
The short answer is a simple yes, you can do this. I tried to find an "official" way to do this and I was not successful, mostly because OAuth is not really designed for this scenario, more on that later. First though how to actually do it. One way to do it would be to provide a custom ServiceAuthorizationManager and inside of it do something like this
public class OAuthAuthorizationManager : ServiceAuthorizationManager
{
protected override bool CheckAccessCore(OperationContext operationContext)
{
// Extract the action URI from the OperationContext. Match this against the claims
// in the AuthorizationContext.
string action = operationContext.RequestContext.RequestMessage.Headers.Action;
try
{
//get the message
var message = operationContext.RequestContext.RequestMessage;
//get the http headers
var httpHeaders = ((System.ServiceModel.Channels.HttpRequestMessageProperty)message.Properties.Values.ElementAt(message.Properties.Keys.ToList().IndexOf("httpRequest"))).Headers;
//get authorization header
var authHeader = httpHeaders.GetValues("Authorization");
if (authHeader != null)
{
var parts = authHeader[0].Split(' ');
if (parts[0] == "Bearer")
{
var tokenClaims = ValidateJwt(parts[1]);
foreach (System.Security.Claims.Claim c in tokenClaims.Where(c => c.Type == "http://www.contoso.com/claims/allowedoperation"))
{
var authorized = true;
//other claims authorization logic etc....
if(authorized)
{
return true;
}
}
}
}
return false;
}
catch (Exception)
{
throw;
}
}
private static IEnumerable<System.Security.Claims.Claim> ValidateJwt(string jwt)
{
var handler = new JwtSecurityTokenHandler();
var validationParameters = new TokenValidationParameters()
{
ValidAudience = "urn://your.audience",
IssuerSigningKey = new InMemorySymmetricSecurityKey(Convert.FromBase64String("base64encoded symmetric key")),
ValidIssuer = "urn://your.issuer",
CertificateValidator = X509CertificateValidator.None,
RequireExpirationTime = true
};
try
{
SecurityToken validatedToken;
var principal = handler.ValidateToken(jwt, validationParameters, out validatedToken);
return principal.Claims;
}
catch (Exception e)
{
return new List<System.Security.Claims.Claim>();
}
}
}
be sure to also set the web.config to use this custom class using the serviceAuthorizationElement
This example requires the System.IdentityModel.Tokens.Jwt nuget package as well, your tokens might be in another format though and in that case you would need to just replace that logic in the example. Also, note that this example is assuming you will be passing our token on the Authorization header in the http request, the OAuth 2.0 Authorization Framework: Bearer Token Usage documentation also specifies that both form encoded body parameters and URI query paramters may be used as well. The form encoded body parameter method is probably entirely incompatible with SOAP services but I see no reason you could not adapt this code to also look at the query parameter method if needed.
What this code does is for every single request to your service the CheckAccessCore method will fire, inside it attempts to extract and validate the JWT oauth token then you can use the extracted principle and associated claims to authorize or deny authorization to the request.
All of this said, I think the best approach would be to not use OAuth at all, the above works but it is a hack to how WCF SOAP services are meant to be secured. OAuth is also not meant to authenticate the user, so you will need to do that in some other way prior to passing the bearer token obtained from authentication on to your service. If you absolutely must use OAuth you can use the above to get you started, there may be better ways but it is not easy by any measure to make it work and be readable. If you have not looked into WS-Security you should do that and familiarize yourself with the abundance of information and possibilities that exist for securing a soap based service most of which have numerous examples to go on here.
Is there an alternative to the session feature plugin in servicestack? In some scenarios I cannot use cookies to match the authorized session in my service implementation. Is there a possibility to resolve the session using a token in http header of the request? What is the preferred solution for that in case the browser is blocking cookies?
I'm using ServiceStack without the built-in auth and session providers.
I use a attribute as request filter to collect the user information (id and token), either from a cookie, request header or string parameter.
You can provide this information after the user takes login. You append a new cookie to the response and inject the id and token info on clientside when rendering the view, so you can use for http headers and query parameters for links.
public class AuthenticationAttribute : Attribute, IHasRequestFilter
{
public void RequestFilter(IHttpRequest request, IHttpResponse response, object dto)
{
var userAuth = new UserAuth { };
if(!string.IsNullOrWhiteSpace(request.GetCookieValue("auth"))
{
userAuth = (UserAuth)request.GetCookieValue("auth");
}
else if (!string.IsNullOrEmpty(request.Headers.Get("auth-key")) &&
!string.IsNullOrEmpty(request.Headers.Get("auth-id")))
{
userAuth.Id = request.Headers.Get("id");
userAuth.Token = request.Headers.Get("token");
}
authenticationService.Authenticate(userAuth.Id, userAuth.token);
}
public IHasRequestFilter Copy()
{
return new AuthenticationAttribute();
}
public int Priority { get { return -3; } } // negative are executed before global requests
}
If the user isn't authorized, i redirect him at this point.
My project supports SPA. If the user consumes the API with xmlhttprequests, the authentication stuff is done with headers. I inject that information on AngularJS when the page is loaded, and reuse it on all request (partial views, api consuming, etc). ServiceStack is powerful for this type of stuff, you can easily configure your AngularJS app and ServiceStack view engine to work side by side, validating every requests, globalizing your app, etc.
In case you don't have cookies and the requests aren't called by javascript, you can support the authentication without cookies if you always generate the links passing the id and token as query parameters, and pass them through hidden input on forms, for example.
#Guilherme Cardoso: In my current solution I am using a PreRequestFilters and the built-in session feature.
My workflow/workaround is the following:
When the user gets authorized I took the cookie and send it to the client by using an http header. Now the client can call services if the cookie is set in a http-header (Authorization) of the request.
To achieve this I redirect the faked authorization header to the cookie of the request using a PreRequestFilter. Now I am able to use the session feature. Feels like a hack but works for the moment ;-)
public class CookieRestoreFromAuthorizationHeaderPlugin : IPlugin
{
public void Register(IAppHost appHost)
{
appHost.PreRequestFilters.Add((req, res) =>
{
var cookieValue = req.GetCookieValue("ss-id");
if(!string.IsNullOrEmpty(cookieValue))
return;
var authorizationHeader = req.Headers.Get("Authorization");
if (!string.IsNullOrEmpty(authorizationHeader) && authorizationHeader.ToLower().StartsWith("basictoken "))
{
var cookie = Encoding.UTF8.GetString(Convert.FromBase64String(authorizationHeader.Split(' ').Last()));
req.Cookies.Add("ss-id",new Cookie("ss-id",cookie));
req.Items.Add("ss-id",cookie);
}
});
}
}
I'm creating an app with MVC4 that will authorize users using Twitter and lets them tweet from the app as well. I'm able to get the user authenticated without a problem using the BuiltInOAuthClient.Twitter that is in MVC4. http://www.asp.net/web-pages/tutorials/security/enabling-login-from-external-sites-in-an-aspnet-web-pages-site
I have the access token, and oauth_verifier, but I need to get the acess_secret back from Twitter as well. https://dev.twitter.com/docs/auth/implementing-sign-twitter
What I'm missing is how to pass the oauth_verifier back to Twitter to get the access secret using OAuthWebSecurity.
Again, I can use Twitter for the login ok, but I need to be able to use twitter as the user as well. I've done this with the TweetSharp library before, but am trying to use DotNetOpenAuth on this project.
UPDATE:
I'm using the OAuthWebSecurity class as described in the first link to manage authentication. OAuthWebSecurity.RegisterClient in the AuthConfig expects a DotNetOpenAuth.AspNet.IAuthenticationClient. You can't swap that out with the TwitterConsumer class as suggested.
I can use the "built in" DotNetOpenAuth authentication piece as described in the first link, OR I can use custom code to do the full authorization, but I'm trying to find a way to do both.
I can do it separately, but then the user is presented with the Twitter dialog twice (once to login and once to authorize). I'm hoping there's a way to use the already wired up authentication piece that uses OAuthWebSecurity but ad the authorization piece as well.
I've been banging my head against a wall with this for a few days now, but I finally have something that works. Would be interested to know if it's a valid solution though!
First off, create a new OAuthClient:
public class TwitterClient : OAuthClient
{
/// <summary>
/// The description of Twitter's OAuth protocol URIs for use with their "Sign in with Twitter" feature.
/// </summary>
public static readonly ServiceProviderDescription TwitterServiceDescription = new ServiceProviderDescription
{
RequestTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/request_token",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
UserAuthorizationEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/authenticate",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
AccessTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.twitter.com/oauth/access_token",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
};
public TwitterClient(string consumerKey, string consumerSecret) :
base("twitter", TwitterServiceDescription, consumerKey, consumerSecret) { }
/// Check if authentication succeeded after user is redirected back from the service provider.
/// The response token returned from service provider authentication result.
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
string accessToken = response.AccessToken;
string accessSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string userId = response.ExtraData["user_id"];
string userName = response.ExtraData["screen_name"];
var extraData = new Dictionary<string, string>()
{
{"accesstoken", accessToken},
{"accesssecret", accessSecret}
};
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
The important part is where you cast the response to an ITokenSecretContainingMessage. It appears that the response has the TokenSecret all along, but it is only on an internal property. By casting it, you get access to a public property. I can't say that I'm a fan of doing this, but then I also don't understand why DotNetOpenAuth the Asp.Net team have hidden the property in the first place. There must be a good reason.
You then register this client in AuthConfig:
OAuthWebSecurity.RegisterClient( new TwitterClient(
consumerKey: "",
consumerSecret: ""), "Twitter", null);
Now, in the ExternalLoginCallback method on the AccountController, the accessSecret is available in the ExtraData dictionary.
The DotNetOpenAuth.AspNet.Clients.TwitterClient class only allows authentication, not authorization. So you wouldn't be able to post tweets as that user if you use that class.
Instead, you can use DotNetOpenAuth.ApplicationBlock.TwitterConsumer, which does not share this limitation and you can even copy the source code for this type into your application and extend it as necessary.
You should be able to enhance the TwitterConsumer class (once you've copied it into your own project) to implement the required interface so that the OAuthWebSecurity class will accept it. Otherwise, you can just use TwitterConsumer directly yourself to both authenticate and authorize your web app so the user only sees Twitter once but you get all the control you need. After all, folks using ASP.NET have been using TwitterConsumer to both login and authorize for subsequent calls to Twitter for long before OAuthWebSecurity even existed.
For a WebForms project template which references Microsoft.AspNet.Membership.OpenAuth in AuthConfig.cs instead of Microsoft.Web.WebPages.OAuth (MVC4 Internet Application) I was able to modify Paul Manzotti's answer to get it to work:
Create a custom twitter client class that derives from DotNetOpenAuth.AspNet.Clients.TwitterClient
public class CustomTwitterClient : TwitterClient
{
public CustomTwitterClient(string consumerKey, string consumerSecret) :
base(consumerKey, consumerSecret) { }
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
//return base.VerifyAuthenticationCore(response);
string accessToken = response.AccessToken;
string accessSecret = (response as ITokenSecretContainingMessage).TokenSecret;
string userId = response.ExtraData["user_id"];
string userName = response.ExtraData["screen_name"];
var extraData = new Dictionary<string, string>()
{
{"accesstoken", accessToken},
{"accesssecret", accessSecret}
};
return new AuthenticationResult(
isSuccessful: true,
provider: ProviderName,
providerUserId: userId,
userName: userName,
extraData: extraData);
}
}
Add the custom client in AuthConfig.cs
public static void RegisterOpenAuth()
{
OpenAuth.AuthenticationClients.Add("Twitter", () => new CustomTwitterClient(
consumerKey: ConfigurationManager.AppSettings["twitterConsumerKey"],
consumerSecret: ConfigurationManager.AppSettings["twitterConsumerSecret"]));
}
Ta-dow! Now you can haz access secret.
You can extract the oauth_token_secret from OAuthWebSecurity by designing your own TokenManager. You can register the token manager when you register your Twitter client in OAuthWebSecurity.RegisterClient.
I used this method to extract the needed values to be able to bypass the authorization step of the Linq-to-Twitter lib.
I will soon post my solution at my blog.
Can anyone point me to a suitable WCF Extension Point for hooking into the WCF Pipeline to extract credentials for UserNamePasswordValidator from the headers of an incoming HTTP REST Request?
Yes I know about all the funky stunts with Http Handlers etc. you can pull to somehow get Basic/Digest Auth working but since the client I'm working on will be strictly Javascript based I've opted for a simple model where the credentials are passed using two custom headers over an SSL pipe.
Update: I've managed to improve on this by using the approach described here. While this does not solves the problem described in my question, it gets rid of having to authenticate in a authorization policy since authentication is now handled by a custom AuthenticationManager, bypassing the UsernamePasswordValidator alltogether.
For the time being I've solved the problem by combining Authentication and Authorization in a custom Authorization Policy. I'd still rather find a way to hook into the normal UserNamePasswordValidator authentication scheme because an Authorization Policy is supposed to to Authorization not Authentication.
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);
...