Authorization 401 error - c#

I am using the following basic authentication method and its outputting the following error - "$id":"1","Message":"Authorization has been denied for this request", when I call -- api/values
[Authorize]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
BasicAuthMessageHandler class:
public class BasicAuthMessageHandler : DelegatingHandler
{
private const string BasicAuthResponseHeader = "WWW-Authenticate";
private const string BasicAuthResponseHeaderValue = "Basic";
[Inject]
public iUser Repository { get; set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
AuthenticationHeaderValue authValue = request.Headers.Authorization;
if (authValue != null && !String.IsNullOrWhiteSpace(authValue.Parameter))
{
api_login parsedCredentials = ParseAuthorizationHeader(authValue.Parameter);
if (parsedCredentials != null)
{
IPrincipal principal;
if (TryGetPrincipal(parsedCredentials.username, parsedCredentials.password, out principal))
{
Thread.CurrentPrincipal = principal;
}
}
}
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
if (response.StatusCode == HttpStatusCode.Unauthorized && !response.Headers.Contains(BasicAuthResponseHeader))
{
response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
}
return response;
});
}
private api_login ParseAuthorizationHeader(string authHeader)
{
string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] { ':' });
if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1])) return null;
return new api_login()
{
username = credentials[0],
password = credentials[1],
};
}
private bool TryGetPrincipal(string userName, string password, out IPrincipal principal)
{
// this is the method that authenticates against my repository (in this case, hard coded)
// you can replace this with whatever logic you'd use, but proper separation would put the
// data access in a repository or separate layer/library.
api_login user = Repository.Validate2(userName, password);
if (user != null)
{
// once the user is verified, assign it to an IPrincipal with the identity name and applicable roles
//principal = new GenericPrincipal(new GenericIdentity(user.username));
principal = new GenericPrincipal(new GenericIdentity(user.username), System.Web.Security.Roles.GetRolesForUser(user.role));
return true;
}
principal = null;
return false;
}
}
User class:
public api_login Validate2(string userName, string Password)
{
// Find a user that matches that username and password (this will only validate if both match)
return db.api_login.FirstOrDefault(u => u.username == userName && u.password == Password);
}
Am I missing something in the code and is this right approach for authenticating web api?
Thanks

Make sure that the credential is included in the header and separated by a ":". Put a break point at
string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] { ':' });
to see what is the value of the authentication header.
Hope this helps

Related

Extend JWT time in c#

I have this filter (below) and I want to extend the time of the token (by replacing the token and re-writing a new one for the user) ... Can anybody help me achieve this?
This is the standard filter without any custom changes or anything, I've already handled token expiry, now I want to renew the token when a request is made within token expiry time
public class JwtAuthenticationAttribute : Attribute, IAuthenticationFilter
{
public string Realm { get; set; }
public bool AllowMultiple => false;
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
var request = context.Request;
var authorization = request.Headers.Authorization;
if (authorization == null || authorization.Scheme != "Bearer")
return;
if (string.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing Jwt Token", request);
return;
}
var token = authorization.Parameter;
var principal = await AuthenticateJwtToken(token);
if (principal == null)
context.ErrorResult = new AuthenticationFailureResult("Invalid token", request);
else
context.Principal = principal;
// HERE SHOULD BE THE IMPLEMENTATION FOR TOKEN RENEWAL
}
private static bool ValidateToken(string token, out string username)
{
username = null;
var simplePrinciple = JwtManager.GetPrincipal(token);
var identity = simplePrinciple?.Identity as ClaimsIdentity;
if (identity == null)
return false;
if (!identity.IsAuthenticated)
return false;
var usernameClaim = identity.FindFirst(ClaimTypes.Name);
username = usernameClaim?.Value;
if (string.IsNullOrEmpty(username))
return false;
// More validate to check whether username exists in system
return true;
}
protected Task<IPrincipal> AuthenticateJwtToken(string token)
{
string username;
if (ValidateToken(token, out username))
{
// based on username to get more information from database in order to build local identity
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, username)
// Add more claims if needed: Roles, ...
};
var identity = new ClaimsIdentity(claims, "Jwt");
IPrincipal user = new ClaimsPrincipal(identity);
return Task.FromResult(user);
}
return Task.FromResult<IPrincipal>(null);
}
public Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
{
Challenge(context);
return Task.FromResult(0);
}
private void Challenge(HttpAuthenticationChallengeContext context)
{
string parameter = null;
if (!string.IsNullOrEmpty(Realm))
parameter = "realm=\"" + Realm + "\"";
context.ChallengeWith("Bearer", parameter);
}
}

