How to get the detailed error in web api Authentication Failed scenario instead of "Message": "Authorization has been denied for this request."? - asp.net-web-api2

I created a web api from VS template with a Values Controller decorated with [Authorize]. I have added Owin Startup as below:
[assembly: OwinStartup(typeof(WebAPIDotNet.Startup))]
namespace WebAPIDotNet
{
public class Startup
{
private static string clientId = "xxxxxxxxx"; // my actual client id from azure ad.
public void Configuration(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
{
AccessTokenFormat = new JwtFormat(
new TokenValidationParameters
{
// Check if the audience is intended to be this application
ValidAudiences = new[] { clientId, $"api://{clientId}" },
// Change below to 'true' if you want this Web API to accept tokens issued to one Azure AD tenant only (single-tenant)
// Note that this is a simplification for the quickstart here. You should validate the issuer. For details,
// see https://github.com/Azure-Samples/active-directory-dotnet-native-aspnetcore
ValidateIssuer = false,
}
//new OpenIdConnectSecurityTokenProvider("https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration")
),
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Active,
// Below Provider is just so that I can capture detailed errors while debugging, but no success
Provider = new OAuthBearerAuthenticationProvider
{
OnRequestToken = Onrequesttoken,
OnApplyChallenge = applyuingChallenge,
OnValidateIdentity = validatingidentiity
},
});
}
private Task validatingidentiity(OAuthValidateIdentityContext arg)
{
return Task.FromResult(0);
}
private Task applyuingChallenge(OAuthChallengeContext arg)
{
return Task.FromResult(0);
}
private Task Onrequesttoken(OAuthRequestTokenContext arg)
{
return Task.FromResult(0);
}
}
}
I have an access token already, and I have validated that it has the correct audience, etc. But making a GET request with Postman keeps giving me the msg - "Message": "Authorization has been denied for this request."
When i debug I can see the token as part of the Authorization, is there any way i can log/see actual error rather than "Message": "Authorization has been denied for this request." ?
Postman request:

Just in case someone else runs into this, I found the answer in Owin Katana docs. Essentially placing below config in your web.config will enable logs in the output window with details and I was able to see the underlying reason of failed authorization since I am using the Owin auth middleware.
<system.diagnostics>
<switches>
<add name="Microsoft.Owin" value="Verbose" />
</switches>
</system.diagnostics>

Related

Blazor Web Assembly App with Azure B2C is always trying to authenticate as soon as page is loaded

I am adding support for Azure AD B2C to a Blazor WebAssembly App, I followed the instructions here
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-azure-active-directory-b2c?view=aspnetcore-3.1#client-app-configuration
however, the application is always trying to authenticate as soon as I load the page,
which does not allow for a public anonymous section of the site.
Is there any solution to this problem?
The default httpClient requires authorization so even making a call to see if a person is authorized causes the code to prompt the user to log in kicks in.
So to get around this, in the Program.cs file (in the Client project), I created a httpClient that allows anonymous requests
// This allows anonymous requests
// See: https://learn.microsoft.com/en-us/aspnet/core/security/blazor/webassembly/additional-scenarios?view=aspnetcore-3.1#unauthenticated-or-unauthorized-web-api-requests-in-an-app-with-a-secure-default-client
builder.Services.AddHttpClient("ServerAPI.NoAuthenticationClient", client => client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress));
This example should help:
https://github.com/ADefWebserver/SyncfusionHelpDeskClient/blob/main/Client/Pages/Index.razor
It calls the NoAuthenticationClient httpClient
protected override void OnInitialized()
{
// Create a httpClient to use for non-authenticated calls
NoAuthenticationClient =
ClientFactory.CreateClient(
"ServerAPI.NoAuthenticationClient");
}
public async Task HandleValidSubmit(EditContext context)
{
try
{
// Save the new Help Desk Ticket
// Create a new GUID for this Help Desk Ticket
objHelpDeskTicket.TicketGuid =
System.Guid.NewGuid().ToString();
await NoAuthenticationClient.PostAsJsonAsync(
"SyncfusionHelpDesk", objHelpDeskTicket);
// Send Email
HelpDeskEmail objHelpDeskEmail = new HelpDeskEmail();
objHelpDeskEmail.EmailType = "Help Desk Ticket Created";
objHelpDeskEmail.EmailAddress = "";
objHelpDeskEmail.TicketGuid = objHelpDeskTicket.TicketGuid;
await NoAuthenticationClient.PostAsJsonAsync(
"Email", objHelpDeskEmail);
// Clear the form
objHelpDeskTicket = new HelpDeskTicket();
// Show the Toast
ToastContent = "Saved!";
StateHasChanged();
await this.ToastObj.Show();
}
catch (Exception ex)
{
ToastContent = ex.Message;
StateHasChanged();
await this.ToastObj.Show();
}
}

