ASP.NET Core - getting a message from AuthenticateResult.Fail - c#

Can not get an error message in HTTP response.
if (!string.IsNullOrWhiteSpace(checkTokenResponse.Error))
{
Logger.LogError(checkTokenResponse.Error);
return AuthenticateResult.Fail(checkTokenResponse.Error);
}
response
I suppose the error message should render into data response field
The same question ASP.NET Core - getting a message back from AuthenticationHandler
Update
code of custom Auth handler
public class AuthHandler : AuthenticationHandler<AuthOptions>
{
private readonly IPrimeApiProxy _primeApiProxy;
private readonly IPrimeProxy _primeProxy;
private readonly int _adminGroupId;
private static readonly string HeaderName = "Authorization";
private static readonly string ApiAuthScheme = "oauth ";
public AuthHandler(
IOptionsMonitor<AuthOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock,
IPrimeApiProxy primeApiProxy,
IPrimeProxy primeProxy,
ApplicationSettingsProvider settings)
: base(options, logger, encoder, clock)
{
_primeApiProxy = primeApiProxy ?? throw new ArgumentNullException(nameof(primeApiProxy));
_primeProxy = primeProxy ?? throw new ArgumentNullException(nameof(primeProxy));
_adminGroupId = int.Parse((settings as dynamic).AdminGroupId as string);
}
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
Logger.LogTrace("HandleAuthenticateAsync");
if (!Request.Headers.ContainsKey("Authorization"))
{
return AuthenticateResult.Fail("Missing Authorization Header");
}
try
{
var token = FetchToken(Request);
if (string.IsNullOrWhiteSpace(token.token) && token.userId < 1)
{
Logger.LogError("Invalid token");
return AuthenticateResult.Fail("Invalid token");
}
var checkTokenResponse = await _primeProxy.CheckToken(token.token);
if (!string.IsNullOrWhiteSpace(checkTokenResponse.Error))
{
Logger.LogError(checkTokenResponse.Error);
return AuthenticateResult.Fail(checkTokenResponse.Error);
}
var isValidInt = int.TryParse(checkTokenResponse.UserId, out var userId);
if (!isValidInt)
{
return AuthenticateResult.Fail("User Id is invalid");
}
if (token.userId != userId)
{
return AuthenticateResult.Fail("The token belongs to another user");
}
bool isUserAdminAndCustomGroupMember = false;
if (checkTokenResponse.UserRole == "admin")
{
isUserAdminAndCustomGroupMember = await _primeApiProxy.IsGroupMember(token.token, token.userId, _adminGroupId);
}
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, token.userId.ToString()),
new Claim(ClaimTypes.Role, isUserAdminAndCustomGroupMember ? "Admin" : "Learner")
};
var identity = new ClaimsIdentity(claims, Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
catch (Exception ex)
{
Logger.LogError(ex, "Auth exception");
return AuthenticateResult.Fail("Invalid Authorization Header");
}
}
private static (string token, int userId) FetchToken(HttpRequest request)
{
string authHeader = request.Headers[HeaderName];
if (authHeader != null && authHeader.StartsWith(ApiAuthScheme, StringComparison.OrdinalIgnoreCase))
{
string token = authHeader.Substring(ApiAuthScheme.Length).Trim();
string[] parts = token.Split(',', StringSplitOptions.RemoveEmptyEntries);
if (int.TryParse(parts[1], out int userId))
{
return (parts[0], userId);
}
}
return (null, 0);
}
}

Related

Receiving 400 Bad Request when requesting an access_token with OAuth Authorization Code Flow in C#?

