Windows Username in WCF 4 Rest - wcf

I'm having difficulty with Windows authentication in a WCF REST app.
Using VS2010, I chose New Project>WCF REST Service Application.
I modified the web.config to assure windows authentication and deny anonymous users.
<system.web>
<compilation debug="true" targetFramework="4.0" />
<authentication mode="Windows" />
<authorization>
<deny users="?"/>
</authorization>
</system.web>
I altered Service1.cs to return the username in the response:
[WebGet(UriTemplate = "",
RequestFormat= WebMessageFormat.Json,
ResponseFormat = WebMessageFormat.Json,
BodyStyle=WebMessageBodyStyle.Bare)]
public List<SampleItem> GetCollection()
{
// TODO: Replace the current implementation to return a collection of SampleItem instances
WebOperationContext.Current.OutgoingResponse.ContentType = "text/plain";
string fullID = ServiceSecurityContext.Current.WindowsIdentity.Name;
return new List<SampleItem>() { new SampleItem() { Id = 1, StringValue = "Hello " + fullID } };
}
I test it successfully running on local machine, then I publish it to IIS7 on Windows Server 2008. In IIS Manager, I enable Windows Authentication on the app and disable all other authentication types.
I give it its own Application Pool with integrated managed pipeline mode.
I can successfully see it in windows explorer running on the Win2008 machine (http://localhost/JobManager/Service1/)
Using IE 7 from another machine, however, it prompts me twice for username/password, I fill it in twice but get a 401 error the second time. (Unauthorized: Access is denied due to invalid credentials.)

Resolved by changing the identity used by the app pool to one that has access to directory services.

Related

How to use Windows authentication on ASP.NET Core subpath only?

I am trying to configure Windows authentication on a subroute only in my ASP.NET Core MVC app.
My problem is that when I add
services.AddAuthentication().AddNegotiate()
I get an error
The Negotiate Authentication handler cannot be used on a server that directly supports Windows Authentication.
which lead me to adding web.config as the docs explained:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<security>
<authentication>
<anonymousAuthentication enabled="false" />
<windowsAuthentication enabled="true" />
</authentication>
</security>
</system.webServer>
</location>
</configuration>
and the error goes away. However, now the Windows authentication is popping up on each request.
I tried changing the location path to .testendpoint but that then throws the original error at the base path.
So is it possible and how do I make such only /testendpoint will ask for Windows authentication and the remaining of the application will work with whatever other auth I configured in my ASP.NET Core app?
Another way using endpoint routing:
We have an application schema for the application that will be used all over the app called eavfw.
Using a custom endpoint here called login/ntlm with metadata new AuthorizeAttribute(NegotiateDefaults.AuthenticationScheme) its only allowed to be visited by a valid windows authenticated user.
Here we then create the user in our DB using its AD username.
endpoints.MapGet("/.auth/login/ntlm", async httpcontext =>
{
var loggger = httpcontext.RequestServices.GetRequiredService<ILogger<Startup>>();
var windowsAuth = await httpcontext.AuthenticateAsync(NegotiateDefaults.AuthenticationScheme);
if (!windowsAuth.Succeeded)
{
loggger.LogWarning("Not authenticated: Challening");
}
if (windowsAuth.Succeeded)
{
loggger.LogWarning("Authenticated");
var name = string.Join("\\", windowsAuth.Principal.Claims.FirstOrDefault(c => c.Type.EndsWith("name")).Value.Split("\\").Skip(1));
var context = httpcontext.RequestServices.GetRequiredService<DynamicContext>();
var users = context.Set<SystemUser>();
var user = await context.Set<SystemUser>().Where(c => c.PrincipalName == name).FirstOrDefaultAsync();
if (user == null)
{
user = new SystemUser
{
PrincipalName = name,
Name = name,
// Email = email,
};
await users.AddAsync(user);
await context.SaveChangesAsync();
}
var principal = new ClaimsPrincipal(new ClaimsIdentity(new Claim[] {
new Claim(Claims.Subject,user.Id.ToString())
}, "ntlm"))
{
};
await httpcontext.SignInAsync("ntlm",
principal, new AuthenticationProperties(
new Dictionary<string, string>
{
["schema"] = "ntlm"
}));
httpcontext.Response.Redirect("/account/login/callback");
}
}).WithMetadata(new AuthorizeAttribute(NegotiateDefaults.AuthenticationScheme));
using a auxility authentication cookie, we can now make it such that specific areas of our app that requires windows authentication, it can simply rely on Authorize("ntlm") as it automatically forward the authenticate call to check if already signin, and it as part of the signin call in the endpoint above actually sign in eavfw.external before it redirects to the general account callback page that will do some final validation before signing in eavfw from the eavfw.external cookie
services.AddAuthentication().AddCookie("ntlm", o => {
o.LoginPath = "/.auth/login/ntlm";
o.ForwardSignIn = "eavfw.external";
o.ForwardAuthenticate = "eavfw";
});
So there are a few ways to extend and use the authentication system in auth core depending on how MVC framework heavy your application is.
Just thought I'd share this tidbit of information:
First off, just because you installed Windows Authentication with Server Manager, doesn't mean it's enabled in IIS. It's NOT enabled, by default.
You have to open IIS Manager, click on your server (NOT the website - the name of the server machine hosting IIS). Then click on Authentication - you will see "Windows Authentication" is disabled. Enable it. Now it will work.
Check this is correctly set first, before making other config changes. The default project for dotNet5 and dotNet6 will work w/o any modifications if IIS is correctly configured for Windows Authentication.
In order to have a certain page/action method secured via Windows authentation, specify the corresponding authentication scheme in the action methods Authorize attribute.
[Authorize(AuthenticationSchemes = IISServerDefaults.AuthenticationScheme)]
public IActionResult UsingWindowsAuthentication()
Make sure to have Windows authentication enabled on your website.
In order to use other authentication schemes, e.g. "Individual Accounts", anonymous authentication is also enabled.
The controllers and/or action methods that must not use Windows Authentication have the default scheme specified.
For example, for an ASP.NET Core MVC project that uses the out of the box "Individual Accounts" authentication type as default authentication method, that is Identity.Application.
[Authorize(AuthenticationSchemes = "Identity.Application")]
public IActionResult Index()
See the documentation about how to set up and configure multiple authentication schemes.

HTTPPost is not allowed by production WebServer for WebAPI (aspnetcore 2.2)

I have the following code in the StartUp.ConfigureServices
services.AddCors(c=>c.AddPolicy(Konstants.CORS, d=>
d.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials()
)
);
and in the Configure method I use the following as the first line:
app.UseCors(Konstants.CORS);
On every controller for web api I use the following
[Route("api/[controller]")]
[EnableCors(Konstants.CORS)]
[ApiController, Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Consumes("application/json"), Produces("application/json")]
public class EmployeesController : ApiBaseController
{
On local machines everything runs fine. But on remote machine i.e. the production one, HTTPGet works fine but for all HTTPPOST it returns 404.
here is my web.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath=".\myapp.Web.exe" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" />
</system.webServer>
</location>
</configuration>
What could be the issue? I have access to webserver through plesk.
A few details I missed. This website has regular controllers as well as API controllers hosted in the same app. for api controller we use JWTToken for other controllers we use cookies. Website works just fine. Api controllers have all Get Requests work fine and they even fetch the data from database correctly (so at least its not a DB issue) but all api POST return 404. URL not found.
As #Anton asked, here is one of the post request
[HttpPost("Start")]
public async Task<IActionResult> StartJob([FromBody] JobActivityModel model)
{
var ret = Empty;
try
{
await Execute(new StartJob
{
JobId = model.JobId,
PersonId = UserId,
At = model.CreatedAt
});
Log.Info($"{SoligoUser} has started the job");
ret = Ok(new TimeClue
{
Message = "job has been started",
IsSuccess = true,
Time = model.CreatedAt
});
}
catch (Exception ex)
{
Log.Error(ex);
ret = Error(ex);
}
return ret;
}
You may try below things.
Install swagger and try
RDP in to server and try to run on server. This will give exact error.
Check headers of post request you tried with server.
Double check paths and payloads are correct
Follow this to host .net core in iis.
Check you are sending correct tokens to server
After much time wasted on web searches this turned out to be the solution:
Adding this line solved all the issues. Post is now working correctly and sometimes the Media Not Supported Error was encountered was also fixed.
I wish someday the compiler will tell "hey, you need to put this"

MVC5: User.Identity.IsAuthenticated always returns true

Our company has SSO site where all our applications gets redirected if the user is not authenticated. The SSO site authenticates the user using Forms authentication. and its been working for all the applications.(ASP.NET applications)
Now we have new MVC5 application created using VS 2013. I am trying to use Forms Authentication. If the user is not authenticated I want to redirect the user to login url ( SSO site). Below is my code. But when I debug, the user is always Authenticated.
IsAutheticated property is true, AuthenticationType is "Negotiate", and Identity is "Windows" ( even though in config file its "Forms")
(Note I am debugging in VS with IIS express if that make difference. Also it's MVC 5 application, is it because of OWIN. How do I know?)
<system.web>
<compilation debug="true" targetFramework="4.5" />
<authentication mode="Forms" >
<forms loginUrl="/Account/Login"></forms>
</authentication>
<authorization>
<deny users="?" />
</authorization>
public class AccountController : Controller
{
public ActionResult Login()
{
string loginUrl = AppSettings.Authentication.LoginUrl;
string failOverUrl = AppSettings.Authentication.FailoverLoginUrl;
string securityGroup = AppSettings.Authentication.SecurityGroup;
if (!User.Identity.IsAuthenticated) // IsAutheticated is always true, why?
{
var returnUrl = "someresturnurl";
MyAuthenticator.Authenticate(loginUrl, failOverUrl, returnUrl, securityGroup);
}
else
{
// Redirect the user if they are already authenticated.
return RedirectToAction("Index", "Home");
}
}
}
In ASP.NET MVC5 authentication mechanism has changed significantly.
Possibly all you authentication configuration comes from OWIN Startup class.
Here is a link where you can find how this configuration may look like
HttpListener listener = (HttpListener)app.Properties["System.Net.HttpListener"];
listener.AuthenticationSchemes = AuthenticationSchemes.IntegratedWindowsAuthentication;
This is a good topic about ASP.NET identity basics
I hope this will help.

MVC 4 Detecting Session Timeout

Unable to get the following standard code to work. Looking for a complete sample project or help on how to bug.
The following code...
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Session != null)
{
if (filterContext.HttpContext.Session.IsNewSession)
{
string cookie = filterContext.HttpContext.Request.Headers["Cookie"];
if ((cookie != null) && (cookie.IndexOf("_sessionId") >= 0))
{
filterContext.Result = newRedirectResult("~/SessionExpired/Index"); //redirect anywhere to message user UI , never hits this breakpoint
return;
}
}
}
base.OnActionExecuting(filterContext);
}
is standard for checking session expiration.
However in my case the the line (cookie != null) && (cookie.IndexOf("_sessionId") >= 0)
always returns false and thus the user is never redirected.
I am testing with a brand new wizard created MVC 4 project.
I cannot find a complete download sample. I suspect my config my be wrong.
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="1" slidingExpiration="true" name=".ASPXFORMSAUTH" />
</authentication>
<sessionState mode="InProc" timeout="1" cookieless="false"/>
Please advise why this code fails or provide a link with a complete download sample project.
.Net 4.5 | VS 2012 | local IIS Web Server , IIS Express
re: Detecting Session expiry on ASP.NET MVC
Try capitalizing the "s" in "_sessionId"
like:
sessionCookie.IndexOf("_SessionId") >= 0)
The search is case-sensitive.
Also, the linked article you are referring to uses:
sessionCookie.IndexOf("ASP.NET_SessionId") >= 0)
It's a really late answer, but it might help someone else.
If I'm not mistaken, for that approach to work, the forms authentication timeout must be set to a higher time than the session itself. That way when you're creating a new session (in the server memory), the cookie from the authentication will still be there in the user's computer to indicate that the user was recently logued.
You should try the following:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="1000" slidingExpiration="true" name=".ASPXFORMSAUTH" />
</authentication>
<sessionState mode="InProc" timeout="10" cookieless="false"/>