Sustainsys Saml2 Handler AuthenticateAsync() method operation is not implemented

I'm trying a simple implementation in my Asp net Core application of Saml2 to integrate with an Ad FS server. I can't figure why I am getting this error. I downloaded the samples from the gitHub and tried to adapt it in my application.
NotImplementedException: The method or operation is not implemented.
Sustainsys.Saml2.AspNetCore2.Saml2Handler.AuthenticateAsync()
Here's my implementation, my application is running on Asp Net Core
On StartUp
services
.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = Saml2Defaults.Scheme;
})
.AddSaml2(options =>
{
options.SPOptions.EntityId = new EntityId("http://myAdfsServer.myDomain.com/adfs/services/trust");
options.SPOptions.ReturnUrl = new Uri("https://localhost:5000");
options.IdentityProviders.Add(
new IdentityProvider(new EntityId("http://myAdfsServer.myDomain.com/adfs/services/trust"), options.SPOptions)
{
LoadMetadata = true,
MetadataLocation = "https://myAdfsServer.myDomain.com/FederationMetadata/2007-06/FederationMetadata.xml"
//MetadataLocation = "FederationMetadata.xml"
});
//options.SPOptions.ServiceCertificates.Add(new X509Certificate2(certificate.ToString()));
})
.AddCookie();
On my Controller
trying something similar to Sustainsys SAML2 Sample for ASP.NET Core WebAPI without Identity
[Authorize(AuthenticationSchemes = Saml2Defaults.Scheme)]
public class AuthenticationController : Controller
{
public AuthenticationController()
{
}
[AllowAnonymous]
public async Task LoginAdfs()
{
string redirectUri = string.Concat("https://localhost:5000", "/verifyAdfs");
try
{
new ChallengeResult(
Saml2Defaults.Scheme,
new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(LoginCallback), new { redirectUri })
});
}catch(Exception e)
{
}
}
[AllowAnonymous]
public async Task<IActionResult> LoginCallback(string returnUrl)
{
var authenticateResult = await HttpContext.AuthenticateAsync(Saml2Defaults.Scheme);
//_log.Information("Authenticate result: {#authenticateResult}", authenticateResult);
// I get false here and no information on claims etc.
if (!authenticateResult.Succeeded)
{
return Unauthorized();
}
var claimsIdentity = new ClaimsIdentity("Email");
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
// _log.Information("Logged in user with following claims: {#Claims}", authenticateResult.Principal.Claims);
await HttpContext.SignInAsync("Email", new ClaimsPrincipal(claimsIdentity));
return LocalRedirect(returnUrl);
}
}
note: I've got a client that won't expose his MetaData in a URL, so I'll need to adapt it and set manually the metadata parameters
I'm stuck in this error, I does not even hit my method LoginAdfs.
The Saml2 handler cannot be used as an authencation scheme, it is a challenge scheme.
I guess that the LoginAdfs() method works fine, but that it's the LoginCallback that fails. The reason should be the call to HttpContext.AuthenticationAsync(Saml2Defaults.Scheme).
You should instead authenticate with the cookie scheme - because that's what keeps the session. Internally when the challenge is completed, the Saml2 handler will use the DefaultSignInScheme to preserve the result in a session (through a cookie, as that's the default sign in scheme).

REST api cacheablity with authorization

I'm building a protected api for a web application.
for each web service call client sends an access token.
when call for a resource depending on the access token it returns different responses.
ex:- call to /employees will return accessible employees only. accessibility will be defined for each access token.
my question is how it's possible to cache the response if it's returned different things depend on the access token.
is the access token part of the request which is considered in caching?
can the API be REST if it's not cacheable?
is partial access to resource allowed in REST?
#DamithK please clear what you want to do i am not getting it.."when call for a resource depending on the access token it returns different responses.
But as much i understand is that you want to authenticate your each api call.
If you are using RestClient for call api you can do it in following way.To call Api
var client = new RestClient(Serviceurl);
var request = new RestRequest("/Apimethod/{Inputs}?oauth_consumer_key=1ece74e1ca9e4befbb1b64daba7c4a24", Method.GET);
IRestResponse response = client.Execute(request);
In your service
public static class Authentication
{
public static bool AuthenticateRequest(IncomingWebRequestContext context)
{
bool Authenticated = false;
try
{
NameValueCollection param = context.UriTemplateMatch.QueryParameters;
if (param != null && param["oauth_consumer_key"] != null)
{
string consumerSecretKey = "1ece74e1ca9e4befbb1b64daba7c4a24";
Authenticated = param["oauth_consumer_key"] == consumerSecretKey;
}
else
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
}
}
catch (Exception)
{
}
return Authenticated;
}
}
Validate each request in called method using
Authentication.AuthenticateRequest(WebOperationContext.Current.IncomingRequest)
All this is c# code

