Perhaps I'm missing something, or perhaps .NET is missing something (preferably the former)
When building an application (not exclusively ASP.NET, but such is my situation; specifically an ASP.NET hosted WCF DS) it seems there's no native way to create a NetworkCredential object from an HttpRequest, or any similar request/header container,.
Do we always have to roll our own, or is there some magic tucked away in System.Net.* or System.Web.* with a signature like:
NetworkCredential GetAuthorization(HttpRequest request);
It's trivial I know, but I would assume something standard to the HTTP architecture would be included in something that is otherwise so encompassing (.NET)
So, home-brew string manipulation, or magic method hiding somewhere?
I don't think there's anything built-in; it would be of limited use, since most clients use Kerberos or Digest authentication instead.
However, it's fairly simple to roll your own:
static NetworkCredential ParseBasicAuthorizationHeader(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
return null;
}
if (!value.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase))
{
return null;
}
byte[] data = Convert.FromBase64String(value.Substring(6));
value = Encoding.GetEncoding("ISO-8859-1").GetString(data);
int index = value.IndexOf(':');
if (index == -1 || index == 0 || index == value.Length - 1)
{
return null;
}
return new NetworkCredential(
value.Substring(0, index), // Username
value.Substring(index + 1)); // Password
}
Bear in mind that, like all other HTTP headers, the Authorization header is completely controlled by the client, and should therefore be treated as untrusted user input.
Related
I'm running into a problem sending massive requests to a .NET Core web service. I'm using a SemaphoreSlim to limit the number of simultaneous requests. When I get a 10061 error (the web service has refused the connection), I want to dial back the number of simultaneous requests. My idea at the moment is to de-reference the SemaphoreSlim and create another:
await this.semaphoreSlim.WaitAsync().ConfigureAwait(false);
counter++;
Uri uri = new Uri($"{api}/{keyProperty}", UriKind.Relative);
string rowVersion = string.Empty;
try
{
HttpResponseMessage getResponse = await this.httpClient.GetAsync(uri).ConfigureAwait(false);
if (getResponse.IsSuccessStatusCode)
{
using (HttpContent httpContent = getResponse.Content)
{
JObject currentObject = JObject.Parse(await httpContent.ReadAsStringAsync().ConfigureAwait(false));
rowVersion = currentObject.Value<string>("rowVersion");
}
}
}
catch (HttpRequestException httpRequestException)
{
SocketException socketException = httpRequestException.InnerException as SocketException;
if (socketException != null && socketException.ErrorCode == PutHandler.ConnectionRefused)
{
this.semaphoreSlim = new SemaphoreSlim(counter * 90 / 100, counter * 90 / 100);
}
}
}
finally
{
this.semaphoreSlim.Release();
}
If I do this, what will happen to the other tasks that are waiting on the Semaphore that I just de-referenced? My guess is that nothing will happen until the object is garbage collected and disposed.
A SemaphoreSlim (just like any other object in .NET) will exist as long as there are references to it.
However, there is a bug in your code: the SemaphoreSlim being released is this.semaphoreSlim, and if this.semaphoreSlim is changed between being acquired and being released, then the code will release a different semaphore than the one that was acquired. To avoid this problem, copy this.semaphoreSlim into a local variable at the beginning of your method, and acquire and release that local variable.
More broadly, there's a difficult in the attempted solution. If you start 1000 tasks, they will all reference the old semaphore and ignore the updated this.sempahoreSlim. So you'd need a separate solution. For example, you could define a disposable "token" which is permission to call the API. Then have an asynchronous collection of these tokens (e.g., a Channel). This gives you full control over how many tokens are released at once.
I've spent days trying to find examples, but none appear to answer what it is I am looking for. Most other examples are for later versions of Web API.
I am currently consuming a REST web service, that uses basic authentication. I want to emulate this in mine. The path is /api/authenticate
I pass a header of Authorization Basic [base 64 username / password]. The response I am currently seeing is a response code of 200, and a json response that has the session token (among other things).
For future calls, I then pass the session token in an X header back to the server, and that then passes all of my calls.
I'm trying to write a web api that works in a very similar way.
I've done very little with web api, so am a bit lost. Here is what I have so far...
public string[] Post([FromBody]string value)
{
if (Request.Headers.Authorization == null)
{
System.Web.HttpContext.Current.Response.AddHeader("WWW-Authenticate", "Basic");
System.Web.HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
else
{
// Check for valid users
string authToken = Request.Headers.Authorization.Parameter;
string decodedToken = Encoding.UTF8.GetString(Convert.FromBase64String(authToken));
string username = decodedToken.Substring(0, decodedToken.IndexOf(":"));
string password = decodedToken.Substring(decodedToken.IndexOf(":") + 1);
if (username == "MyApiUserName" && password == "MyApiPassword")
{
// The line below doesn't work yet.
//WebSecurity.Login(model.Username, model.Password, persistCookie: true);
System.Web.HttpContext.Current.Response.AddHeader("Test", "Something");
System.Web.HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.OK;
Pair[0] = username;
Pair[1] = password;
return Pair;
}
else
{
System.Web.HttpContext.Current.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
}
}
return null;
}
What I need is to respond once authenticated with a session token in the json response and I also need to know how to use that session token in my other API calls.
Asp.net Api has its own authentication methods, so you don't need to implement them by yourself. this is why all examples are for latest versions of Web API.
if you want return json responce try to use JsonResult instead of string[ ]
see this https://msdn.microsoft.com/en-us/library/system.web.mvc.jsonresult(v=vs.118).aspx
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this question
I am wanting to implement two websites that need to communicate with each other. (Since one of the sites has a separate deployment for each customer, and is spread across many servers, sharing a database or communicating privately is not an option.) So I've been looking into RESTful APIs.
Unfortunately, I'm running into a lot of information that I'm not familiar with. One issue is security. We don't need anything fancy--we're not a bank or anything. I think we can just get away with HTTPS and a basic username and password.
Questions:
How would I pass the username and password to the API? Would they just be passed as bare arguments in the URL?
Does .NET provide any mechanism for authorizing such username and passwords, or do I just manually see if the password is in our database on each and every request? (I would hash for security.)
How would I pass the username and password to the API? Would they just
be passed as bare arguments in the URL?
It can be either in the URL or in the header. If you are using HTTPS, it will all be encrypted so it will not be bare. Please see this for more details.
Does .NET provide any mechanism for authorizing such username and
passwords, or do I just manually see if the password is in our
database on each and every request? (I would hash for security.)
No you do not need to check the database on every request. You can check once, create a token with an expiry and the client can keep sending you the token. This way you do not have to keep checking the database every single time.
Please see see this answer for some helpful information.
I think basic authentication with base64 encoding will be sufficient. If not you can always change it. Here are the different ways to apply it to your backend code:
To apply an authentication filter to a controller, decorate the controller class with the filter attribute. The following code sets the [IdentityBasicAuthentication] filter on a controller class, which enables Basic Authentication for all of the controller's actions.
[IdentityBasicAuthentication] // Enable Basic authentication for this controller.
[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
public IHttpActionResult Get() { . . . }
public IHttpActionResult Post() { . . . }
}
To apply the filter to one action, decorate the action with the filter. The following code sets the [IdentityBasicAuthentication] filter on the controller's Post method.
[Authorize] // Require authenticated requests.
public class HomeController : ApiController
{
public IHttpActionResult Get() { . . . }
[IdentityBasicAuthentication] // Enable Basic authentication for this action.
public IHttpActionResult Post() { . . . }
}
To apply the filter to all Web API controllers, add it to GlobalConfiguration.Filters.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new IdentityBasicAuthenticationAttribute());
// Other configuration code not shown...
}
}
Finally here is an example of the implementation, you may change it as you need:
using System;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security.Principal;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;
using BasicAuthentication.Results;
namespace BasicAuthentication.Filters
{
public abstract class BasicAuthenticationAttribute : Attribute, IAuthenticationFilter
{
public string Realm { get; set; }
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
if (authorization == null)
{
// No authentication was attempted (for this authentication method).
// Do not set either Principal (which would indicate success) or ErrorResult (indicating an error).
return;
}
if (authorization.Scheme != "Basic")
{
// No authentication was attempted (for this authentication method).
// Do not set either Principal (which would indicate success) or ErrorResult (indicating an error).
return;
}
if (String.IsNullOrEmpty(authorization.Parameter))
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
return;
}
Tuple<string, string> userNameAndPasword = ExtractUserNameAndPassword(authorization.Parameter);
if (userNameAndPasword == null)
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Invalid credentials", request);
return;
}
string userName = userNameAndPasword.Item1;
string password = userNameAndPasword.Item2;
IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
if (principal == null)
{
// Authentication was attempted but failed. Set ErrorResult to indicate an error.
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
}
else
{
// Authentication was attempted and succeeded. Set Principal to the authenticated user.
context.Principal = principal;
}
}
protected abstract Task<IPrincipal> AuthenticateAsync(string userName, string password,
CancellationToken cancellationToken);
private static Tuple<string, string> ExtractUserNameAndPassword(string authorizationParameter)
{
byte[] credentialBytes;
try
{
credentialBytes = Convert.FromBase64String(authorizationParameter);
}
catch (FormatException)
{
return null;
}
// The currently approved HTTP 1.1 specification says characters here are ISO-8859-1.
// However, the current draft updated specification for HTTP 1.1 indicates this encoding is infrequently
// used in practice and defines behavior only for ASCII.
Encoding encoding = Encoding.ASCII;
// Make a writable copy of the encoding to enable setting a decoder fallback.
encoding = (Encoding)encoding.Clone();
// Fail on invalid bytes rather than silently replacing and continuing.
encoding.DecoderFallback = DecoderFallback.ExceptionFallback;
string decodedCredentials;
try
{
decodedCredentials = encoding.GetString(credentialBytes);
}
catch (DecoderFallbackException)
{
return null;
}
if (String.IsNullOrEmpty(decodedCredentials))
{
return null;
}
int colonIndex = decodedCredentials.IndexOf(':');
if (colonIndex == -1)
{
return null;
}
string userName = decodedCredentials.Substring(0, colonIndex);
string password = decodedCredentials.Substring(colonIndex + 1);
return new Tuple<string, string>(userName, password);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
Challenge(context);
return Task.FromResult(0);
}
private void Challenge(HttpAuthenticationChallengeContext context)
{
string parameter;
if (String.IsNullOrEmpty(Realm))
{
parameter = null;
}
else
{
// A correct implementation should verify that Realm does not contain a quote character unless properly
// escaped (precededed by a backslash that is not itself escaped).
parameter = "realm=\"" + Realm + "\"";
}
context.ChallengeWith("Basic", parameter);
}
public virtual bool AllowMultiple
{
get { return false; }
}
}
}
If you still want to read more then here is a great article which goes into details. I have copied the above code from this article. It has lots of great information.
If you control or exert significant influence on both sides of the connection, client ssl certificates is a really strong and powerful way of doing this. It's attractive to me in this case because it only requires distributing a trusted CA certificate which can be done before the client certificates are created. It's far more secure than any username and password could ever be ( because the password doesn't need to go across the wire).
Any other solution with authentication I can think of, you're going to have to have some sort of data source to verify the credentials. But x509 solves this problem for you. We've done it at work between applications and other than managing the certificates it works really, really well. And it's basically the most secure thing available.
I don't know much about .net in general, but ( not to lmgtfy ) https://support.microsoft.com/en-us/kb/315588 seems like the step by step format you are looking for.
Just a thought, and it really depends on what you meant by "username/password". If this means "authorization"/access to some API call and you want to ensure that the client is "authorized" to make a call to your API (only apps A, B can make api calls to API - and it seems this is what you're looking for based on your comment above):
As in the comment above, authorization header, using JWT. There is an great/easy JWT library in Nuget
it's pretty much something like a "shared secret" used to sign a "payload" (the JWT)
the "sender" will build the JWT and sign it (and add to header or whatever protocol you want - it can be body if prefer it over headers)
the "receiver" will verify the JWT sent
this includes handling/mitigating "replays" - the JWT spec has an "expire" field (exp) that you can have the library validate as well (or not, it's up to you)
The project site is on Github with samples.
Hth.
We currently have two different servers on same domain. But one server resolves
#Url.Content("~/api/User")'
as
http://domain.com/virtualdirectory/api/User
where as other server doesnt resolve it absolutely; rather it resolves it relatively like
api/user
The code base is same and we are using MVC4. I am not sure as to where we went wrong or if there is any IIS/DNS settings that need to be done in order to get this fixed.
All help is appreciated; thanks :)
This is related with the IIS Rewriting module in your IIS web server that return the path to http://domain.com/virtualdirectory/api/User
Take a look on the part of source code of #Url.Content below:
private static string GenerateClientUrlInternal(HttpContextBase httpContext, string contentPath)
{
if (String.IsNullOrEmpty(contentPath))
{
return contentPath;
}
// can't call VirtualPathUtility.IsAppRelative since it throws on some inputs
bool isAppRelative = contentPath[0] == '~';
if (isAppRelative)
{
string absoluteContentPath = VirtualPathUtility.ToAbsolute(contentPath, httpContext.Request.ApplicationPath);
return GenerateClientUrlInternal(httpContext, absoluteContentPath);
}
// we only want to manipulate the path if URL rewriting is active for this request, else we risk breaking the generated URL
bool wasRequestRewritten = _urlRewriterHelper.WasRequestRewritten(httpContext);
if (!wasRequestRewritten)
{
return contentPath;
}
// Since the rawUrl represents what the user sees in his browser, it is what we want to use as the base
// of our absolute paths. For example, consider mysite.example.com/foo, which is internally
// rewritten to content.example.com/mysite/foo. When we want to generate a link to ~/bar, we want to
// base it from / instead of /foo, otherwise the user ends up seeing mysite.example.com/foo/bar,
// which is incorrect.
string relativeUrlToDestination = MakeRelative(httpContext.Request.Path, contentPath);
string absoluteUrlToDestination = MakeAbsolute(httpContext.Request.RawUrl, relativeUrlToDestination);
return absoluteUrlToDestination;
}
Use the codes below to check whether your web servers are having the URL rewritten:
bool requestWasRewritten = (httpWorkerRequest != null && httpWorkerRequest.GetServerVariable("IIS_WasUrlRewritten") != null);
And Also:
private volatile bool _urlRewriterIsTurnedOnCalculated = false;
private bool _urlRewriterIsTurnedOnValue;
private object _lockObject = new object();
private bool IsUrlRewriterTurnedOn(HttpContextBase httpContext)
{
// Need to do double-check locking because a single instance of this class is shared in the entire app domain (see PathHelpers)
if (!_urlRewriterIsTurnedOnCalculated)
{
lock (_lockObject)
{
if (!_urlRewriterIsTurnedOnCalculated)
{
HttpWorkerRequest httpWorkerRequest = (HttpWorkerRequest)httpContext.GetService(typeof(HttpWorkerRequest));
//bool urlRewriterIsEnabled = (httpWorkerRequest != null && httpWorkerRequest.GetServerVariable(UrlRewriterEnabledServerVar) != null);
bool urlRewriterIsEnabled = (httpWorkerRequest != null && httpWorkerRequest.GetServerVariable("IIS_UrlRewriteModule") != null);
_urlRewriterIsTurnedOnValue = urlRewriterIsEnabled;
_urlRewriterIsTurnedOnCalculated = true;
}
}
}
return _urlRewriterIsTurnedOnValue;
}
In summary, If both requestWasRewritten and IsUrlRewriterTurnedOn
return true, that means one of your web server has IIS Rewrite Module
turned on and running while the other one doesn't have.
For more details on ASP.NET MVC source codes, please refer to this link:
http://aspnetwebstack.codeplex.com/
Hope it helps!
I'm working with a programmatically configurated WCF Client (System.ServiceModel.ClientBase). This WCF Client is configured using a CustomBinding, which has a TextMessageEncodingBindingElement by default.
Now when I try to switch to Mtom encoding, I change the Client's Endpoint.Binding property, which works fine. The Endpoint.Binding property show's it has changed.
Unfortunately when I execute one of the methods the WCF service exposes, it still uses TextMessageEncoding and I can't figure out why.
I've got it working though, by constructing a new ClientBase and passing the new EndPointBinding in the constructor:
socialProxy = new SocialProxyClient(SocialProxyClientSettings.SocialProxyMTomEndPointBinding, new EndpointAddress(SocialProxyClientSettings.SocialProxyEndPointAddress));
But when I try this it doesn't work:
socialProxy.Endpoint.Binding = SocialProxyClientSettings.SocialProxyMTomEndPointBinding;
These are my definitions for the EndPointBindings:
public static TextMessageEncodingBindingElement TextMessageEncodingBindingElement
{
get
{
if (_textMessageEncodingBindingElement == null)
{
_textMessageEncodingBindingElement = new TextMessageEncodingBindingElement() { MessageVersion = MessageVersion.Soap11 };
_textMessageEncodingBindingElement.ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxDepth = 32,
MaxStringContentLength = 5242880,
MaxArrayLength = 204800000,
MaxBytesPerRead = 5242880,
MaxNameTableCharCount = 5242880
};
}
return _textMessageEncodingBindingElement;
}
}
public static MtomMessageEncodingBindingElement MtomMessageEncodingBindingElement
{
get
{
if (_mtomMessageEncodingBindingElement == null)
{
_mtomMessageEncodingBindingElement = new MtomMessageEncodingBindingElement();
_mtomMessageEncodingBindingElement.MaxReadPoolSize = TextMessageEncodingBindingElement.MaxReadPoolSize;
_mtomMessageEncodingBindingElement.MaxWritePoolSize = TextMessageEncodingBindingElement.MaxWritePoolSize;
_mtomMessageEncodingBindingElement.MessageVersion = TextMessageEncodingBindingElement.MessageVersion;
_mtomMessageEncodingBindingElement.ReaderQuotas.MaxDepth = TextMessageEncodingBindingElement.ReaderQuotas.MaxDepth;
_mtomMessageEncodingBindingElement.ReaderQuotas.MaxStringContentLength = TextMessageEncodingBindingElement.ReaderQuotas.MaxStringContentLength;
_mtomMessageEncodingBindingElement.ReaderQuotas.MaxArrayLength = TextMessageEncodingBindingElement.ReaderQuotas.MaxArrayLength;
_mtomMessageEncodingBindingElement.ReaderQuotas.MaxBytesPerRead = TextMessageEncodingBindingElement.ReaderQuotas.MaxBytesPerRead;
_mtomMessageEncodingBindingElement.ReaderQuotas.MaxNameTableCharCount = TextMessageEncodingBindingElement.ReaderQuotas.MaxNameTableCharCount;
}
return _mtomMessageEncodingBindingElement;
}
}
Can someone explain why changing the Endpoint.Binding programmatically doesn't work?
I believe that during construction of the ClientBase, the original Binding is used to create some helper objects. Changing the binding later does not change those helper objects.
To make any adjustments after construction, you likely need a custom Binding Behavior that you can tweak the internals of the Binding as you need. Use that in the construction so all helper objects are prepared for your later changes. As usual, all you want is one simple behavior change, but you will need to also write the ancillary helper classes to support your one behavior change.
See the SO thread: ONVIF Authentication in .NET 4.0 with Visual Studio 2010
For a discussion of CustomBinding issues.
See the blog post: Supporting the WS-I Basic Profile Password Digest in a WCF Client Proxy
For an example of a custom Behavior that lets you change the Username Token on the fly.
Perhaps something similar can be done to let you control the local endpoint binding on the fly.
UPDATE: More reading here in StackOverflow, and pages it links to and I believe i have found the answer you are looking for.
For PasswordDigestBehavior:
see: ONVIF Authentication in .NET 4.0 with Visual Studios 2010
and: http://benpowell.org/supporting-the-ws-i-basic-profile-password-digest-in-a-wcf-client-proxy/
For local NIC binding:
see: Specify the outgoing IP address to use with WCF client
// ASSUMPTIONS:
// 1: DeviceClient is generated by svcutil from your WSDL.
// 1.1: DeviceClient is derived from
// System.ServiceModel.ClientBase<Your.Wsdl.Device>
// 2: serviceAddress is the Uri provided for your service.
//
private static DeviceClient CreateDeviceClient(IPAddress nicAddress,
Uri serviceAddress,
String username,
String password)
{
if (null == serviceAddress)
throw new ArgumentNullException("serviceAddress");
//////////////////////////////////////////////////////////////////////////////
// I didn't know how to put a variable set of credentials into a static
// app.config file.
// But I found this article that talks about how to set up the right kind
// of binding on the fly.
// I also found the implementation of PasswordDigestBehavior to get it all to work.
//
// from: https://stackoverflow.com/questions/5638247/onvif-authentication-in-net-4-0-with-visual-studios-2010
// see: http://benpowell.org/supporting-the-ws-i-basic-profile-password-digest-in-a-wcf-client-proxy/
//
EndpointAddress serviceEndpointAddress = new EndpointAddress(serviceAddress);
HttpTransportBindingElement httpBinding = new HttpTransportBindingElement();
if (!String.IsNullOrEmpty(username))
{
httpBinding.AuthenticationScheme = AuthenticationSchemes.Digest;
}
else
{
httpBinding.AuthenticationScheme = AuthenticationSchemes.Anonymous;
}
var messageElement = new TextMessageEncodingBindingElement();
messageElement.MessageVersion =
MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None);
CustomBinding bind = new CustomBinding(messageElement, httpBinding);
////////////////////////////////////////////////////////////////////////////////
// from: https://stackoverflow.com/questions/3249846/specify-the-outgoing-ip-address-to-use-with-wcf-client
// Adjust the serviceEndpointAddress to bind to the local NIC, if at all possible.
//
ServicePoint sPoint = ServicePointManager.FindServicePoint(serviceAddress);
sPoint.BindIPEndPointDelegate = delegate(
System.Net.ServicePoint servicePoint,
System.Net.IPEndPoint remoteEndPoint,
int retryCount)
{
// if we know our NIC local address, use it
//
if ((null != nicAddress)
&& (nicAddress.AddressFamily == remoteEndPoint.AddressFamily))
{
return new System.Net.IPEndPoint(nicAddress, 0);
}
else if (System.Net.Sockets.AddressFamily.InterNetworkV6 == remoteEndPoint.AddressFamily)
{
return new System.Net.IPEndPoint(System.Net.IPAddress.IPv6Any, 0);
}
else // if (System.Net.Sockets.AddressFamily.InterNetwork == remoteEndPoint.AddressFamily)
{
return new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
}
};
/////////////////////////////////////////////////////////////////////////////
DeviceClient client = new DeviceClient(bind, serviceEndpointAddress);
// Add our custom behavior
// - this requires the Microsoft WSE 3.0 SDK file: Microsoft.Web.Services3.dll
//
PasswordDigestBehavior behavior = new PasswordDigestBehavior(username, password);
client.Endpoint.Behaviors.Add(behavior);
return client;
}