ASP.NET Core Web API Skip Authentication - c#

I'm writing an ASP.NET Core web application using a custom basic authentication, based on the following example:
ASP.NET Core Web API Authentication
Now I have an action in my user controller in order to register user. So I would like to pass this method my custom Attribute "SkipAuthAttribute" in order to say, if someone calls this method (action) you have to skip the authenticaion (the user which want to register, has no login).
But the type HttpContext has no ActionDescriptor to get the Custom Attributes of the action
Knows someone, how can I skip the authenticaion by some specific actions?

Updated answer:
You should try to write code according to framework. Middleware from example are unsuitable for ASP.NET Core MVC. Here is my example:
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authHeader = (string)this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
if (username == "test" && password == "test")
{
var user = new GenericPrincipal(new GenericIdentity("User"), null);
var ticket = new AuthenticationTicket(user, new AuthenticationProperties(), Options.AuthenticationScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
return Task.FromResult(AuthenticateResult.Fail("No valid user."));
}
}
public class BasicAuthenticationMiddleware : AuthenticationMiddleware<BasicAuthenticationOptions>
{
public BasicAuthenticationMiddleware(
RequestDelegate next,
IOptions<BasicAuthenticationOptions> options,
ILoggerFactory loggerFactory,
UrlEncoder encoder)
: base(next, options, loggerFactory, encoder)
{
}
protected override AuthenticationHandler<BasicAuthenticationOptions> CreateHandler()
{
return new BasicAuthenticationHandler();
}
}
public class BasicAuthenticationOptions : AuthenticationOptions
{
public BasicAuthenticationOptions()
{
AuthenticationScheme = "Basic";
AutomaticAuthenticate = true;
}
}
Registration at Startup.cs - app.UseMiddleware<BasicAuthenticationMiddleware>();. With this code, you can restrict any controller with standart attribute Autorize:
[Authorize(ActiveAuthenticationSchemes = "Basic")]
[Route("api/[controller]")]
public class ValuesController : Controller
and use attribute AllowAnonymous if you apply authorize filter on application level.
Original answer:
From documentation
Add [AllowAnonymous] to the home controller so anonymous users can get information about the site before they register.
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
namespace ContactManager.Controllers {
[AllowAnonymous]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}

Related

Google Sign-in with existing ASP.NET MVC Application