Following Owin OAuth guide implemented the authorization code flow below. Am successfully receiving the authorization code from server, ie. till step D mentioned in the article.
When I request access_token from the server with grant_type=authorization_code. I am getting 400 Bad Request error saying invalid grant
Here's the code:
Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
// Configure the db context and user manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
//use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(Microsoft.AspNet.Identity.DefaultAuthenticationTypes.ExternalCookie);
OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
AuthorizeEndpointPath = new PathString("/oauth/authorize"),
TokenEndpointPath = new PathString("/oauth/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(60),
Provider = new SimpleOAuthProvider(),
AuthorizationCodeProvider = new AuthenticationCodeProvider(),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(OAuthBearerOptions);
// Uncomment the following lines to enable logging in with third party login providers
//app.UseMicrosoftAccountAuthentication(
// clientId: "",
// clientSecret: "");
//app.UseTwitterAuthentication(
// consumerKey: "",
// consumerSecret: "");
//app.UseFacebookAuthentication(
// appId: "",
// appSecret: "");
//app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
//{
// ClientId = "",
// ClientSecret = ""
//});
}
AuthorizationCodeProvider
internal class AuthenticationCodeProvider: IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var code = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
{
var token = new AuthCode()
{
Token = HashProvider.GetHash(code),
IssuedUtc = DateTime.UtcNow
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddAuthCodeToken(token);
if (result)
{
context.SetToken(token.Token);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
if (headerPresent)
{
context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
}
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
string hashedToken = context.Token;
hashedToken = Uri.UnescapeDataString(hashedToken);
using (AuthRepository _repo = new AuthRepository())
{
var authCode = await _repo.FindAuthCodeToken(hashedToken);
if (authCode != null)
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(authCode.ProtectedTicket);
var result = await _repo.RemoveAuthCodeToken(hashedToken);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
OAuthAuthorizationServerProvider
public class SimpleOAuthProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
//Check if client details are valid
string clientId = string.Empty;
string clientSecret = string.Empty;
AuthClient client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
if (client.Type == (Int16)ClientTypeEnum.Native)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != clientSecret)
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
if (headerPresent)
{
context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
}
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password);
using (AuthRepository _repo = new AuthRepository())
{
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
if (!user.EmailConfirmed)
{
context.SetError("unconfirmed_email", "Please confirm your email id.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("FullName", user.FullName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"session_id", Guid.NewGuid().ToString("n")
},
{
"email", user.Email
},
{
"user_id", user.Id.ToString()
},
{
"fullname", user.FullName.ToString()
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
{
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
//var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
//if (newClaim != null)
//{
// newIdentity.RemoveClaim(newClaim);
//}
//newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
//Authorization Code flow
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
string clientId = context.ClientId;
AuthClient client = null;
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
context.Validated(context.RedirectUri);
return Task.FromResult<object>(null);
}
public override async Task GrantClientCredentials(OAuthGrantClientCredentialsContext context)
{
var identity = new ClaimsIdentity(new GenericIdentity(
context.ClientId, OAuthDefaults.AuthenticationType),
context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
context.Validated(identity);
}
public override async Task AuthorizeEndpoint(OAuthAuthorizeEndpointContext context)
{
//Generate auth code to be sent to oauth/token endpoint for getting access_token
if (context.AuthorizeRequest.IsImplicitGrantType)
{
var identity = new ClaimsIdentity("Bearer");
context.OwinContext.Authentication.SignIn(identity);
context.RequestCompleted();
}
else if (context.AuthorizeRequest.IsAuthorizationCodeGrantType)
{
var redirectUri = context.Request.Query["redirect_uri"];
var clientId = context.Request.Query["client_id"];
var userId = context.Request.Query["user_id"];
var state = context.Request.Query["state"];
var scope = context.Request.Query["scope"];
var sessionId = Guid.NewGuid().ToString("n");
var identity = new ClaimsIdentity(new GenericIdentity(
clientId, OAuthDefaults.AuthenticationType));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, userId.ToString()));
var authorizeCodeContext = new AuthenticationTokenCreateContext(
context.OwinContext,
context.Options.AuthorizationCodeFormat,
new AuthenticationTicket(
identity,
new AuthenticationProperties(new Dictionary<string, string>
{
{"client_id", clientId},
{"user_id",userId },
{"session_id", sessionId },
{"redirect_uri", redirectUri}
})
{
IssuedUtc = DateTimeOffset.UtcNow,
ExpiresUtc = DateTimeOffset.UtcNow.Add(context.Options.AuthorizationCodeExpireTimeSpan)
}));
await context.Options.AuthorizationCodeProvider.CreateAsync(authorizeCodeContext);
//Set Cors
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
if (headerPresent)
{
context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
}
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
var model = new { RedirectUri = redirectUri + "?code=" + Uri.EscapeDataString(authorizeCodeContext.Token) + "&state=" + state + "&scope=" + scope };
var json = Newtonsoft.Json.JsonConvert.SerializeObject(model);
context.Response.Write(json);
context.RequestCompleted();
}
}
/// <summary>
/// Verify the request for authorization_code
/// </summary>
public override async Task ValidateAuthorizeRequest(OAuthValidateAuthorizeRequestContext context)
{
if ((context.AuthorizeRequest.IsAuthorizationCodeGrantType || context.AuthorizeRequest.IsImplicitGrantType))
{
context.Validated();
}
else
{
context.Rejected();
}
}
//public override async Task GrantAuthorizationCode(OAuthGrantAuthorizationCodeContext context)
//{
// var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
// if (allowedOrigin == null) allowedOrigin = "*";
// var headerPresent = context.OwinContext.Response.Headers.Any(x => x.Key == "Access-Control-Allow-Origin");
// if (headerPresent)
// {
// context.OwinContext.Response.Headers.Remove("Access-Control-Allow-Origin");
// }
// context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
// var userIdTemp = context.Ticket.Identity.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier);
// if(userIdTemp == null)
// {
// }
// long userId = Convert.ToInt64(userIdTemp.Value);
// var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
// ApplicationUser user = await userManager.FindByIdAsync(userId);
// var identity = new ClaimsIdentity(context.Options.AuthenticationType);
// identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
// identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
// identity.AddClaim(new Claim("FullName", user.FullName));
// identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
// //identity.AddClaim(new Claim("sub", context.UserName));
// var props = new AuthenticationProperties(new Dictionary<string, string>
// {
// //{
// // "client_id", (context.Ticket.Properties.Dictionary.FirstOrDefault.ClientId == null) ? string.Empty : context.ClientId
// //},
// {
// "session_id", Guid.NewGuid().ToString("n")
// },
// {
// "email", user.Email
// },
// {
// "user_id", user.Id.ToString()
// },
// {
// "fullname", user.FullName.ToString()
// }
// });
// var ticket = new AuthenticationTicket(identity, props);
// context.Validated(ticket);
//}
}
It seems the middleware will check if the key redirect_uri exists in the dictionary of AuthenticationProperties, remove it and everything works fine(with validated context).
A simplified example of AuthorizationCodeProvider woubld be like so:
public class AuthorizationCodeProvider:AuthenticationTokenProvider {
public override void Create(AuthenticationTokenCreateContext context) {
context.SetToken(context.SerializeTicket());
}
public override void Receive(AuthenticationTokenReceiveContext context) {
context.DeserializeTicket(context.Token);
context.Ticket.Properties.Dictionary.Remove("redirect_uri"); // <-
}
}
And don't forget to make the context validated in the overridden method OAuthAuthorizationServerProvider.ValidateClientAuthentication. Again, here's a simplified example which inherit from the ApplicationOAuthProvider class of the template project:
public partial class DefaultOAuthProvider:ApplicationOAuthProvider {
public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context) {
if(null!=context.RedirectUri) {
context.Validated(context.RedirectUri);
return Task.CompletedTask;
}
return base.ValidateClientRedirectUri(context);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) {
if(context.TryGetFormCredentials(out String clientId, out String clientSecret)) {
// Specify the actual expected client id and secret in your case
if(("expected-clientId"==clientId)&&("expected-clientSecret"==clientSecret)) {
context.Validated(); // <-
return Task.CompletedTask;
}
}
return base.ValidateClientAuthentication(context);
}
public DefaultOAuthProvider(String publicClientId) : base(publicClientId) {
}
}
Note that if you invoke context.Validated with a particular client id, then you will have to put the same client_id in the properties of the ticket, you can do that with the method AuthenticationTokenProvider.Receive

Handle AuthorizeAttribute answer

I did create custom AuthorizeAttribute that should handle Jwt Bearer token but the question is - now I'm receiving all answers, including 200 and 401 within Ok status, how I should change it in order to receive proper http status code? Here is how AuthorizeAttribute look like:
public class JwtAuthorizeAttribute : AuthorizeAttribute
{
private readonly string role;
public JwtAuthorizeAttribute()
{
}
public JwtAuthorizeAttribute(string role)
{
this.role = role;
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var jwtToken = new JwtToken();
string json = String.Empty;
var ctx = actionContext.Request.GetRequestContext();
if (ctx.Principal.Identity.IsAuthenticated) return true;
if (actionContext.Request.Headers.Contains("Authorization"))
{
try
{
IJsonSerializer serializer = new JsonNetSerializer();
IDateTimeProvider provider = new UtcDateTimeProvider();
IJwtValidator validator = new JwtValidator(serializer, provider);
IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder();
IJwtDecoder decoder = new JwtDecoder(serializer, validator, urlEncoder);
json = decoder.Decode(actionContext.Request.Headers.Authorization.Parameter, SiteGlobal.Secret, verify: true);
jwtToken = JsonConvert.DeserializeObject<JwtToken>(json);
if (jwtToken.aud != SiteGlobal.Audience || jwtToken.iss != SiteGlobal.Issuer || role != jwtToken.role)
{
return false;
}
}
catch (TokenExpiredException)
{
return false;
}
catch (SignatureVerificationException)
{
return false;
}
}
else
{
return false;
}
var identity = new ClaimsIdentity("JWT");
identity.AddClaim(new Claim(ClaimTypes.Name, jwtToken.unique_name));
identity.AddClaim(new Claim(ClaimTypes.Role, jwtToken.role));
identity.AddClaim(new Claim("user_id", jwtToken.user_id.ToString()));
actionContext.Request.GetRequestContext().Principal = new ClaimsPrincipal(identity);
return true;
}
}
This is how controller look like:
[JwtAuthorize("Admin")]
[HttpGet]
[ResponseType(typeof(CatalogueListDto))]
public async Task<IHttpActionResult> Get()
{
var result = await _catalogueService.GetCatalogues();
if (result == null) return BadRequest(ActionAnswer.Failed.CatalogueNotFound);
return Ok(result);
}
i have a similar attribute by following some tutorials in the past. If authorized, i'm not returning true, but i return this:
return base.IsAuthorized(actionContext);
Maybe it's worth a check if this returns you the correct status code.

IdentityServer4 with EF6

I've already implemented the basic Web API protection via IdentityServer4 based on this.
The demo is based on in-memory data. And most of tutorials are based on EF Core implementation for user data. As I searched there was a IUserService in IdentityServer3 which is now missing in version 4.
builder.AddInMemoryClients(Clients.Get());
builder.AddInMemoryScopes(Scopes.Get());
builder.AddInMemoryUsers(Users.Get());
How can I retrieve my user data from an EF6 store?
In Startup.cs, do this
builder.Services.AddTransient<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();
builder.Services.AddTransient<IProfileService, ProfileService>();
Here is a sample of ResourceOwnerPasswordValidator and ProfileService
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private MyUserManager _myUserService { get; set; }
public ResourceOwnerPasswordValidator()
{
_myUserService = new MyUserManager();
}
public async Task<CustomGrantValidationResult> ValidateAsync(string userName, string password, ValidatedTokenRequest request)
{
var user = await _myUserService.FindByNameAsync(userName);
if (user != null && await _myUserService.CheckPasswordAsync(user, password))
{
return new CustomGrantValidationResult(user.EmailAddress, "password");
}
return new CustomGrantValidationResult("Invalid username or password");
}
}
public class ProfileService : IProfileService
{
MyUserManager _myUserManager;
public ProfileService()
{
_myUserManager = new MyUserManager();
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.FindFirst("sub")?.Value;
if (sub != null)
{
var user = await _myUserManager.FindByIdAsync(sub);
var cp = await getClaims(user);
var claims = cp.Claims;
if (context.AllClaimsRequested == false ||
(context.RequestedClaimTypes != null && context.RequestedClaimTypes.Any()))
{
claims = claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToArray().AsEnumerable();
}
context.IssuedClaims = claims;
}
}
public Task IsActiveAsync(IsActiveContext context)
{
return Task.FromResult(0);
}
private async Task<ClaimsPrincipal> getClaims(CustomerSite user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var userId = await _myUserManager.GetUserIdAsync(user);
var userName = await _myUserManager.GetUserNameAsync(user);
var id = new ClaimsIdentity();
id.AddClaim(new Claim(JwtClaimTypes.Id, userId));
id.AddClaim(new Claim(JwtClaimTypes.PreferredUserName, userName));
var roles = await _myUserManager.GetRolesAsync(user);
foreach (var roleName in roles)
{
id.AddClaim(new Claim(JwtClaimTypes.Role, roleName));
}
id.AddClaims(await _myUserManager.GetClaimsAsync(user));
return new ClaimsPrincipal(id);
}
}

How to add custom exception/errors?

I am would like to add errors or exception, which can show up on the cliet-side, when the user leaves the username/password blank or press 'cancel' on the login dialog. Currently, it shows a blank screen to those exception.
public class BasicAuthHandler : DelegatingHandler
{
private const string BasicAuthResponseHeader = "WWW-Authenticate";
private const string BasicAuthResponseHeaderValue = "Basic";
public BasicAuthHandler(iUser repository)
{
this.repository = repository;
}
[Inject]
iUser repository { get; set; }
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
AuthenticationHeaderValue authValue = request.Headers.Authorization;
if (authValue == null || authValue.Scheme != BasicAuthResponseHeaderValue)
{
return Unauthorized(request);
}
string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authValue.Parameter)).Split(new[] { ':' });
if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
{
return Unauthorized(request);
}
api_login user = repository.Validate2(credentials[0], credentials[1]);
if (user == null)
{
return Unauthorized(request);
}
string[] roles = new[] { "Users", "Testers" };
IPrincipal principal = new GenericPrincipal(new GenericIdentity(user.username, BasicAuthResponseHeaderValue), roles);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
return base.SendAsync(request, cancellationToken);
}
private Task<HttpResponseMessage> Unauthorized(HttpRequestMessage request)
{
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
var task = new TaskCompletionSource<HttpResponseMessage>();
task.SetResult(response);
return task.Task;
}
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],
};
}
Updated error code:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
AuthenticationHeaderValue authValue = request.Headers.Authorization;
if (authValue == null || authValue.Scheme != BasicAuthResponseHeaderValue)
{
return Unauthorized(request);
}
string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authValue.Parameter)).Split(new[] { ':' });
if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
{
//return Unauthorized(request);
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("access denied")),
};
}
api_login user = repository.auth(credentials[0], credentials[1]);
if (user == null)
{
//return Unauthorized(request);
//return request.CreateErrorResponse(HttpStatusCode.NotFound, "If not member, please sign in using:");
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("access denied")),
};
}
var roles = repository.GetRolesForUser(user.username);
IPrincipal principal = new GenericPrincipal(new GenericIdentity(user.username, BasicAuthResponseHeaderValue), roles);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
return base.SendAsync(request, cancellationToken)
.ContinueWith(task =>
{
var response = task.Result;
if (response.StatusCode == HttpStatusCode.Unauthorized
&& !response.Headers.Contains(BasicAuthResponseHeader))
{
// redirect to some log in page?
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("access denied")),
};
}
return response;
});
}
I am not sure on the right approach to go about adding exception to the code, I have added couple of error codes in the code above but whenever i press cancel on login dialog, it goes to blank screen instead of showing the following error messages.
Any help would be very much appreciated.
Thank you

