I am using the new oAuthWebSecurity functionality in MVC4 to do Facebook authentication for users of the site and it works great.
However, what I would like to do is that for a specific controller only, enable basic HTTP authentication.
I have tried implementing a custom action filter (authenticationFilter) to intercept the call and do the basic authentication with custom code but the code never hits the overloads of the AuthorizationFilter.
Is there an easier way to implement this rather than creating a custom SimpleMembershipProvider?
You can use [Authorize] filter is as below.
public class BooksController : ApiController
{
[Authorize]
public IEnumerable<Book> Get()
{
var result = new List<Book>()
{
new Book()
{
Author = "John Fowles",
Title = "The Magus",
Description = "A major work of mounting tensions " +
"in which the human mind is the guinea-pig."
},
new Book()
{
Author = "Stanislaw Ulam",
Title = "Adventures of a Mathematician",
Description = "The autobiography of mathematician Stanislaw Ulam, " +
"one of the great scientific minds of the twentieth century."
}
};
return result;
}
}
For more information check Basic HTTP authentication
I hope this will help to you.
You can create custom AuthorizeAttribute to handle both authentication and authorization using basic authentication. This attribute works as a filter and will process the request before it gets to your controller action or Web API method. In the overridden OnAuthorize method you can grab the header information to perform authentication.
If you are using ajax to make request to a controller or Web API method 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. Use jquery.base64.js to encode the information being sent over. 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 server side you can make a custom AuthorizeAttribute that gets the credentials from the header, decodes them, and performs your authentication/authorization process. Note that there is a a 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 if you are using Web API. 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 authentication and authorization using the SimpleMembership provider.
Related
Let me put the problem with a bit of structure.
Context
We have a web application build with Web Forms and hosted in an Azure Web App that authenticates the users against an Azure Active Directory using the OWIN + OpenId Connect standards.
The authentication process works like a charm and users are able to access the application without any problem.
So, whats the issue?
After struggling for many days with it I'm unable to pass any query string parameter to the application through the authentication process. For example, if I try to access the application for the first time through the URL: https://myapp.azurewebsites.net/Default.aspx?param=value. The reason I need to pass this parameter is that it triggers some specific actions in the main page.
The problem is that after the authentication redirects to the webapp's main page the original query string parameters of the request are gone.
The code
The startup class looks like this:
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = Constants.ADTenant.ClientId,
Authority = Constants.ADTenant.Authority,
PostLogoutRedirectUri = Constants.ADTenant.PostLogoutRedirectUri,
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = context =>
{
var code = context.Code;
ClientCredential credential = new ClientCredential(Constants.ADTenant.ClientId,
Constants.ADTenant.AppKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst(
Constants.ADTenant.ObjectIdClaimType).Value;
AuthenticationContext authContext = new AuthenticationContext(Constants.ADTenant.Authority,
new NaiveSessionCache(userObjectID));
if (HttpContext.Current != null)
{
AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential,
Constants.ADTenant.GraphResourceId);
AuthenticationHelper.token = result.AccessToken;
AuthenticationHelper.refreshToken = result.RefreshToken;
}
return Task.FromResult(0);
}
}
});
And it works properly!
What I already tried
I've got access to the original request Url by adding an overwrite of the RedirectToIdentityProvider notification:
RedirectToIdentityProvider = (context) =>
{
// Ensure the URI is picked up dynamically from the request;
string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase + context.Request.Uri.PathAndQuery;
context.ProtocolMessage.RedirectUri = appBaseUrl;
return Task.FromResult(0);
}
With this I tried to force the redirect to the main page including the original query string parameter, but then the redirection after authentication breaks and gets stuck in an infinite loop.
I've also tried with changing the redirect url of the application configuration in Azure AD without luck. Also tried to store the query string parameters somewhere else, but the Session is not accessible that early in the process.
Does anyone know what am I doing wrong? Or I'm just asking for something impossible? Any help would be appreciated.
Thank you very much in advance!
I recently had a need to do the exact same thing. My solution may not be the most sophisticated, but simple isn't always bad either.
I have two Authentication Filters...
The first filter is applied to all controllers that could potentially be hit with query string parameters prior to authorization. It checks if the principal is authenticated. If false it caches the complete url string in a cookie. If true it looks for any cookies present and clears them, just for cleanup.
public class AuthCheckActionFilter : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
if (!filterContext.Principal.Identity.IsAuthenticated)
{
HttpCookie cookie = new HttpCookie("OnAuthenticateAction");
cookie.Value = filterContext.HttpContext.Request.Url.OriginalString;
filterContext.HttpContext.Response.Cookies.Add(cookie);
}
else
{
if (filterContext.HttpContext.Request.Cookies.AllKeys.Contains("OnAuthenticateAction"))
{
HttpCookie cookie = filterContext.HttpContext.Request.Cookies["OnAuthenticateAction"];
cookie.Expires = DateTime.Now.AddDays(-1);
filterContext.HttpContext.Response.Cookies.Add(cookie);
}
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
}
}
The second filter is applied only to the default landing page, or in other words where the identity server is redirecting after login. This second filter looks for a cookie and if it exists it calls response.Redirect on cookie value.
public class AutoRedirectFilter : ActionFilterAttribute, IAuthenticationFilter
{
public void OnAuthentication(AuthenticationContext filterContext)
{
if(filterContext.Principal.Identity.IsAuthenticated)
{
if(filterContext.HttpContext.Request.Cookies.AllKeys.Contains("OnAuthenticateAction"))
{
HttpCookie cookie = filterContext.HttpContext.Request.Cookies["OnAuthenticateAction"];
filterContext.HttpContext.Response.Redirect(cookie.Value);
}
}
}
public void OnAuthenticationChallenge(AuthenticationChallengeContext filterContext)
{
}
}
How can I redirect a user from one Asp.net Mvc site to another Asp.net MVC site and automatically log them in?
The situation is that we will have some customers that need to go to one site and some that will need to go to the other. I've been asked to make it so that when customers are redirected to the correct site that they are also auto logged in to the site they are redirected to.
Assuming you don't want to integrate existing single sign-on solution and that you are using forms authentication for both sites and those sites are not on the same domain. The forms authentication in MVC is done via cookie. So the task is when you're logged in to Site1 to create authentication cookie on Site2.
Usually you craft a request to Site2 like:
/Impersonate/Start?encryptedToken=some_encrypted_stuff
And Site2 handling it like:
[DataContract]
public class Token
{
[DataMember(Name = "u")]
public string UserName { get; set; }
[DataMember(Name = "t")]
public DateTime TimeStamp { get; set; }
[DataMember(Name = "m")]
public string Magic { get; set; }
public Token()
{
Magic = MAGIC;
TimeStamp = DateTime.Now;
}
public const string MAGIC = "SOME_RANDOM_STRING";
}
public class ImpersonateController : Controller
{
[HttpGet]
public ActionResult Start(string encryptedToken)
{
// symmetric encryption - hopefully you know how to do it :)
string decryptedToken = Decrypt(encryptedToken);
var serializer = new DataContractJsonSerializer(typeof(Token));
Token token;
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(decryptedToken)))
{
token = serializer.ReadObject(stream);
}
if (!string.Equals(token.Magic, Token.MAGIC) ||
(DateTime.Now - token.TimeStap).TotalMinutes > 1))
{
// magic doesn't match or timestamp is too old
throw new Exception("Invalid token.");
}
FormsAuthentication.SetAuthCookie(token.UserName, true);
return new HttpStatusCodeResult(HttpStatusCode.OK);
}
}
Maybe needless to say /Impersonate/Start should be under https.
As for crafting the request - you can put it directly into view & make request via json.
In Site1:
public class LoginController : Controller
{
public ActionResult Login(string userName, string password)
{
// ... validate user logs to site 1 etc
var site2UserName = userName;
var token = new Token { UserName = site2UserName };
var serializer = new DataContractJsonSerializer(typeof(Token));
string decryptedToken;
using (var stream = new MemoryStream())
{
serializer.WriteObject(stream, token);
decryptedToken = Encoding.UTF8.GetString(stream.ToArray());
}
// symmetrical encryption
return new View(new LoginMode { Token = HttpUtility.UrlEncode(Encrypt(decryptedToken)) });
}
}
View (assuming you have jQuery)
$(function() {
$.get("https://site2.com/Impersonate/Start?token=#Html.Raw(Model.Token)");
});
So right after you log-in to Site1 you serve view that uses AJAX to send request to Site2 that will authenticate user also there. It is usually better idea to do it on request - the form authentication cookie for Site2 will eventually expire. So I'd favor something like like this on Site1:
Continue to site 2
And
[HttpGet]
[Authorize]
public ActionResult StartImpersonation()
{
// this is essentially similar to Login action
string encryptedToken = "";
string redirectUrl = string.Format(CultureInfo.InvariantCulture,
"https://site2.com/Impersonate/Start?encryptedToken={0}",
HttpUtility.UrlEncode(encryptedToken));
return Redirect(redirectUrl);
}
Which is better because a) cookie on Site2 can't expire b) if there is an error in impersonation user will see why (if there is an error in AJAX impersonation you can show some error to user, but it will look weird - authentication to site 2 haven't succeeded - why they're trying to authenticate me there ? :).
You want a single-sign-on (SSO) solution. This may be done any number of ways. OpenID is popular: https://en.wikipedia.org/wiki/OpenID This goes into plenty of details on a slightly older approach: http://www.codeproject.com/Articles/114484/Single-Sign-On-SSO-for-cross-domain-ASP-NET-appl Even more stuff here: C# ASP.NET Single Sign-On Implementation
HTH
On the link that would take someone from one site to the other, here's some of what you could put in the JS in a few places that may do what you want:
On clicking the link, a $.get is done to grab the HTML of the log-in page.
Then put into JS variables the login and password of the user for the second site's security into that HTML.
Post the data through a $.ajax request so that the person is logged in through the JS behind the scenes.
Now, either redirect in the current window or open a new window with the other site's home page and voila they are signed in without having to do any extra lifting if their own.
Note that some of this could be done earlier if you want to make the transition easier as when the page with the link loads, this could be done in the JS when the document is ready. The key point here is to have the cookies required for the authentication on the second site done without the user having to do any extra clicks.
I'm trying to understand if it is possible to post on the users' wall from my Facebook application.
At the moment I have:
One Facebook app with the permission to write on the users' wall
A BackEnd with Fairbooks SDK Installed
Actually I'm following this approach:
public static string GetToken()
{
var fb = new Facebook.FacebookClient();
dynamic result = fb.Get("oauth/access_token", new
{
client_id = APP_ID,
client_secret = APP_S,
grant_type = "client_credentials"
});
return result.access_token;
}
public static void Post(string Message, long UserID)
{
var token = GetToken();
var client = new FacebookClient(token);
client.Post("/" + UserID + "/photos", new { url = "url", caption = Message });
}
My final goal is to post on facebook when the user interact with my API without client-side popups. Is this possible?
This line of code calls for an application access token
dynamic result = fb.Get("oauth/access_token", new
{
client_id = APP_ID,
client_secret = APP_S,
grant_type = "client_credentials"
});
It makes no sense to use this if you haven't first retrieved a user access token in advance. Only then can you make calls on behalf of the user.
My final goal is to post on facebook when the user interact with my API without client-side popups. Is this possible?
This will never be possible by design. All 3rd party applications must invoke a client-side activity for the user in some format. It cannot be automated.
TL;DR : Is there any way to use the auth=CREDENTIALS with the Simple Login (Email/Password) in Firebase?
I am trying to connect my C# Application's users to my Firebase. I could set up pretty much all calls using my Secret Token, but now I need to be able to, at least, get the current user UID so I know where the data should be sent to.
The way I went with my PUSH, PUT, GET request was something like this, using my secret token as login:
var authToken = "SECRET";
url = "https://MyLocation.firebaseio.com/" + url + ".json?auth=" + authToken;
return WebRequest.Create(url);
But now I'd like to get something supporting the Email/Password simple login, something like this:
var authToken = "{email:an#email.com, password:thePassword}";
url = "https://MyLocation.firebaseio.com/" + url + ".json?auth=" + authToken;
return WebRequest.Create(url);
My tries using CURL weren't successful... Maybe there's no way to do that? or any suggestions?
Thanks for the help!
I spoke with the support at Firebase and found a temporary solution, and a real solution.
Real solution: Manage the user and their password manually in all environments, using Firebase as "Database". That was basically what I was trying to do with my question. That resolve in using Firebase custom auth.
Temporary solution: (And what I did as I do not need as much security as the real solution offers)
Get something that identify the current user. Here I can get the current user email without even asking him.
Base64 the identifier:
byte[] result = System.Text.Encoding.UTF8.GetBytes(email);
email = Convert.ToBase64String(result);
Put, push, patch the required information via REST to firebaseio.com/Base64
In the user interface, that uses JavaScript, do the same process to read/write data at the user, using something like base64.min.js
var ref = new Firebase("https://aFirebase.firebaseio.com");
//Things happen
...
//We register a user
function createUser(email, password){
//Allows us to create a user within firebase
ref.createUser({
email : email,
password : password
}, function(error, userData){
if (error) {
//The creation of the user failed
alert(error);
} else {
//The creation of the user succeeded
console.log("Successfully created user account with uid:", userData.uid);
//We make sure we are at the correct position in our firebase
ref = ref.root().child(base64.encode(email));
//We check if the child exist
if(ref == ref.root()){
//The child doesn't exist
//We have to create it
user = {};
//Set the child with a value for the UID, that will fit with the rules
user[base64.encode(email)] = {uid:userData.uid};
//We set the new child with his value in firebase
ref.set(user);
}else{
//The child exist, we can update his information to go accordingly with our rules
ref.update({uid:userData.uid});
}
//Who wants to register and then not be logged in?
//We can add something upon login if his email is not validated...
login(email, password);
}
}
);
}
Now we have to update our rules in Firebase:
{
"rules": {
"$uid":{
".read":"!(data.child('uid').exists() == true) || data.child('uid').val() == auth.uid",
".write":"!(data.child('uid').exists() == true) || data.child('uid').val() == auth.uid"
}
}
}
With this, the application is somehow secure (as long as the user use the C# application and the JS application, where the rules will be set).
In case of a WebApi application a JWT token could be used along with OWIN pipeline.
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { FirebaseValidAudience },
Provider = new OAuthBearerAuthenticationProvider
{
OnValidateIdentity = OnValidateIdentity
},
TokenValidationParameters = new TokenValidationParameters
{
IssuerSigningKeys = issuerSigningKeys,
ValidAudience = FirebaseValidAudience,
ValidIssuer = FirebaseValidIssuer,
IssuerSigningKeyResolver = (arbitrarily, declaring, these, parameters) => issuerSigningKeys
}
});
Here is the sample of Firebase ASP.NET WebApi Authentication application: https://github.com/PavelDumin/firebase-webapi-auth
I have inherited an existing application. This application uses ASP.NET MVC 3. It has some APIs. Those APIs look like the following:
[AcceptVerbs(HttpVerbs.Post)]
[Endpoint]
public ActionResult AuthenticatePlayer(string username, string password)
{
// Ensure that the user entered valid credentials
if (Membership.ValidateUser(username, password) == false)
return Json(new { statusCode = StatusCodes.INVALID_CREDENTIALS, message = "You entered an invalid username or password. Please try again." });
// Get the profile of the person that just logged in.
ProfileCommon userProfile = (ProfileCommon)(ProfileCommon.Create(username));
if (userProfile != null)
{
string name = username;
if (String.IsNullOrEmpty(userProfile.FirstName) == false)
name = userProfile.FirstName;
return Json(new {
statusCode = StatusCodes.SUCCESS,
payload = name,
username = username.ToLower(),
});
}
}
[AcceptVerbs(HttpVerbs.Get)]
[Endpoint]
public ActionResult SomeUserAction(string q)
{
// TODO: Ensure the user is authorized to perform this action via a token
// Do something
return Json(new { original = q, response = DateTime.UtcNow.Millisecond }, JsonRequestBehavior.AllowGet);
}
I'm trying to figure out how to integrate a token-based authorization schema into this process. From my understanding, a token-based system would return a short-lived token and a refresh token to a user if they successfully login. Then, each method can check to see if a user is authorized to perform the action by looking at the token. I'm trying to learn if this is built-in to ASP.NET MVC or if there is a library I can use. I need to figure out the shortest way to get this done.
Thank you so much!
I've built a WebAPI Token Authentication library a year ago, providing Token based authentication:
WebAPI Token Auth Bootstrap is out of the box Token based User Auth for WebAPI applications, Provides ready to use 'TokenAuthorize'
Attribute and 'TokenAuthApiController' Controller.
Among its features - Token Based User Authentication User Property inside the
TokenAuthApiController (Id, Username, Role, LastAccess).
Token Based User Authorization TokenAuthorizeAttribute with Access
Level - Public, User, Admin or Anonymous.
Built-in Functionality Login(), Logoff(), Error(), Unauthorized()
Responses with various overloads.
You can read more about here and in its own wiki in GitHub.
Nowadays I am working on a Node.js application and I am using Json Web Tokens (JWT) using Node.js library and it is very easy and straightforward.. its Node.js after all ;)
I saw there is a .NET implementation of JWT explained on this article which I recommend you to look at.
You can use Owin ... i.e. Microsoft.owin.security
I haven't tried this implementation but this is just to give you an idea:
var identity = new ClaimsIdentity(Startup.OAuthBearerOptions.AuthenticationType);
var currentUtc = new SystemClock().UtcNow;
ticket.Properties.IssuedUtc = currentUtc;
ticket.Properties.ExpiresUtc = currentUtc.Add(TimeSpan.FromMinutes(30));
DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return Json(new {
statusCode = StatusCodes.SUCCESS,
payload = name,
username = username.ToLower(),
accessToken = Startup.OAuthBearerOptions.AccessTokenFormat.Protect(ticket)
});