I'm trying to handle google-signin to my existing ASP.NET MVC application. It has a forms authentication developped customely and it works fine for username and password.
I want to add Google-sign-in to this project. I currently added the button, created the app in google developper console got my id and secret and set up the urls.
I can see the button in login page, click, i see my account and picture, select account.
At this point google posts me some data. I received in an Action method an string array object called "credential" which has a string in first position. But I don't know what to do from here on...
Can somebody help me with this? Which document I should use?
I'm reading this: https://developers.google.com/identity/gsi/web till now but i'm stuck.
What I dont want is this:
https://learn.microsoft.com/en-us/aspnet/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on
I want to handle the requests needed myself (with cookies, database checks and keeping track of tokens) and get the user information google provides by myself in my controller's action methods.
Here is a part of the razor view code
<div id="g_id_onload"
data-client_id="my id is here"
data-login_uri="#Url.Action("LoginWithGoogle","Login")"
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="left"
data-auto_prompt="true"
>
</div>
I added this script:
<script src="https://accounts.google.com/gsi/client" async defer></script>
This Action method can catch the string:
[HttpPost]
public ActionResult LoginWithGoogle(string[] credential)
{
ViewBag.Data = credential;
///I will do necessary stuff here.
return View();
}
Notes:
-Identity is not installed and will not be used (if unless impossible without using it).
-My .Net Framework version: 4.7.2
Thanks for the help.
I don't use "full" default auth code too, here is how I handle Google/Yandex/Discord/OAuth response:
[HttpPost]
[AllowAnonymous]
public IActionResult ExternalLogin(string provider, string? returnUrl = null)
{
var redirectUrl = Url.Action(nameof(ExternalLoginCallback), null, new { returnUrl });
var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties);
}
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string? returnUrl = null)
{
var info = await signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return this.Redirect(LoginPath);
}
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
var name = info.Principal.FindFirstValue(ClaimTypes.Name) ?? info.Principal.Identity.Name;
// your own actions & checks with email, name etc
This still required some "default" preparations in Startup:
services.AddIdentity<User, UserStoreService.UserRole>()
.AddUserStore<UserStoreService>()
.AddRoleStore<UserStoreService>()
.AddDefaultTokenProviders();
services.AddAuthentication().AddGoogle(...)
But here User is my own class, UserRole is own (end empty) class, and UserStoreService is my own implementation of IDisposable, IUserStore<User>, IUserEmailStore<User>, IUserClaimStore<User>, IUserSecurityStampStore<User>, IRoleStore<UserStoreService.UserRole> (you may modify this list according to your needs)
This is example how to process external authentication using owin. First of all, you have to setup your startup.cs and update configuration with something like this. Of course you have to import required packages into your project.
public void Configuration(IAppBuilder app)
{
....
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "YourGoogleClientId",
ClientSecret = "YourGoogleClientSecret",
});
}
Then in your account controller (example name) create method that will handle your login with google button. For example:
public ActionResult ExternalLogin(string provider)
{
var returnUrl = Request.Url.GetLeftPart(UriPartial.Authority) + "/Account/ExternalLoginCallback";
return new ChallengeResult(provider, returnUrl);
}
Create your ChallengeResult method:
internal class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUrl)
{
LoginProvider = provider;
RedirectUri = redirectUrl;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
var owin = context.HttpContext.GetOwinContext();
owin.Authentication.Challenge(properties, LoginProvider);
}
}
Add your new authentication method in your account controller. For example:
public async Task<ActionResult> ExternalLoginCallback()
{
try
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null || string.IsNullOrEmpty(loginInfo.Email))
{
return RedirectToAction("ExternalLoginFailed", "WebAccount");
}
else
{
// handle external login, means process user login in your system
}
}
catch (Exception ex)
{
// handle possible exceptions
}
return RedirectToAction("ExternalLoginFailed", "WebAccount");
}
private IAuthenticationManager AuthenticationManager
{
get { return HttpContext.GetOwinContext().Authentication; }
}

Token can't store in .NET Core

