Swagger and WebAPI (Swashbuckle) - making the /swagger endpoint private? - asp.net-web-api

I just installed Swashbuckle in my WebAPI project and so far so good, though the /swagger api endpoint is public.
I'd like to only allow access to my mobile app developer. How can I hide my API behind a password or require forms auth here?

I achieved this with the below:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
string username = null;
if (Thread.CurrentPrincipal.Identity != null && Thread.CurrentPrincipal.Identity.Name != null) {
username = Thread.CurrentPrincipal.Identity.Name.ToLower();
}
if (IsSwagger(request) && username != "Admin") {
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
return Task.FromResult(response);
}
else {
return base.SendAsync(request, cancellationToken);
}
}
private bool IsSwagger(HttpRequestMessage request) {
return request.RequestUri.PathAndQuery.StartsWith("/swagger");
}

If your project if hosted under IIS, you can use either Windows (with proper access rights on site folder) or basic authentication on your IIS website, and disable Anonymous authentication.

#bsoulier answer is perfectly reasonable. Another way is to use JavaScript/AJAX.
I have not tried securing the actual swagger-ui index page, since most of the time authentication on the endpoints is enough.
But any JavaScript can easily be injected into the index page or you can use your own custom index page. That means you can use any authentication that you want to. Simply create a simple HTML form and use AJAX to make a call to login the user before allowing them to see any content or before redirecting them to the real swagger-ui page.
Injecting JavaScript files into the swagger-ui page from SwaggerConfig:
c.InjectJavaScript(thisAssembly, "Your.Namespace.testScript1.js");
Injecting custom index page into the swagger-ui page from SwaggerConfig:
c.CustomAsset("index", containingAssembly, "Your.Namespace.index.html");

Related

Redirect from void action

I have a void function that based on a conditional statement should redirect users to a different action.
public void MyFunction()
{
if (!condition)
{
Redirect(url);
}
...
...
}
Using redirect without a return statement doesn't seem to work. Is there a more suitable method for accomplishing this?
Response.Redirect("Some URL") or Server.Transfer("Some URL") can be used to redirect any web page
Response.Redirect("Some URL") should be used when to :
redirect the request to some plain HTML pages on our
server or to some other web server
not to care about causing additional roundtrips to the server on each request
not need to preserve Query String and Form Variables from the original request
to be able to see the new redirected URL where he is
redirected in his browser (and be able to bookmark it if its
necessary)
Server.Transfer("Some URL") should be used when to:
transfer current page request to another .aspx page on the
same server
preserve server resources and avoid the
unnecessary roundtrips to the server
preserve Query String and Form Variables (optionally) we don't need to show the real URL
where we redirected the request in the users Web Browser
try this
public IActionResult MyFunction()
{
if (!condition)
{
return Redirect(url);
}
...
...
}
About IActionResult
Actions can return anything, but frequently return an instance of
IActionResult (or Task for async methods) that produces
a response. The action method is responsible for choosing what kind of
response. The action result does the responding.
from https://learn.microsoft.com/en-us/aspnet/core/mvc/controllers/actions
You may get more information about action methods at above link

What is a good way to pass additional information to the http response when issuing access token with Owin middleware in Asp Net Web Api?

I am using Owin middleware to implement token-based security for my application. When issuing the access token to the client I would also like to pass User Id along with the token, so that, the client application will know the User Id and will be able to call GetUserById (one of the methods inside UserController) in order to show the user his starting page. The best solution I could come up with so far is just adding User Id to the response header. Take a look at my OAuthAuthorizationServerProvider class, in GrantResourceOwnerCredentialsmethod I am adding User Id to the header, using context.Response.Headers.Add("User-Id", new string[] { "1" })
Here is the implementation of my OAuthAuthorizationServerProviderclass
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
//The actual credential check will be added later
if (context.UserName=="user" && context.Password=="user")
{
identity.AddClaim(new Claim("Id", "1"));
context.Validated(identity);
//Here I am adding User Id to the response header
context.Response.Headers.Add("User-Id", new string[] { "1" });
}
else
{
context.SetError("invalid_grant","The credentials provided are not valid");
return;
}
}
}
Basically the client then will have to read User-Id from the header. Is this a good solution I came up with or there is a better one? Also what if I want to pass the whole User object with all its properties to the response is it possible and how to do this?
Since you store the ID already in the claims, why don't you just decode your token on the client and read out the user id like that? Passing it through the headers could allow tampering with it (security).
Have a look on how you could achieve to decode your token and read the claims. This is a c# example https://contos.io/peeking-inside-your-jwt-tokens-using-c-bf6a729d06c8 but this could also be done even through javascript.
This, assuming you use the JWT-format as token (was not specified in your initial question).
Bad way to store UserID as a response header back to client side. This is a huge security concern.
The best way would be to store it as a Claims.
It is very easy to achieve this and get back the claims in the client side.
In your controller, call this and remember to reference
using Microsoft.AspNet.Identity;
var userId = User.Identity.GetUserId();

Why am I automatically pushed to login page when a user is unauthenticated by calling the Web API?