authorize error from authorize handler class

I cannot seem to login when I call -- api/values. the client-end throws "Authorization has been denied for this request." message.
I tried debugging the basicAuthHandler class but it does not seem to be crashing anywhere, so I am little stuck and how can I pin point the issue.
could it be my validate method or constructor in my global.aspx?
public class BasicAuthMessageHandler : DelegatingHandler
{
private const string BasicAuthResponseHeader = "WWW-Authenticate";
private const string BasicAuthResponseHeaderValue = "Basic";
//[Inject]
//public iUser Repository { get; set; }
// private readonly iUser Repository;
private readonly iUser Repository = new User();
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;
//request.GetRequestContext().Principal = 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.username != 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), null);
}
principal = null;
return false;
}
}
}
global.aspx:
GlobalConfiguration.Configuration.MessageHandlers.Add(new BasicAuthMessageHandler());
Any help would be very much appreciated.
Thank you.
I think you didn't handle the response correctly in your code, I created a MessageHandler for Basic Authentication base on your code, hope it'll give you an good idea (I didn't test it), see below:
public class BasicAuthMessageHandler : DelegatingHandler
{
private const string BasicAuthResponseHeader = "WWW-Authenticate";
private const string BasicAuthResponseHeaderValue = "Basic";
//[Inject]
//public iUser Repository { get; set; }
// private readonly iUser Repository;
private readonly iUser Repository = new User();
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
AuthenticationHeaderValue authValue = request.Headers.Authorization;
if (authValue == null || authValue.Scheme != BasicAuthResponseHeaderValue)
{
return Unauthorized(request);
}
string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authValue.Parameter)).Split(new[] { ':' });
if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
{
return Unauthorized(request);
}
api_login user = Repository.Validate2(credentials[0], credentials[1]);
if (user == null)
{
return Unauthorized(request);
}
IPrincipal principal = new GenericPrincipal(new GenericIdentity(user.username, BasicAuthResponseHeaderValue), null);
Thread.CurrentPrincipal = principal;
HttpContext.Current.User = principal;
return base.SendAsync(request, cancellationToken);
}
private Task<HttpResponseMessage> Unauthorized(HttpRequestMessage request)
{
var response = request.CreateResponse(HttpStatusCode.Unauthorized);
response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
var task = new TaskCompletionSource<HttpResponseMessage>();
task.SetResult(response);
return task.Task;
}
}

Categories

Resources