I have a project using .NET core version 3.1 and I'm using token for logging in. Everything works perfectly when testing with Postman, it created token and I can use it to access the Home page.
The problem is, when I started testing on client side, it doesn't work. I debugged and saw after logging in, the token is generated but I can't access the HomeController because of [Authorize] attribute.
This is my code to generate token:
public async Task<HttpResponse<LoginResult>> GetTokenAsync(LoginRequest loginInfo)
{
var audience = await _audiences.FindAsync(a => a.Id == loginInfo.ClientId);
string message = string.Empty;
if (audience != null)
{
bool audienceIsValid = _jwtProvider.ValidateAudience(audience.Issuer
, audience.SecretKey
, ref message);
if (audienceIsValid)
return await GenerateToken(loginInfo);
else
message = ErrorMessages.Login_AudienceInvalid;
}
else
message = string.Format(ErrorMessages.Login_Not_Permitted, "Your client Id");
return HttpResponse<LoginResult>.Error(message, HttpStatusCode.BadRequest);
}
I guess that token couldn't be stored correctly.
What am I missing?
UPDATE
This is my code in login
[HttpPost]
[Route("login")]
[AllowAnonymous]
public async Task<ActionResult> Login([FromForm]LoginRequest model)
{
model.ClientId = 1;
var response = await _services.GetTokenAsync(model);
if (response.StatusCode == 200)
{
return RedirectToAction("Index", "Home");
}
return RedirectToAction("Login");
}
And this is what I'm trying to access
[HttpGet]
[Route("index")]
[Authorize]
public IActionResult Index()
{
return View();
}
You need to create a custom policy to specify in the Authorize attribute that is configured to use a custom requirement handler
First you lay out the requirement of the custom policy via a class that inherits IAuthorizationRequirement
public class TokenRequirement : IAuthorizationRequirement
{
}
This is where you would optionally accept parameters if you need them. But normally you pass a token in the header of a request which your custom policy's requirement handler would have access to without the need for explicit parameters.
Your requirement's requirement handler to be used by your custom policy would look something like this
public class TokenHandler : AuthorizationHandler<TokenRequirement>
{
//Some kind of token validator logic injected into your handler via DI
private readonly TokenValidator _tokenValidator;
//The http context of this request session also injected via DI
private readonly IHttpContextAccessor _httpCtx;
//The name of the header your token can be found under on a Http Request
private const string tokenHeaderKey = "authToken";
//Constructor using DI to get a instance of a TokenValidator class you would
//have written yourself, and the httpContext
public TokenHandler(TokenValidator tokenValidator, IHttpContextAccessor httpCtx)
{
_tokenValidator = tokenValidator;
_httpCtx = httpCtx;
}
//Overriden implementation of base class AuthorizationHandler's HandleRequirementAsync method
//This is where you check your token.
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context
,TokenRequirement requirement)
{
if (context.Resource is Endpoint endpoint)
{
HttpRequest httpReqCtx = _httpCtx.HttpContext.Request;
string token =
httpReqCtx.Headers.TryGetValue(tokenHeaderKey, out StringValues tokenVal)
? tokenVal.FirstOrDefault()
: null;
if (string.IsNullOrWhitespace(token))
{
context.Fail();
}
else
{
bool tokenIsValid = await _tokenValidator.ValidToken(token);
if(tokenIsValid)
context.Succeed(requirement);
else
context.Fail();
}
}
return Task.CompletedTask;
}
}
You'd register your custom requirement handler on a custom policy name in Startup.cs like so
//This is a framework extension method under Microsoft.Extensions.DependencyInjection
services.AddHttpContextAccessor();
//Your custom handler
services.AddSingleton<IAuthorizationHandler, TokenHandler>();
//Your custom policy
services.AddAuthorization(options =>
{
options.AddPolicy(
//Your custom policy's name, can be whatever you want
"myCustomTokenCheckerPolicy",
//The requirement your policy is going to check
//Which will be handled by the req handler added above
policy => policy.Requirements.Add(new TokenRequirement())
);
});
The impl on the attribute would look like this
[HttpGet]
[Route("index")]
[Authorize(Policy = "myCustomTokenCheckerPolicy")]
public IActionResult Index()
{
return View();
}

Pass connection string to custom AuthorizeAttribute in asp.net core

I am migrating AuthorizationFilterAttribute from asp.net web api to asp.net core web api.
Below KeywordAuthorizationAttribute is in my asp.net core attribute code.
public class KeywordAuthorizationAttribute : AuthorizeAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationFilterContext context)
{
var user = context.HttpContext.User;
if (user.Identity.IsAuthenticated)
{
SQLDataAccess sqlDataAccess = new SQLDataAccess(**passedConnectionStringFrom_appsettings.json**);
var username = context.HttpContext.User.Identity.Name.Substring
(context.HttpContext.User.Identity.Name.LastIndexOf(#"\") + 1);
if (!sqlDataAccess.IsUserAllowed((string)context.RouteData.Values["Controller"], username))
{
context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
return;
}
}
else
{
context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.Unauthorized);
return;
}
}
}
Example of KeywordAuthorizationAttribute - If the controller or controller action is decorated with this AuthorizeAttribute it will take the username and check the access of that controller from database.
[Authorize]
[HttpGet]
[KeywordAuthorization]
public IActionResult Get()
return Ok();
}
My question is how can I pass the connection string to KeywordAuthorizationAttribute?
I have already set the connection string in appsettings.json
{
"ConnectionStrings": {
"EmployeeDBConnection": "server=(localdb)\\MSSQLLocalDB;database=EmployeeDB;Trusted_Connection=true"
}
}
Use the AuthorizationFilterContext.HttpContext.RequestServices.GetService method from the context argument passed into your OnAuthorization method:
public void OnAuthorization(AuthorizationFilterContext context)
{
...
if (user.Identity.IsAuthenticated)
{
var connectionString = context.HttpContext.RequestServices
.GetService(typeof(IConfiguration))
.GetConnectionString("EmployeeDBConnection");
// GetConnectionString is an extension method, so add
// using Microsoft.Extensions.Configuration
...
}
}
Using this technique, you could also simply use your DbContext as well.
You will need to use the extension for Configuration like in Startup.cs and you can get the connection string with:
Configuration.GetConnectionString("EmployeeDBConnection");