ASP.NET MVC 5 function from another project within same solution always fails

I have a project that contains 2 applications that are structured like this:
App
AppAPI
AppAPI references App and calls AuthenticateUser within the ApiAccountController class from App.
AppAPI
public class TokenController : ApiController
{
// This is naive endpoint for demo, it should use Basic authentication to provide token or POST request
// GET api/token/
public string Get(string username, string password)
{
if (CheckUser(username, password))
{
return JwtManager.GenerateToken(username);
}
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
private bool CheckUser(string username, string password)
{
ApiAccountController accountController = new ApiAccountController();
return accountController.AuthenticateUser(username,password);
}
}
App
ApplicationDbContext dbContext = new ApplicationDbContext();
Logger log = LogManager.GetCurrentClassLogger();
PasswordHasher passwordHasher = new PasswordHasher();
// GET: Account
public bool AuthenticateUser(String username, String password)
{
try
{
var user = dbContext.Users.FirstOrDefault(u => u.UserName == username);
//var user = dbContext.Users.Count(u => u.UserName == username);
if (user == null)
{
log.Error(username + " not found");
return false;
}
else
{
var result = passwordHasher.VerifyHashedPassword(user.PasswordHash, password);
if (result == PasswordVerificationResult.Success)
{
return true;
}
else
{
log.Error("Invalid password for user: " + username);
return false;
}
}
//return false;
}
catch (Exception e)
{
log.Error(e, "Exception found for user: " + username);
return false;
}
}
The expected behaviour is for me to use Postman to connect to AppApi like this
http://localhost:9000/api/token?username=user#one.com&password=P#ssw0rd
and for it to authenticate this user.
However, for some reason this has been failing by returning null even though there is already a user that has been created.

How can I validate a JWT passed via cookies?

The UseJwtBearerAuthentication middleware in ASP.NET Core makes it easy to validate incoming JSON Web Tokens in Authorization headers.
How do I authenticate a JWT passed via cookies, instead of a header? Something like UseCookieAuthentication, but for a cookie that just contains a JWT.
I suggest you take a look at the following link.
https://stormpath.com/blog/token-authentication-asp-net-core
They store JWT token in an http only cookie to prevent XSS attacks.
They then validate the JWT token in the cookie by adding the following code in the Startup.cs:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
AuthenticationScheme = "Cookie",
CookieName = "access_token",
TicketDataFormat = new CustomJwtDataFormat(
SecurityAlgorithms.HmacSha256,
tokenValidationParameters)
});
Where CustomJwtDataFormat() is their custom format defined here:
public class CustomJwtDataFormat : ISecureDataFormat<AuthenticationTicket>
{
private readonly string algorithm;
private readonly TokenValidationParameters validationParameters;
public CustomJwtDataFormat(string algorithm, TokenValidationParameters validationParameters)
{
this.algorithm = algorithm;
this.validationParameters = validationParameters;
}
public AuthenticationTicket Unprotect(string protectedText)
=> Unprotect(protectedText, null);
public AuthenticationTicket Unprotect(string protectedText, string purpose)
{
var handler = new JwtSecurityTokenHandler();
ClaimsPrincipal principal = null;
SecurityToken validToken = null;
try
{
principal = handler.ValidateToken(protectedText, this.validationParameters, out validToken);
var validJwt = validToken as JwtSecurityToken;
if (validJwt == null)
{
throw new ArgumentException("Invalid JWT");
}
if (!validJwt.Header.Alg.Equals(algorithm, StringComparison.Ordinal))
{
throw new ArgumentException($"Algorithm must be '{algorithm}'");
}
// Additional custom validation of JWT claims here (if any)
}
catch (SecurityTokenValidationException)
{
return null;
}
catch (ArgumentException)
{
return null;
}
// Validation passed. Return a valid AuthenticationTicket:
return new AuthenticationTicket(principal, new AuthenticationProperties(), "Cookie");
}
// This ISecureDataFormat implementation is decode-only
public string Protect(AuthenticationTicket data)
{
throw new NotImplementedException();
}
public string Protect(AuthenticationTicket data, string purpose)
{
throw new NotImplementedException();
}
}
Another solution would be to write some custom middleware that would intercept each request, look if it has a cookie, extract the JWT from the cookie and add an Authorization header on the fly before it reaches the Authorize filter of your controllers. Here is some code that work for OAuth tokens, to get the idea:
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace MiddlewareSample
{
public class JWTInHeaderMiddleware
{
private readonly RequestDelegate _next;
public JWTInHeaderMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var authenticationCookieName = "access_token";
var cookie = context.Request.Cookies[authenticationCookieName];
if (cookie != null)
{
var token = JsonConvert.DeserializeObject<AccessToken>(cookie);
context.Request.Headers.Append("Authorization", "Bearer " + token.access_token);
}
await _next.Invoke(context);
}
}
}
... where AccessToken is the following class:
public class AccessToken
{
public string token_type { get; set; }
public string access_token { get; set; }
public string expires_in { get; set; }
}
Hope this helps.
NOTE: It is also important to note that this way of doing things (token in http only cookie) will help prevent XSS attacks but however does not immune against Cross Site Request Forgery (CSRF) attacks, you must therefore also use anti-forgery tokens or set custom headers to prevent those.
Moreover, if you do not do any content sanitization, an attacker can still run an XSS script to make requests on behalf of the user, even with http only cookies and CRSF protection enabled. However, the attacker will not be able to steal the http only cookies that contain the tokens, nor will the attacker be able to make requests from a third party website.
You should therefore still perform heavy sanitization on user-generated content such as comments etc...
EDIT: It was written in the comments that the blog post linked and the code have been written by the OP himself a few days ago after asking this question.
For those who are interested in another "token in a cookie" approach to reduce XSS exposure they can use oAuth middleware such as the OpenId Connect Server in ASP.NET Core.
In the method of the token provider that is invoked to send the token back (ApplyTokenResponse()) to the client you can serialize the token and store it into a cookie that is http only:
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Server;
using Newtonsoft.Json;
namespace Shared.Providers
{
public class AuthenticationProvider : OpenIdConnectServerProvider
{
private readonly IApplicationService _applicationservice;
private readonly IUserService _userService;
public AuthenticationProvider(IUserService userService,
IApplicationService applicationservice)
{
_applicationservice = applicationservice;
_userService = userService;
}
public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
{
if (string.IsNullOrEmpty(context.ClientId))
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Missing credentials: ensure that your credentials were correctly " +
"flowed in the request body or in the authorization header");
return Task.FromResult(0);
}
#region Validate Client
var application = _applicationservice.GetByClientId(context.ClientId);
if (applicationResult == null)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Application not found in the database: ensure that your client_id is correct");
return Task.FromResult(0);
}
else
{
var application = applicationResult.Data;
if (application.ApplicationType == (int)ApplicationTypes.JavaScript)
{
// Note: the context is marked as skipped instead of validated because the client
// is not trusted (JavaScript applications cannot keep their credentials secret).
context.Skip();
}
else
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidClient,
description: "Authorization server only handles Javascript application.");
return Task.FromResult(0);
}
}
#endregion Validate Client
return Task.FromResult(0);
}
public override async Task HandleTokenRequest(HandleTokenRequestContext context)
{
if (context.Request.IsPasswordGrantType())
{
var username = context.Request.Username.ToLowerInvariant();
var user = await _userService.GetUserLoginDtoAsync(
// filter
u => u.UserName == username
);
if (user == null)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid username or password.");
return;
}
var password = context.Request.Password;
var passWordCheckResult = await _userService.CheckUserPasswordAsync(user, context.Request.Password);
if (!passWordCheckResult)
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid username or password.");
return;
}
var roles = await _userService.GetUserRolesAsync(user);
if (!roles.Any())
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidRequest,
description: "Invalid user configuration.");
return;
}
// add the claims
var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
identity.AddClaim(ClaimTypes.NameIdentifier, user.Id, OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken);
identity.AddClaim(ClaimTypes.Name, user.UserName, OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken);
// add the user's roles as claims
foreach (var role in roles)
{
identity.AddClaim(ClaimTypes.Role, role, OpenIdConnectConstants.Destinations.AccessToken, OpenIdConnectConstants.Destinations.IdentityToken);
}
context.Validate(new ClaimsPrincipal(identity));
}
else
{
context.Reject(
error: OpenIdConnectConstants.Errors.InvalidGrant,
description: "Invalid grant type.");
return;
}
return;
}
public override Task ApplyTokenResponse(ApplyTokenResponseContext context)
{
var token = context.Response.Root;
var stringified = JsonConvert.SerializeObject(token);
// the token will be stored in a cookie on the client
context.HttpContext.Response.Cookies.Append(
"exampleToken",
stringified,
new Microsoft.AspNetCore.Http.CookieOptions()
{
Path = "/",
HttpOnly = true, // to prevent XSS
Secure = false, // set to true in production
Expires = // your token life time
}
);
return base.ApplyTokenResponse(context);
}
}
}
Then you need to make sure each request has the cookie attached to it. You must also write some middleware to intercept the cookie and set it to the header:
public class AuthorizationHeader
{
private readonly RequestDelegate _next;
public AuthorizationHeader(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var authenticationCookieName = "exampleToken";
var cookie = context.Request.Cookies[authenticationCookieName];
if (cookie != null)
{
if (!context.Request.Path.ToString().ToLower().Contains("/account/logout"))
{
if (!string.IsNullOrEmpty(cookie))
{
var token = JsonConvert.DeserializeObject<AccessToken>(cookie);
if (token != null)
{
var headerValue = "Bearer " + token.access_token;
if (context.Request.Headers.ContainsKey("Authorization"))
{
context.Request.Headers["Authorization"] = headerValue;
}else
{
context.Request.Headers.Append("Authorization", headerValue);
}
}
}
await _next.Invoke(context);
}
else
{
// this is a logout request, clear the cookie by making it expire now
context.Response.Cookies.Append(authenticationCookieName,
"",
new Microsoft.AspNetCore.Http.CookieOptions()
{
Path = "/",
HttpOnly = true,
Secure = false,
Expires = DateTime.UtcNow.AddHours(-1)
});
context.Response.Redirect("/");
return;
}
}
else
{
await _next.Invoke(context);
}
}
}
In Configure() of startup.cs:
// use the AuthorizationHeader middleware
app.UseMiddleware<AuthorizationHeader>();
// Add a new middleware validating access tokens.
app.UseOAuthValidation();
You can then use the Authorize attribute normally.
[Authorize(Roles = "Administrator,User")]
This solution works for both api and mvc apps. For ajax and fetch requests however your must write some custom middleware that will not redirect the user to the login page and instead return a 401:
public class RedirectHandler
{
private readonly RequestDelegate _next;
public RedirectHandler(RequestDelegate next)
{
_next = next;
}
public bool IsAjaxRequest(HttpContext context)
{
return context.Request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
public bool IsFetchRequest(HttpContext context)
{
return context.Request.Headers["X-Requested-With"] == "Fetch";
}
public async Task Invoke(HttpContext context)
{
await _next.Invoke(context);
var ajax = IsAjaxRequest(context);
var fetch = IsFetchRequest(context);
if (context.Response.StatusCode == 302 && (ajax || fetch))
{
context.Response.Clear();
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
await context.Response.WriteAsync("Unauthorized");
return;
}
}
}
I implemented the middleware successfully (based on Darxtar answer):
// TokenController.cs
public IActionResult Some()
{
...
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
Response.Cookies.Append(
"x",
tokenString,
new CookieOptions()
{
Path = "/"
}
);
return StatusCode(200, tokenString);
}
// JWTInHeaderMiddleware.cs
public class JWTInHeaderMiddleware
{
private readonly RequestDelegate _next;
public JWTInHeaderMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
var name = "x";
var cookie = context.Request.Cookies[name];
if (cookie != null)
if (!context.Request.Headers.ContainsKey("Authorization"))
context.Request.Headers.Append("Authorization", "Bearer " + cookie);
await _next.Invoke(context);
}
}
// Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMiddleware<JWTInHeaderMiddleware>();
...
}
You can also use Events.OnMessageReceived property of JwtBearerOptions class
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddCookie()
.AddJwtBearer(options =>
{
options.Events = new()
{
OnMessageReceived = context =>
{
var request = context.HttpContext.Request;
var cookies = request.Cookies;
if (cookies.TryGetValue("AccessTokenCookieName",
out var accessTokenValue))
{
context.Token = accessTokenValue;
}
return Task.CompletedTask;
};
};
})