Bearer token is not validating with authorize attribute

I have a simple api controller with a method "GetDashboard" that contains an Authorize attribute like so...
[RoutePrefix("api/Dashboard")]
public class DashboardController : ApiController
{
[HttpGet]
[Authorize]
[Route("GetDashboard")]
public HttpResponseMessage GetDashboard()
{
//Do stuff...
}
}
I'm using Owin pipeline and bearer tokens for my api authorization and in my Owin configuration i have created two authorization providers using the app.Map() functionality to select the correct mechanism for authorizing users depending your on your entry point to the api like so...
app.Map("/RouteOne", app1 =>
{
appAteb.UseCookieAuthentication(new CookieAuthenticationOptions());
appAteb.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
PublicClientId = "app1";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Authenticate1"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = false
};
app1.UseOAuthBearerTokens(OAuthOptions);
});
app.Map("/RouteTwo", app2 =>
{
app2.UseCookieAuthentication(new CookieAuthenticationOptions());
app2.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
PublicClientId = "app2";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Authenticate2"),
Provider = new AnotherAuthProvider(PublicClientId),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = false
};
app2.UseOAuthBearerTokens(OAuthOptions);
});
Both mechanisms authenticate correctly, the bearer token is generated and passed back to the browser however when then use the token for authorization on my dashboard controller it returns a 401 Unorthorized?
I suspect its something to do with the app.map because if i remove it and just have one mechanism calls to my dashboard controller work fine however i need to be able to use both mechanisms of authorization and my api controllers to accept the tokens.
Any help on solving this issue would be greatly appreciated.
Thanks
The RoutePrefix of your action GetDashboard() should include the route of one of the maps. For example you could redefine your maps as api/RouteOne and api/RouteTwo and then the route prefix could be for example [RoutePrefix("api/RouteOne/Dashboard")].
I hope it helps.

Problems with ServiceStack Authentication when deployed to IIS 7

I am having problems getting authentication to work when deploying to my IIS 7 production web server. I am using the in memory authentication and everything works perfectly when I run locally from Visual Studio. However when I try to connect to the production web server, from a WPF application using the ServiceStack client, I am getting different errors including:
"Not Found"
"Unauthorised"
"{"Length cannot be less than zero. Parameter name: length"}"
I have tried numerous configurations of authentication in IIS including enabling/disabling Forms Authentication, Windows Authentication and Basic Authentication, all to no avail.
I can sometimes connect using the CodeInChaos.com REST client.
The password and username are definitely correct.
global.asax:
public override void Configure(Funq.Container container)
{
RegisterPlugins();
RegisterValidators(container);
RegisterCaches(container);
RegisterSessionFactories(container);
RegisterRepositories(container);
RegisterUsers(container);
}
private void RegisterPlugins()
{
Plugins.Add(new AuthFeature(() => new AuthUserSession(),new IAuthProvider[] { new BasicAuthProvider()}));
}
private void RegisterUsers(Funq.Container container)
{
var userRepository = new InMemoryAuthRepository();
container.Register<IUserAuthRepository>(userRepository);
string hash;
string salt;
new SaltedHash().GetHashAndSaltString("xxxxxxxx", out hash, out salt);
userRepository.CreateUserAuth(new UserAuth
{
Id = 1,
DisplayName = "xxx xxxxxx",
Email = "xxxx#xxxxx.com",
UserName = ""xxxxxxxx,
FirstName = "xxxx",
LastName = "xxxx",
PasswordHash = hash,
Salt = salt,
Roles = new List<string> { RoleNames.Admin },
}, "xxxxxxxx");
}
My client:
public UserEntity GetUserFromDomainUsername(string domainUsername)
{
try
{
using (var client = new StormJsonServiceClient(WebServiceUrl){UserName = "xxxxxxx", Password = "xxxxxxxx"})
{
var response = client.Send(new UserFromDomainUsernameQuery { DomainUsername = domainUsername });
return response.User;
}
}
catch (Exception exception)
{
var ex = exception as WebServiceException;
if (ex != null)
{
throw new VqsWebServiceException(GetWebServiceErrorMessage(ex));
}
throw;
}
}
IIS authentication is not related to ServiceStack authentication, turn it off if you don't need it.
If you have IIS authentication enabled it would run effectively on top of your ServiceStack application. So it would run before all requests to your ServiceStack service and you would end up having to satisfy this security criteria first. If this passed your request would then go through to the ServiceStack application.
Most people implementing authentication in ServiceStack will not require IIS to also implement authentication.