How do I create Basic Authentication in Web Api?

I have the following .cs in order to create some basic authentication in my api. This works fine,but it appears only one time, when i run it for the first time.How do I make it appear again (in every run)?
namespace CMob
{
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(HttpActionContext actionContext)
{
var authHeader = actionContext.Request.Headers.Authorization;
if (authHeader != null)
{
var authenticationToken = actionContext.Request.Headers.Authorization.Parameter;
var decodedAuthenticationToken = Encoding.UTF8.GetString(Convert.FromBase64String(authenticationToken));
var usernamePasswordArray = decodedAuthenticationToken.Split(':');
var userName = usernamePasswordArray[0];
var password = usernamePasswordArray[1];
var isValid = userName == "chrysa" && password == "1234";
if (isValid)
{
var principal = new GenericPrincipal(new GenericIdentity(userName), null);
Thread.CurrentPrincipal = principal;
return;
}
}
HandleUnathorized(actionContext);
}
private static void HandleUnathorized(HttpActionContext actionContext)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", "Basic Scheme='Data' location = 'http://localhost:");
}
}
}ยจ
My controller
public class DController : ApiController
{
[BasicAuthentication]
[Route("api/D")]
public IEnumerable<D> Get()
{
using (CM_DataEntities entities = new CM_DataEntities())
{
return entities.Ds.ToList();
}
}
}
Thanks!
"To unauthenticated requests, the server should return a response whose header contains a HTTP 401 Unauthorized status[4] and a WWW-Authenticate field.[5]"
You should refer to https://en.wikipedia.org/wiki/Basic_access_authentication.
I am quite sure you can find the answer you're looking for over there.
Basically, The browser provides authentication, and you have absolutely no control over it.
You have to declare the attribute in WebApiConfig.cs :
config.Filters.Add(new BasicAuthenticationAttribute());
And you have to decorate your Controllers and or Actions :
public class MyController : ApiController
{
[BasicAuthentication]
public string Get()
{
return "Hello";
}
}
It actually depends on what behavior you want to define.
If you wish to use your authentication filter for your whole API, you can add it to the global filter list this way (in WebApiConfig.cs) :
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new BasicAuthenticationAttribute());
}
If you desire to restrict all methods of a controller, decorate it this way :
[BasicAuthentication]
public class RestrictedController : ApiController
{
//Your controller definition
}
Of course you can use it on a single method, this way :
[BasicAuthentication]
public JsonResult GetJsonDataAsAuthenticatedUser()
{
//your method definition
}
You can specify a method which require no authentication with AllowAnonymous decoration :
[BasicAuthentication]
public class RestrictedController : ApiController
{
[AllowAnonymous]
public IActionResult Authenticate()
{
//Your authentication entry point
}
}
You can refer to this link

ASP.NET Core Web API Authentication