I am using MVC 6 web api and I have a method (shown below). When the user is not authenticated (logged on) and makes the call (example url: https://localhost:44338/api/account/Test), they get automatically pushed to url:
https://localhost:44338/Account/Login?ReturnUrl=%2Fapi%2Faccount%2FTest
BUT this is a web api project and does not have any views (such as im automatically getting pushed to here).
Code:
[HttpGet("test")]
[Authorize]
public async Task<IActionResult> Test()
{
return Json("success");
}
Why am I getting automatically pushed to the login page? I've NOTHING in my startup.cs or web.config that specifies this. It seems like default behaviour? How do I disable this so I just get the 401 status?
Thanks in advance!
For convenience, here's the solution that worked for me:
services.Configure<IdentityOptions>(o =>
{
o.Cookies.ApplicationCookie.Events = new CookieAuthenticationEvents()
{
OnRedirectToLogin = ctx =>
{
if (ctx.Response.StatusCode == (int)HttpStatusCode.Unauthorized)
{
return Task.FromResult<object>(null);
}
ctx.Response.Redirect(ctx.RedirectUri);
return Task.FromResult<object>(null);
}
};
});
I was pointed to this article: mvc6 unauthorized results in redirect instead by #TrevorWard
ASP.NET automatically redirects to the login page, if you have AuthorizationAttribute defined in your App_Start/FilterConfig.cs, see if AuthorizeAttribute() is defined. If it does, remove it.
Check if you have App_Start/Startup.Auth.cs. If it does, delete it.
If Startup.cs is decorated with the attribute [assembly: OwinStartupAttribute(typeof(FleetSys.Startup))], remove that attribute.
You are probably using wrong Authorize attribute.
In MVC, you should use System.Web.Mvc.AuthorizeAttribute and that one will redirect.
In Web API, you should use System.Web.Http.AuthorizeAttribute and that one will return 401 status code.

Passing logged on user to Angular services (Windows Authentication)

I'm building a mostly client-side app (HTML/CSS/Angular) and it calls a Web API backend for data retrieval. Pretty standard stuff. However, we are behind a firewall and use Windows Authentication to pass through the currently logged on user. I have exhausted myself trying to determine how to simply retreive the username of the currently logged on user to pass to Angular so I can then pass it up to the Web API.
Any suggestions?
So far I've created a <script> section in the head of my HTML and retrieve the username into a local variable like so:
<script type="text/javascript">
var loggedOnUser = '<%= Request.ServerVariables["REMOTE_USER"] %>';
console.log('logged on user is ' + loggedOnUser);
</script>
The problem is that I'm always getting back an empty string (well, no value at all actually).
The controller I'm using looks like this:
public class AuthenticationController : ApiController
{
private static Logger logger = LogManager.GetCurrentClassLogger();
public IHttpActionResult Get(string activeDirectoryDomainName, string username)
{
string user = HttpContext.Current.User.Identity.Name;
logger.Debug("user: " + user);
return Json(BLL.GetAuthenticationInfo(activeDirectoryDomainName, username));
}
}
The logged result from the controller is empty too.
Your server should be doing the validation and checking who the user is, not the angular application telling the server who they are (not secure!).
If you just want to display the username you should be able to do a call to the web api and have it return the username (that way you can see who they are authenticating as)
If you are returning a Razor / cshtml file as your view / layout, you can include the username there as well with #User.Identity.Name

Https and Http only on .net webapi v2 actions

I have been working on a project with webapi 2 using oauth 2 (openid connect to be precise) bearer tokens to grant access. Now the whole idea is that the bearer tokens are only secure if used with a secure connection.
Until now I have simply not allowed http calls to the webserver which kinda worked since no one could do a http call with a bearer token.
We now have some endpoints that need to be avaible over http (no bearer token/authenticaiton required) and we are going to enable http of course. Now my question is, what is normal in these situations?
Would I have an attribute that I can put on all actions that only accept https?
Can I make that the default behaviour and only put attribute on those that are okay on http?
What is the advice on, is it our responsibility that no one use a oauth token over a non secure line or is the user of the api ?
I believe the right way to do this is to add global action filter which forces you to use HTTPs on all controllers/actions on your Web API. The implementation for this HTTPs action filter can be as the below:
public class ForceHttpsAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
var request = actionContext.Request;
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
var html = "<p>Https is required</p>";
if (request.Method.Method == "GET")
{
actionContext.Response = request.CreateResponse(HttpStatusCode.Found);
actionContext.Response.Content = new StringContent(html, Encoding.UTF8, "text/html");
UriBuilder httpsNewUri = new UriBuilder(request.RequestUri);
httpsNewUri.Scheme = Uri.UriSchemeHttps;
httpsNewUri.Port = 443;
actionContext.Response.Headers.Location = httpsNewUri.Uri;
}
else
{
actionContext.Response = request.CreateResponse(HttpStatusCode.NotFound);
actionContext.Response.Content = new StringContent(html, Encoding.UTF8, "text/html");
}
}
}
}
Now you want to register this globally on WebApiConfig class as the below:
config.Filters.Add(new ForceHttpsAttribute());
As I understand from your question, the number of controllers you want to call them over https are greater than controllers over http, so you want to add override attribute to those http controllers [OverrideActionFiltersAttribute]
Do not forget to attribute your anonymous controllers with [AllowAnonymous] attribute.
But my recommendation is to keep all the communication over https and you just allow anonymos calls.
You can read more about enforcing https on my blog here: http://bitoftech.net/2013/12/03/enforce-https-asp-net-web-api-basic-authentication/
Hope this helps.
Firstly I think you definitely have to make best efforts to ensure the security of that token and so the server should enforce SSL.
We are using web api v1 (infrastructure restrictions :() and we have a global DelegatingHandler that enforces SSL on all requests except for certain uris that are on a whitelist (not the prettiest solution but it works for now).
In web api 2 I reckon you could have a global FilterAttribute to enforce the SSL connectivity and then use the new attribute override feature http://dotnet.dzone.com/articles/new-filter-overrides-feature to create your exceptions - all theory though ! :)
Hope that helps
Garrett

Resources