I have setup a basic authentication ActionFilterAttribute in my MVC web site to lock it down while in development, which works 100% across all the browsers that I am testing for (IE9+, Chrome, FF, iOS Safari) but when I load up Chrome in Android 4.0, it simply displays a 401 access denied and NEVER asks me for the basic authentication credentials?
This is my code for the OnAuthorization method:
public void OnAuthorization(AuthorizationContext filterContext)
{
var controllerName = (filterContext.RouteData.Values["controller"] as string).ToLower();
if (_controllersToIgnore.Contains(controllerName))
{
return;
}
bool credentialsMatch = false;
var req = filterContext.HttpContext.Request;
if (!string.IsNullOrEmpty(req.Headers["Authorization"]))
{
var cred = System.Text.Encoding.ASCII.GetString(Convert.FromBase64String(req.Headers["Authorization"].Substring(6))).Split(':');
var user = new { Name = cred[0], Pass = cred[1] };
if (user.Name == Username && user.Pass == Password)
{
credentialsMatch = true;
}
}
if (!credentialsMatch)
{
var res = filterContext.HttpContext.Response;
res.StatusCode = 401;
res.AddHeader("WWW-Authenticate", "Basic realm=\"\"");
res.End();
filterContext.Result = new EmptyResult();
}
}
This problem went away on its own strangely enough, I did nothing (apparent) and it just stopped occurring.
Related
I have an Asp.net web api, which is configured with OAuth. Now I have new client who cannot use Oauth but wants to use Basic Authentication with the same endpoint url.
Haven't found any ways to do this yet. Any help on this is appreciated. Thanks in Advance
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if ((Thread.CurrentPrincipal.Identity.Name?.Length ?? 0) <= 0)
{
AuthenticationHeaderValue auth = actionContext.Request.Headers.Authorization;
if (string.Compare(auth.Scheme, "Basic", StringComparison.OrdinalIgnoreCase) == 0)
{
string credentials = UTF8Encoding.UTF8.GetString(Convert.FromBase64String(auth.Parameter));
int separatorIndex = credentials.IndexOf(':');
if (separatorIndex >= 0)
{
string userName = credentials.Substring(0, separatorIndex);
string password = credentials.Substring(separatorIndex + 1);
var userManager = new MembershipUserManager();
var user = userManager.FindAsync(userName, password).Result;
if (user != null)
Thread.CurrentPrincipal = actionContext.ControllerContext.RequestContext.Principal = new GenericPrincipal(new GenericIdentity(userName, "Basic"), System.Web.Security.Roles.Provider.GetRolesForUser(userName));
}
}
}
return base.IsAuthorized(actionContext);
}
}
Use this code once you have set up the token auth (Oauth) and this would work for both: This attribute should be used everywhere (ditch the Authorize) [contains roles] and would verify the Basic auth, whereas the base.IsAuthorized(actionContext); would verify the token approach (Oauth).
MembershipUserManager is a custom class I've created to make this work with Membership, I'm guessing you'd use Identity User Manager.
Tonight i come search some help about how to call a web api hosted in IIS.
Everything work well in local from visual studio to iis express. But strangely, after publish on my IIS server. I always get 401 unauthorized :'(
Here is the code i use and the settings from my IIS server. I will be very grateful if somebody can help me.
Thank you
**
The controller and the function i try to call (with basic authentication attribute)
**
[HttpGet]
[ActionName("Get_UserID")]
[IdentityBasicAuthentication]
[Authorize]
public HttpResponseMessage Get_UserID(string userName)
{
HttpResponseMessage res = new HttpResponseMessage(HttpStatusCode.Created);
try
{
var user = Membership.GetUser(userName, false);
if (user != null)
{
res = Request.CreateResponse(HttpStatusCode.OK, (Guid)user.ProviderUserKey);
}
else
{
res = Request.CreateResponse(HttpStatusCode.ExpectationFailed);
res.Content = new StringContent("Error");
res.ReasonPhrase = "UserName not find in the database";
}
}
catch (Exception exc)
{
//Set the response message as an exception
res = Request.CreateResponse(HttpStatusCode.InternalServerError);
res.Content = new StringContent("Exception");
res.ReasonPhrase = exc.Message;
}
return res;
}
**
Client side - How i call the web api and build my httpClient
**
public static async Task<HttpResponseMessage> RequestStart(string requestUrl, string webApiUrlBase = Globals.WebApi_Url, bool IsAuthenticateMemberRequest = false)
{
if (webApiUrlBase == null)
{
webApiUrlBase = Globals.WebApi_Url;
}
var response = new HttpResponseMessage(HttpStatusCode.Created);
using (var client = new HttpClient())
{
if (IsAuthenticateMemberRequest)
{
string strToEncode = ApplicationData.Current.LocalSettings.Values["userName"].ToString() + ":" + ApplicationData.Current.LocalSettings.Values["password"].ToString();
var authenticationBytes = Encoding.ASCII.GetBytes(strToEncode);
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(authenticationBytes));
}
client.BaseAddress = new Uri(Globals.WebApi_Url);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
response = await client.GetAsync(requestUrl);
}
return response;
}
**
IIS Configuration (appPool => NetworkServices - integrate)
**
**
Fiddler Debug
**
Finally after search many times , many houres by myself. I find the solution. We should never enable Basic Authentication....
I know it's weird ^^ But if you want to use your custom basic authentication. Just disabled the Basic Authentication on IIS and everything goes well.
we have written a WinRT App connected to a Sharepoint 2013.
We are able to authenticate and login to the sharepoint, but we have problems with the logout 'process'. Login is implemented as follows:
We are setting up a HttpClient with the corresponding user credentials and domain information. The configuration is wrapped in the HttpClientConfig class an delivered to a the HttpClientService which holds the HttpClient object.
After that we retrieve the formdigestValue from the sharepoint and use the token in the X-RequestDigest Header in every request. If the token times out we retrieve a new one.
Here is some code how we implemented the above mentioned authentication.
public async Task Inialize()
{
var httpConfig = new HttpClientConfig();
httpConfig.Headers.Add("Accept", "application/json;odata=verbose");
httpConfig.Headers.Add("User-Agent", _userAgent);
httpConfig.DefaultTimeout = Statics.DEFAULT_NETWORK_TIMEOUT_SECONDS;
httpConfig.PreAuthenticate = true;
httpConfig.NetworkCredentials = new NetworkCredential(username, password, _domain);
_httpClientService.ResetCookies();
_httpClientService.ConfigureHttpClient(httpConfig);
}
The ConfigureHttpClient method disposes an old HttpClient instance and creates a new HttpClient instance, like this:
public void ConfigureHttpClient(HttpClientConfig config, bool disposeCurrent = true)
{
_config = config;
if (disposeCurrent)
{
DisposeHttpClient();
}
_httpClient = CreateHttpClient(config);
if (disposeCurrent)
{
//make sure remove old httpclient and httpclienthandler instances after they are not hold anywhere else
GC.Collect();
}
_httpClientDisposed = false;
}
public HttpClient CreateHttpClient(HttpClientConfig config)
{
_httpClientHandler = _httpClientFactoryService.CreateHttpClientHandler();
_httpClientHandler.CookieContainer = _cookieContainer;
_httpClientHandler.UseCookies = true;
_httpClientHandler.AllowAutoRedirect = config.AllowAutoRedirect;
_httpClientHandler.PreAuthenticate = config.PreAuthenticate;
if (config.NetworkCredentials != null)
{
_httpClientHandler.Credentials = config.NetworkCredentials;
}
var client = _httpClientFactoryService.CreateHttpClient(_httpClientHandler, true);
client.Timeout = TimeSpan.FromSeconds(config.DefaultTimeout);
if (config.UseGzipCompression)
{
if (_httpClientHandler.SupportsAutomaticDecompression)
{
_httpClientHandler.AutomaticDecompression = DecompressionMethods.GZip;
client.DefaultRequestHeaders.AcceptEncoding.Add(StringWithQualityHeaderValue.Parse("gzip"));
}
}
return client;
}
public void DisposeHttpClient()
{
var client = _httpClient;
_httpClientDisposed = true; //set flag before disposing is done to be able to react correctly!
if (client != null)
{
client.Dispose();
}
var handler = _httpClientHandler;
if (handler != null)
{
handler.Dispose();
}
GC.Collect();
}
public async Task<object> InitNewSharepointSession(bool useCookies = true)
{
var config = _httpClientService.CurrentClientConfig;
config.UseCookies = useCookies;
var res = await getRequestDigestAsync();
if (res.IsSuccess)
{
SharepointContextInformation = res.Response;
if (config.Headers.ContainsKey("X-RequestDigest"))
{
config.Headers.Remove("X-RequestDigest");
}
config.Headers.Add("X-RequestDigest", SharepointContextInformation.FormDigestValue);
return new DataServiceResponse<bool>(true);
}
else
{
return new DataServiceResponse<bool>(res.Error);
}
}
The ResetCookies method only disposes the old cookies list:
public void ResetCookies()
{
_cookieContainer = new CookieContainer();
}
As you can see we used some GC.Collect() calls which shows a bit our helplessness according the logout stuff.
For logout, we just dispose our httpclient.
But for some reason, if we login with another user, we sometimes get the data of the previous user which is a pretty high rated bug for us.
Everything works nice if we restart the app, but if we only dispose the current users httpClient we may run in this failure having access with the wrong credential/user context of the previous user.
Another thing I watched is the behaviour after a password change. The old password remains and is valid until the app has been restarted.
So I would be very thankful for some hints or suggestions of a sharepoint REST specialist on how to solve this issue.
I guess you are creating a Universal app for Windows 10. In that case, there is no other option than restarting the app, see this answer.
HTTP credentials are not the same as cookies, so resetting the cookies will not help.
However, if you are using System.Net.Http.HttpClient in a Windows 8/8.1 project (no Universal project), disposing the HttpClient should work.
Example with Windows 8/8.1 template. Do NOT use with Universal template.
private async void Foo()
{
// Succeeds, correct username and password.
await Foo("foo", "bar");
// Fails, wrong username and passord.
await Foo("fizz", "buzz");
}
private async Task Foo(string user, string password)
{
Uri uri = new Uri("http://heyhttp.org/?basic=1&user=foo&password=bar");
HttpClientHandler handler = new HttpClientHandler();
handler.Credentials = new System.Net.NetworkCredential(user, password);
HttpClient client = new HttpClient(handler);
Debug.WriteLine(await client.GetAsync(uri));
}
I have implemented a DNN module that works fine. Now my employer wants me to expose some of the features as web services.I want to keep everything clean and simple so I decided to add an asmx web service to my module.
The web service itself was working fine but when I tried to add a HttpModule to handle request authentication everything was messed up.
I have an event handler for AuthenticateRequest as follow :
private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
{
var app = sender as HttpApplication;
if (app == null || !(app.Request.Url.ToString().EndsWith("WebServices.asmx"))) return;
var request = app.Request;
var authHeader = request.Headers["Authorization"];
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)
{
AuthenticateUser(authHeaderVal.Parameter);
}
}
else
{
HttpContext.Current.Response.StatusCode = 401;
}
}
and here's the client side :
var service = new localhost.WebServices();
var credentialsCache = new CredentialCache
{
{new Uri(service.Url), "Basic", new NetworkCredential("user", "password")}
};
service.Credentials = credentialsCache;
Console.WriteLine(service.HelloWorld());
The problem is no Authorization header is added to my request this way. I searched and I found out that there was a round trip here that the header was not added for the first time and if response had a basic authorization header ,another request was sent with the header. Thus I added the else part :
else
{
HttpContext.Current.Response.StatusCode = 401;
}
and an EndRequest handler :
private static void OnApplicationEndRequest(object sender, EventArgs e)
{
var response = HttpContext.Current.Response;
if (response.StatusCode == 401)
{
response.Headers.Add("WWW-Authenticate",
string.Format("Basic realm=\"{0}\"", Realm));
}
}
Now every time I send a request I am redirected to login page !!!(I know that this happens because there is another FormAuthentication HttpModule that redirects the request to login page if StatusCode is 401)
Please tell me what am I doing wrong ?(is there a way to prevent the other HttpModule to interfere with my response ? )
UPDATE
I set PreAuthenticate property to true but still no authorization header is sent
I am trying to get the grip on the Facebook SDK and at the same time transitioning from ASP.NET forms to MVC (finally). So please bear with me ..
I have created two controller actions:
FBLogon is execetued when the user clicks on the FB login button on the form.
He is then redirected to the FB login page.
Afterwards he gets sent back to the FBAuthorize page, which is supposed to parse the returned url for the access token. I get something like:
http://localhost:5000/account/FBAuthorize#access_token=199143326771791|827213759889396d5408fee6-100001815992604|BmYchAOMqSoZ2L0TYgCrtpoKP3M&expires_in=0
The problem I see, is that as the access_token is passed behind a #, asp.net cannot parse it on the server. Am I doing something fundamentaly wrong?
Code follows:
public ActionResult FBLogon()
{
var settings = ConfigurationManager.GetSection("facebookSettings");
IFacebookApplication current = null;
if (settings != null)
{
current = settings as IFacebookApplication;
if (current.AppId == "{app id}" || current.AppSecret == "{app secret}")
{
return View();
}
}
string[] extendedPermissions = new[] { "publish_stream", "offline_access" };
var oauth = new FacebookOAuthClient { ClientId = current.AppId, RedirectUri = new Uri("http://localhost:5000/account/FBAuthorize") };
var parameters = new Dictionary<string, object>
{
{ "response_type", "token" },
{ "display", "page" }
};
if (extendedPermissions != null && extendedPermissions.Length > 0)
{
var scope = new StringBuilder();
scope.Append(string.Join(",", extendedPermissions));
parameters["scope"] = scope.ToString();
}
var loginUrl = oauth.GetLoginUrl(parameters);
return Redirect(loginUrl.ToString());
}
public ActionResult FBAuthorize()
{
FacebookOAuthResult result;
if (FacebookOAuthResult.TryParse(Request.Url, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
Ok. The facebook docs say it quite clearly:
Because the access token is passed in
an URI fragment, only client-side code
(such as JavaScript executing in the
browser or desktop code hosting a web
control) can retrieve the token. App
authentication is handled by verifying
that the redirect_uri is in the same
domain as the Site URL configured in
the Developer App
from http://developers.facebook.com/docs/authentication/ ---> Client-side Flow Section.
So I'm sending the token back to my server to complete the authentication..
Update:
The sending back to the server I do using Javascript something like this:
var appId = "<%: Facebook.FacebookContext.Current.AppId %>";
if (window.location.hash.length > 0) {
accessToken = window.location.hash.substring(1);
var url = window.location.href.replace(/#/, '?');
window.location = url;
}
On the server then I have the following action. Not very nice but it works..
public ActionResult FBAuthorize()
{
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
/// hack to make FacebookOuthResult accept the token..
url = url.Replace("FBAuthorize?", "FBAuthorize#");
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string[] extendedPermissions = new[] { "user_about_me", "offline_access" };
var fb = new FacebookClient(result.AccessToken);
dynamic resultGet = fb.Get("/me");
var name = resultGet.name;
RegisterModel rm = new Models.RegisterModel();
rm.UserName = name;
rm.Password = "something";
rm.Email = "somethig";
rm.ConfirmPassword = "23213";
//Label1.Text = name;
//Response.Write(name);
//return RedirectToAction("register", "Account", rm);
ViewData["Register"] = rm;
return RedirectToAction("Register");
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
I found this post http://facebooksdk.codeplex.com/discussions/244568 on codeplex. I think this is what you need.
Note that instead of using the client-side flow, you need to use the server-side flow.
This is what you should do
Create a login link for server-side flow. After Authorization, facebook will return an url containing a code instead of a access token.
Then you request for a token from facebook using the code. this is my example
public ActionResult FBAuthorize()
{
FacebookOAuthClient cl = new FacebookOAuthClient(FacebookContext.Current);
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
// verify that there is a code in the url
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string code = result.Code;
// this line is necessary till they fix a bug *see details below
cl.RedirectUri = new UriBuilder("http://localhost:5000/account/FBAuthorize").Uri;
var parameters = new Dictionary<string, object>();
//parameters.Add("permissions", "offline_access");
Dictionary<String, Object> dict = (Dictionary<String, Object>)cl.ExchangeCodeForAccessToken(code, new Dictionary<string, object> { { "redirect_uri", "http://localhost:5000/account/FBAuthorize" } });
Object Token = dict.Values.ElementAt(0);
TempData["accessToken"] = Token.ToString();
return RedirectToAction ("ShowUser");
}
else
{
var errorDescription = result.ErrorDescription;
}
}
else
{
// TODO: handle error
}
return View();
}
*There is bug when using IIS in localhost, see the original post for details (the redirect uri when asking for the token must be the same as the one used asking for the code)
It is highly recommended to use IIS and not visual studio web server. There are many things that wont work in visual studio web server.
I am in the same spot you are at the moment.
We never get the Request.QueryString populated becasue of the "fragment" or # in the url.
Love to know if you solved this and how.
It does not look like the FacebookOAuthResult class was written to be used in web applications of any sort.
you can change the response type in you scope paramas to be "code" then it will send back a code in the querystring in which you can swap for a token.