I'm struggling with how to set up authentication in my web service.
The service is build with the ASP.NET Core web api.
All my clients (WPF applications) should use the same credentials to call the web service operations.
After some research, I came up with basic authentication - sending a username and password in the header of the HTTP request.
But after hours of research, it seems to me that basic authentication is not the way to go in ASP.NET Core.
Most of the resources I found are implementing authentication using OAuth or some other middleware. But that seems to be oversized for my scenario, as well as using the Identity part of ASP.NET Core.
So what is the right way to achieve my goal - simple authentication with username and password in a ASP.NET Core web service?
Now, after I was pointed in the right direction, here's my complete solution:
This is the middleware class which is executed on every incoming request and checks if the request has the correct credentials. If no credentials are present or if they are wrong, the service responds with a 401 Unauthorized error immediately.
public class AuthenticationMiddleware
{
private readonly RequestDelegate _next;
public AuthenticationMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
string authHeader = context.Request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith("Basic"))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':');
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
if(username == "test" && password == "test" )
{
await _next.Invoke(context);
}
else
{
context.Response.StatusCode = 401; //Unauthorized
return;
}
}
else
{
// no authorization header
context.Response.StatusCode = 401; //Unauthorized
return;
}
}
}
The middleware extension needs to be called in the Configure method of the service Startup class
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseMiddleware<AuthenticationMiddleware>();
app.UseMvc();
}
And that's all! :)
A very good resource for middleware in .Net Core and authentication can be found here:
https://www.exceptionnotfound.net/writing-custom-middleware-in-asp-net-core-1-0/
You can implement a middleware which handles Basic authentication.
public async Task Invoke(HttpContext context)
{
var authHeader = context.Request.Headers.Get("Authorization");
if (authHeader != null && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
var token = authHeader.Substring("Basic ".Length).Trim();
System.Console.WriteLine(token);
var credentialstring = Encoding.UTF8.GetString(Convert.FromBase64String(token));
var credentials = credentialstring.Split(':');
if(credentials[0] == "admin" && credentials[1] == "admin")
{
var claims = new[] { new Claim("name", credentials[0]), new Claim(ClaimTypes.Role, "Admin") };
var identity = new ClaimsIdentity(claims, "Basic");
context.User = new ClaimsPrincipal(identity);
}
}
else
{
context.Response.StatusCode = 401;
context.Response.Headers.Set("WWW-Authenticate", "Basic realm=\"dotnetthoughts.net\"");
}
await _next(context);
}
This code is written in a beta version of asp.net core. Hope it helps.
To use this only for specific controllers for example use this:
app.UseWhen(x => (x.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase)),
builder =>
{
builder.UseMiddleware<AuthenticationMiddleware>();
});
I think you can go with JWT (Json Web Tokens).
First you need to install the package System.IdentityModel.Tokens.Jwt:
$ dotnet add package System.IdentityModel.Tokens.Jwt
You will need to add a controller for token generation and authentication like this one:
public class TokenController : Controller
{
[Route("/token")]
[HttpPost]
public IActionResult Create(string username, string password)
{
if (IsValidUserAndPasswordCombination(username, password))
return new ObjectResult(GenerateToken(username));
return BadRequest();
}
private bool IsValidUserAndPasswordCombination(string username, string password)
{
return !string.IsNullOrEmpty(username) && username == password;
}
private string GenerateToken(string username)
{
var claims = new Claim[]
{
new Claim(ClaimTypes.Name, username),
new Claim(JwtRegisteredClaimNames.Nbf, new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds().ToString()),
new Claim(JwtRegisteredClaimNames.Exp, new DateTimeOffset(DateTime.Now.AddDays(1)).ToUnixTimeSeconds().ToString()),
};
var token = new JwtSecurityToken(
new JwtHeader(new SigningCredentials(
new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Secret Key You Devise")),
SecurityAlgorithms.HmacSha256)),
new JwtPayload(claims));
return new JwtSecurityTokenHandler().WriteToken(token);
}
}
After that update Startup.cs class to look like below:
namespace WebAPISecurity
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = "JwtBearer";
options.DefaultChallengeScheme = "JwtBearer";
})
.AddJwtBearer("JwtBearer", jwtBearerOptions =>
{
jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("Secret Key You Devise")),
ValidateIssuer = false,
//ValidIssuer = "The name of the issuer",
ValidateAudience = false,
//ValidAudience = "The name of the audience",
ValidateLifetime = true, //validate the expiration and not before values in the token
ClockSkew = TimeSpan.FromMinutes(5) //5 minute tolerance for the expiration date
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
}
And that's it, what is left now is to put [Authorize] attribute on the Controllers or Actions you want.
Here is a link of a complete straight forward tutorial.
http://www.blinkingcaret.com/2017/09/06/secure-web-api-in-asp-net-core/
I have implemented BasicAuthenticationHandler for basic authentication so you can use it with standart attributes Authorize and AllowAnonymous.
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authHeader = (string)this.Request.Headers["Authorization"];
if (!string.IsNullOrEmpty(authHeader) && authHeader.StartsWith("basic", StringComparison.OrdinalIgnoreCase))
{
//Extract credentials
string encodedUsernamePassword = authHeader.Substring("Basic ".Length).Trim();
Encoding encoding = Encoding.GetEncoding("iso-8859-1");
string usernamePassword = encoding.GetString(Convert.FromBase64String(encodedUsernamePassword));
int seperatorIndex = usernamePassword.IndexOf(':', StringComparison.OrdinalIgnoreCase);
var username = usernamePassword.Substring(0, seperatorIndex);
var password = usernamePassword.Substring(seperatorIndex + 1);
//you also can use this.Context.Authentication here
if (username == "test" && password == "test")
{
var user = new GenericPrincipal(new GenericIdentity("User"), null);
var ticket = new AuthenticationTicket(user, new AuthenticationProperties(), Options.AuthenticationScheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
else
{
return Task.FromResult(AuthenticateResult.Fail("No valid user."));
}
}
this.Response.Headers["WWW-Authenticate"]= "Basic realm=\"yourawesomesite.net\"";
return Task.FromResult(AuthenticateResult.Fail("No credentials."));
}
}
public class BasicAuthenticationMiddleware : AuthenticationMiddleware<BasicAuthenticationOptions>
{
public BasicAuthenticationMiddleware(
RequestDelegate next,
IOptions<BasicAuthenticationOptions> options,
ILoggerFactory loggerFactory,
UrlEncoder encoder)
: base(next, options, loggerFactory, encoder)
{
}
protected override AuthenticationHandler<BasicAuthenticationOptions> CreateHandler()
{
return new BasicAuthenticationHandler();
}
}
public class BasicAuthenticationOptions : AuthenticationOptions
{
public BasicAuthenticationOptions()
{
AuthenticationScheme = "Basic";
AutomaticAuthenticate = true;
}
}
Registration at Startup.cs - app.UseMiddleware<BasicAuthenticationMiddleware>();. With this code, you can restrict any controller with standart attribute Autorize:
[Authorize(ActiveAuthenticationSchemes = "Basic")]
[Route("api/[controller]")]
public class ValuesController : Controller
and use attribute AllowAnonymous if you apply authorize filter on application level.
As rightly said by previous posts, one of way is to implement a custom basic authentication middleware. I found the best working code with explanation in this blog:
Basic Auth with custom middleware
I referred the same blog but had to do 2 adaptations:
While adding the middleware in startup file -> Configure function, always add custom middleware before adding app.UseMvc().
While reading the username, password from appsettings.json file, add static read only property in Startup file. Then read from appsettings.json. Finally, read the values from anywhere in the project. Example:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public static string UserNameFromAppSettings { get; private set; }
public static string PasswordFromAppSettings { get; private set; }
//set username and password from appsettings.json
UserNameFromAppSettings = Configuration.GetSection("BasicAuth").GetSection("UserName").Value;
PasswordFromAppSettings = Configuration.GetSection("BasicAuth").GetSection("Password").Value;
}
You can use an ActionFilterAttribute
public class BasicAuthAttribute : ActionFilterAttribute
{
public string BasicRealm { get; set; }
protected NetworkCredential Nc { get; set; }
public BasicAuthAttribute(string user,string pass)
{
this.Nc = new NetworkCredential(user,pass);
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var req = filterContext.HttpContext.Request;
var auth = req.Headers["Authorization"].ToString();
if (!String.IsNullOrEmpty(auth))
{
var cred = System.Text.Encoding.UTF8.GetString(Convert.FromBase64String(auth.Substring(6)))
.Split(':');
var user = new {Name = cred[0], Pass = cred[1]};
if (user.Name == Nc.UserName && user.Pass == Nc.Password) return;
}
filterContext.HttpContext.Response.Headers.Add("WWW-Authenticate",
String.Format("Basic realm=\"{0}\"", BasicRealm ?? "Ryadel"));
filterContext.Result = new UnauthorizedResult();
}
}
and add the attribute to your controller
[BasicAuth("USR", "MyPassword")]
In this public Github repo
https://github.com/boskjoett/BasicAuthWebApi
you can see a simple example of a ASP.NET Core 2.2 web API with endpoints protected by Basic Authentication.
ASP.NET Core 2.0 with Angular
https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login
Make sure to use type of authentication filter
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

Categories

Resources