Sharing ASP.NET simplemembership authentication across web roles and client applications

I've been trying to figure this out for a while now, reading a lot of blogs, MSDN documentation, sample code and other stackoverflow questions and have yet to get this to work.
Here is my scenario:
I am using Windows Azure to host two web roles. One is my MVC4 web API, the other is my MVC4 web app which uses the web API. I also have a number of client applications using .NET that will access the web API.
So my main components are:
Web API
Web App
.NET Client
I want to use forms authentication that is 'hosted' in the Web App. I am using the built in simplemembership authentication mechanism and it works great. I can create and log in to accounts in the Web App.
Now I also want to use these same accounts to authenticate the Web API, both from the Web App and any .NET client apps.
I've read numerous ways to do this, the simplest appearing to be using Basic Authentication on the Web API. Currently I am working with this code as it appears to solve my exact problem: Mixing Forms Authentication, Basic Authentication, and SimpleMembership
I can't get this to work. I log in successfully to my Web App (127.0.0.1:81) and when I try to call a Web API that requires authentication (127.0.0.1:8081/api/values for example) the call fails with a 401 (Unauthorized) response. In stepping through the code, WebSecurity.IsAuthenticated returns false. WebSecurity.Initialized returns true.
I've implemented this code and am trying to call my Web API from my Web App (after logging in) with the following code:
using ( var handler = new HttpClientHandler() )
{
var cookie = FormsAuthentication.GetAuthCookie( User.Identity.Name, false );
handler.CookieContainer.Add( new Cookie( cookie.Name, cookie.Value, cookie.Path, cookie.Domain ) );
using ( var client = new HttpClient() )
{
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
// "Basic",
// Convert.ToBase64String( System.Text.ASCIIEncoding.ASCII.GetBytes(
// string.Format( "{0}:{1}", User.Identity.Name, "123456" ) ) ) );
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Cookie",
Convert.ToBase64String( System.Text.ASCIIEncoding.ASCII.GetBytes( User.Identity.Name ) ) );
string response = await client.GetStringAsync( "http://127.0.0.1:8080/api/values" );
ViewBag.Values = response;
}
}
As you can see, I've tried both using the cookie as well as the username/password. Obviously I want to use the cookie, but at this point if anything works it will be a good step!
My ValuesController in my Web API is properly decorated:
// GET api/values
[BasicAuthorize]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
In my Global.asax.cs in my Web API, I am initializing SimpleMembership:
// initialize our SimpleMembership connection
try
{
WebSecurity.InitializeDatabaseConnection( "AzureConnection", "User", "Id", "Email", autoCreateTables: false );
}
catch ( Exception ex )
{
throw new InvalidOperationException( "The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex );
}
This succeeds and WebSecurity later says that it is initialized so I guess this part is all working properly.
My config files have matching authentication settings as required per MSDN.
Here is the API config:
<authentication mode="Forms">
<forms protection="All" path="/" domain="127.0.0.1" enableCrossAppRedirects="true" timeout="2880" />
</authentication>
<machineKey decryption="AES" decryptionKey="***" validation="SHA1" validationKey="***" />
Here is the Web App config:
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" protection="All" path="/" domain="127.0.0.1" enableCrossAppRedirects="true" timeout="2880" />
</authentication>
<machineKey decryption="AES" decryptionKey="***" validation="SHA1" validationKey="***" />
Note, I am trying this locally (hence the 127.0.0.1 domain), but referencing a database hosted on Azure.
I haven't got to trying any of this from a .NET client application since I can't even get it working between web roles. For the client app, ideally I would make a web call, passing in username/password, retrieve the cookie, and then use the cookie for further web API requests.
I'd like to get what I have working as it seems pretty simple and meets my requirements.
I have not yet tried other solutions such as Thinktecture as it has way more features than I need and it doesn't seem necessary.
What am I missing?
Well, this is embarrassing. My main problem was a simple code error. Here is the correct code. Tell me you can spot the difference from the code in my question.
using ( var handler = new HttpClientHandler() )
{
var cookie = FormsAuthentication.GetAuthCookie( User.Identity.Name, false );
handler.CookieContainer.Add( new Cookie( cookie.Name, cookie.Value, cookie.Path, cookie.Domain ) );
using ( var client = new HttpClient( handler ) )
...
}
Once that was fixed, I started getting 403 Forbidden errors. So I tracked that down and made a small change to the BasicAuthorizeAttribute class to properly support the [BasicAuthorize] attribute when no role is specified.
Here is the modified code:
private bool isAuthorized( string username )
{
// if there are no roles, we're good!
if ( this.Roles == "" )
return true;
bool authorized = false;
var roles = (SimpleRoleProvider)System.Web.Security.Roles.Provider;
authorized = roles.IsUserInRole( username, this.Roles );
return authorized;
}
With that change basic authentication by passing in the forms cookie works!
Now to get non-web client apps working and then refactor the Web App as recommended.
I hope this helps someone in the future!