Get Full GenericPrincipal MVC From Web Api

This time I'm triying to set and get the whole information about the user at the FrontSide but I don't know whant i'm doing wrong
I have two separated projects the first one is the Webapi Project and I'm using it to SingIn the user giving then a token.
// GET api/Account/ExternalLogin
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)
{
if (error != null)
return Redirect(Url.Content("~/") + "#error=" + Uri.EscapeDataString(error));
if (!User.Identity.IsAuthenticated)
return new ChallengeResult(provider, this);
ExternalLoginData externalLogin = ExternalLoginData.FromIdentity(User.Identity as ClaimsIdentity);
if (externalLogin == null)
return InternalServerError();
if (externalLogin.LoginProvider != provider)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return new ChallengeResult(provider, this);
}
AppJobSeeker user = await UserManager.FindAsync(new UserLoginInfo(externalLogin.LoginProvider, externalLogin.ProviderKey));
bool hasRegistered = user != null;
if (hasRegistered)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await UserManager.CreateIdentityAsync(user, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await UserManager.CreateIdentityAsync(user, CookieAuthenticationDefaults.AuthenticationType);
AuthenticationProperties properties = ApplicationOAuthProvider.CreateProperties(user.UserName, user.Id);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
}
else
{
IEnumerable<Claim> claims = externalLogin.GetClaims();
ClaimsIdentity identity = new ClaimsIdentity(claims, OAuthDefaults.AuthenticationType);
Authentication.SignIn(identity);
}
return Ok();
}
And the client side is a MVC 5 Project where I have one method to postasyn the authentication and another one to Create the AuthTickect like this...
public async Task<T> AuthenticateAsync<T>(string userName, string password)
{
using (var client = new HttpClient())
{
var result = await client.PostAsync((#"http://localhost:8060/Token"), new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(#"grant_type", #"password"),
new KeyValuePair<string, string>(#"userName", userName),
new KeyValuePair<string, string>(#"password", password)
}));
string json = await result.Content.ReadAsStringAsync();
if (result.IsSuccessStatusCode)
return JsonConvert.DeserializeObject<T>(json);
throw new ApiException(result.StatusCode, json);
}
}
private void CreateTicket(SignInResult result, SignInModel model, string returnUrl)
{
//Let's keep the user authenticated in the MVC webapp.
//By using the AccessToken, we can use User.Identity.Name in the MVC controllers to make API calls.
FormsAuthentication.SetAuthCookie(result.AccessToken, model.RememberMe);
//Create an AuthenticationTicket to generate a cookie used to authenticate against Web API.
//But before we can do that, we need a ClaimsIdentity that can be authenticated in Web API.
Claim[] claims =
{
new Claim(ClaimTypes.Name, result.AccessToken), //Name is the default name claim type, and UserName is the one known also in Web API.
new Claim(ClaimTypes.Email, result.UserName), //If you want to use User.Identity.GetUserId in Web API, you need a NameIdentifier claim.
};
//Generate a new ClaimsIdentity, using the DefaultAuthenticationTypes.ApplicationCookie authenticationType.
//This also matches what we've set up in Web API.
AuthenticationTicket authTicket = new AuthenticationTicket(new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie), new AuthenticationProperties
{
ExpiresUtc = result.Expires,
IsPersistent = model.RememberMe,
IssuedUtc = result.Issued,
RedirectUri = returnUrl,
});
//HttpContext.Response..User = principal;
//And now it's time to generate the cookie data. This is using the same code that is being used by the CookieAuthenticationMiddleware class in OWIN.
byte[] userData = DataSerializers.Ticket.Serialize(authTicket);
//Protect this user data and add the extra properties. These need to be the same as in Web API!
byte[] protectedData = MachineKey.Protect(userData, new[] { "Microsoft.Owin.Security.Cookies.CookieAuthenticationMiddleware", DefaultAuthenticationTypes.ApplicationCookie, "v1" });
//base64-encode this data.
string protectedText = TextEncodings.Base64Url.Encode(protectedData);
//And now, we have the cookie.
Response.SetCookie(new HttpCookie("JobSeekerAuth")
{
HttpOnly = true,
Expires = result.Expires.UtcDateTime,
Value = protectedText,
});
}
And my login method looks like
// POST: Account/SignIn
[HttpPost]
public async Task<ActionResult> Login(SignInModel model, string returnUrl)
{
if (!ModelState.IsValid)
return View(model);
try
{
CreateTicket(await WebApiService.Instance.AuthenticateAsync<SignInResult>(model.Email, model.Password), model, returnUrl);
return RedirectToLocal(returnUrl);
//return await WebApiService.Instance.AuthenticateAsync<SignInResult>(model.Email, model.Password) != null ? RedirectToLocal(returnUrl) : RedirectToLocal(returnUrl);
}
catch (ApiException ex)
{
//No 200 OK result, what went wrong?
HandleBadRequest(ex);
if (!ModelState.IsValid)
return View(model);
throw;
}
}
The problem is I want to use the GenericPrincipal at the Razor View two get the userId or the username fro the logged user and when I trying to doing so It gives me nothing more than the token here
#if (HttpContext.Current.User.Identity.IsAuthenticated)
{
<li>#Html.ActionLink("Sign Out", "SignOut", "Account")</li>
}
else
{...
So, I don't know how to get this goal
Best regards!...
My Authentication Method Works Well because once I go to the login method this one gives me my entity SignInResult wich looks like with all its value setted
[JsonProperty("access_token")]
public string AccessToken { get; set; }
//Included to show all the available properties, but unused in this sample
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public uint ExpiresIn { get; set; }
[JsonProperty("userName")]
public string UserName { get; set; }
[JsonProperty(".issued")]
public DateTimeOffset Issued { get; set; }
[JsonProperty(".expires")]
public DateTimeOffset Expires { get; set; }
[JsonProperty("userId")]
public string UserId { get; set; }
I also tried to set to the Thread.CurrentPrincipal but not success
Best Regards

How use AuthorizationFilterAttribute in WebApi with WebClient Library?

I use the following code for Authorization (I found it in internet and change it for my use)
when i call my url seems authorization works
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class ClientAuthorizationAttribute : AuthorizationFilterAttribute
{
private bool _active = true;
public ClientAuthorizationAttribute()
{
}
public ClientAuthorizationAttribute(bool active)
{
_active = active;
}
public override void OnAuthorization(HttpActionContext actionContext)
{
if (_active)
{
var identity = ParseAuthorizationHeader(actionContext);
if (identity == null)
{
Challenge(actionContext);
return;
}
if (!OnAuthorizeUser(identity.Name, identity.Password, actionContext))
{
Challenge(actionContext);
return;
}
var principal = new GenericPrincipal(identity, null);
Thread.CurrentPrincipal = principal;
base.OnAuthorization(actionContext);
}
}
protected virtual bool OnAuthorizeUser(string clientId, string authId, HttpActionContext actionContext)
{
return false;
}
protected virtual ClientAuthenticationIdentity ParseAuthorizationHeader(HttpActionContext actionContext)
{
string authHeader = null;
var auth = actionContext.Request.Headers.Authorization;
if (auth != null && auth.Scheme == "Basic")
authHeader = auth.Parameter;
if (string.IsNullOrEmpty(authHeader))
return null;
authHeader = Encoding.UTF8.GetString(Convert.FromBase64String(authHeader));
var tokens = authHeader.Split(':');
if (tokens.Length < 2)
return null;
return new ClientAuthenticationIdentity(tokens[0], tokens[1]);
}
void Challenge(HttpActionContext actionContext)
{
var host = actionContext.Request.RequestUri.DnsSafeHost;
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
actionContext.Response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", host));
}
}
public class ClientAuthenticationIdentity : GenericIdentity
{
public ClientAuthenticationIdentity(string name, string password)
: base(name, "Basic")
{
Password = password;
}
public string Password { get; set; }
}
public class BasicAuthorizationAttribute : ClientAuthorizationAttribute
{
public BasicAuthorizationAttribute()
{ }
public BasicAuthorizationAttribute(bool active)
: base(active)
{ }
protected override bool OnAuthorizeUser(string clientId, string authId, HttpActionContext actionContext)
{
var businness = new WebServiceAuthBusiness();
return businness.Count(x => x.ClientID == clientId && x.AuthenticateID == authId) > 0;
}
}
}
in Client I use WebClient for Get application data (Does not work)
[BasicAuthorization]
public IList<Application> Get()
{
using (var client = new WebClient())
{
client.BaseAddress = _baseAddress;
client.Encoding = Encoding.UTF8;
client.UseDefaultCredentials = true; ???
client.Credentials = new NetworkCredential(clientId, authId); ???
var str = client.DownloadString("api/application/get");
return JsonConvert.DeserializeObject<List<Application>>(str);
}
}
How i can send username and password with webClient for AuthorizationFilter ???
As described on c# corner:
Add BasicAuthenticationAttribute.cs class in your solution.
With following code
public class BasicAuthenticationAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
try
{
if (actionContext.Request.Headers.Authorization == null)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
else
{
// Gets header parameters
string authenticationString = actionContext.Request.Headers.Authorization.Parameter;
string originalString = Encoding.UTF8.GetString(Convert.FromBase64String(authenticationString));
// Gets username and password
string usrename = originalString.Split(':')[0];
string password = originalString.Split(':')[1];
AuthsController auth = new AuthsController();
// Validate username and password
if (!auth.ValidateUser(usrename, password))
{
// returns unauthorized error
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
base.OnAuthorization(actionContext);
}
// Handling Authorize: Basic <base64(username:password)> format.
catch(Exception e)
{
actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
}
}
}
In AuthsController.cs(Entity Framework)
Add
[NonAction]
public bool ValidateUser(string userName, string password)
{
// Check if it is valid credential
var queryable = db.Auths
.Where(x => x.Name == userName)
.Where(x => x.Password == password);
if (queryable != null)
{
return true;
}
else
{
return false;
}
}
In WebApiConfig.cs
Add
config.Filters.Add(new BasicAuthenticationAttribute());
In Your controller that requires Basic Authorization.
Add
[BasicAuthentication]
public class YourController : ApiController{.......}
Basic authentication requires Authorization header to be set:
using (var client = new WebClient())
{
var credential = String.Format("{0}:{1}", userName, password);
var encodedCredential = Convert.ToBase64String(Encoding.UTF8.GetBytes(credential))
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", encodedCredential);
// ...
}
You should be able to send User name and encrypted Password as part of GET api URL.
/api/application/Get?user=''&pw=''
Your AuthorizationFilter should be able to parse them from RequestUri but of course you never want to do that, instead you might need to implement OAuth Token style access token send along with your API. Basically your user will use login panel and POST through https the login details and receive a token and then every time he or she makes request will send the access token along with that api like this:
/api/application/Get?access_token=""
This access token might expire after certain period of time or rate limitation.
You can find an implementation here:
http://www.asp.net/web-api/overview/security/individual-accounts-in-web-api

Categories