Prevent forms authorisation redirect in MVC when Web Api authentication fails - asp.net-mvc-4

I'm basically a novice with Web Api, but I have finally added Web Api into an existing project and implemented a basic authorisation filter which allows me to both authenticate the user and use their identity in my apicontroller action methods.
The problem I'm having is that when the user is not successfully authenticated (their authorisation credentials are not valid) I am not able to return a 401 forbidden response as the MVC site automatically redirects to the login page and returns the html with a 302 redirect code.
I have seen fixes like:
protected void Application_EndRequest(Object sender, EventArgs e)
{
HttpApplication context = (HttpApplication)sender;
context.Response.SuppressFormsAuthenticationRedirect = true;
}
in global.asax
Which simply have not worked. Even if it had worked it would prevent the redirect for users browsing the website which I would like to keep.
Is there a way of preventing this redirect from taking place only in instances of failed authorisation with my Web Api, whilst also keeping the redirect for the main MVC site?

Related

Redirect after successful ADB2C login

I'm using Azure ADB2C authentication in my ASP.NET Core web app.
Based on the claims received after the user logging in, I'd like to redirect the user to another page.
I thought I might be able to redirect the user on the OnTokenValidated event of OpenIdConnectEvents. But frankly, I'm not sure if this is redirecting the client, or redirecting the auth flow. Bottom line, it doesn't redirect the user.
public async Task OnTokenValidated(TokenValidatedContext context)
{
// ... clipped code ...
context.HttpContext.Response.Redirect("~/somewhere");
}
My event handler works otherwise--just doesn't redirect.
What is the final event received after a user successfully logs in with ADB2C?
And how, specifically, can I redirect a user?
Thanks

Blazor WASM AAD auth always returns to homepage

I have my Blazor WASM site set up with Azure AD Authentication, and it works great. However, if I am sent to authenticate from any page that is not the homepage (for example mysite.com/counter), when the auth is successful I am redirected to the homepage (mysite.com) I assume there is some state that I can save client side of where the user was before the user was redirected for authentication but I cannot find it.
Edit: I Did some more digging and realized that if a user already has sign in before and is coming back to the site with a page link (for example: mysite.com/counter), it works no problem. However, if a user has not authenticated and it is sent to the login.microsoftonline.com by the authorize attribute of my page the redirect url that is sent is the mysite.com/authentication/login-callback instead of the mysite.com/counter
You could use RedirectToLogin component, it preserves the current URL that the user is attempting to access so that they can be returned to that page if authentication is successful.
#inject NavigationManager Navigation
#using Microsoft.AspNetCore.Components.WebAssembly.Authentication
#code {
protected override void OnInitialized()
{
Navigation.NavigateTo(
$"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
}
}
Reference - https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/standalone-with-azure-active-directory?view=aspnetcore-5.0#redirecttologin-component

How signin-google in asp.net core authentication is linked to the google handler?

I went into the source code but I can't see where it's wired to the handler.
In the GoogleExtensions.cs file, I see the
=> builder.AddOAuth<GoogleOptions, GoogleHandler>(authenticationScheme,
displayName, configureOptions);
But I don't understand how the route "/signin-google" calls the handler.
How signin-google in asp.net core authentication is linked to the google handler?
The question can be divided into two small questions .
How user is redirected to the url of /signin-google
How GoogleHandler process the request on /signin-google
How user is redirected to signin-google
Initially, when user clicks the Google button to login with Google Authentication, the browser will post a request to the following url:
https://your-server/Identity/Account/ExternalLogin?returnUrl=%2F
Your server simply redirects the user to Google.com and asks Google to authenticate the current user :
https://accounts.google.com/o/oauth2/v2/auth?
response_type=code
&client_id=xxx
&scope=openid%20profile%20email
&redirect_uri=https%3A%2F%2Fyour-server%2Fsignin-google
&state=xxx
When Google has authenticated the user successfully, it will redirect the user to your website with a parameter of code according to redirect_uri above.
https://your-server/signin-google?
state=xxx
&code=yyy
&scope=zzz
&authuser=0
&session_state=abc
&prompt=none
Note the path here equals /signin-google. That's the first key point.
How GoogleHandler process the signin-google
Before we talk about how GoogleHandler goes , we should take a look at how AuthenticationMiddleware and AuthenticationHandler work:
When there's an incoming request, the AuthenticationMiddleware (which is registered by UseAuthentication() in your Configure() method of Startup.cs), will inspect every request and try to authenticate user.
Since you've configured authentication services to use google authentication , the AuthenticationMiddleware will invoke the GoogleHandler.HandleRequestAsync() method
If needed, the GoogleHandler.HandleRequestAsync() then handle remote authentication with OAuth2.0 protocol , and get the user's identity.
Here the GoogleHandler inherits from RemoteAuthenticationHandler<TOptions> , and its HandleRequestAsync() method will be used by AuthenticationMiddleware to determine if need to handle the request. . When it returns true, that means the current request has been already processed by the authentication handler and there's no further process will be executed.
So how does the HandleRequestAsync() determine whether the request should be processed by itself ?
The HandleRequestAsync() method just checks the current path against the Options.CallbackPath . See source code below :
public abstract class RemoteAuthenticationHandler<TOptions> : AuthenticationHandler<TOptions>, IAuthenticationRequestHandler
where TOptions : RemoteAuthenticationOptions, new()
{
// ...
public virtual Task<bool> ShouldHandleRequestAsync()
=> Task.FromResult(Options.CallbackPath == Request.Path);
public virtual async Task<bool> HandleRequestAsync()
{
if (!await ShouldHandleRequestAsync())
{
return false;
}
// ... handle remote authentication , such as exchange code from google
}
}
Closing
The whole workflow will be :
The user clicks on button to login with Google
Google authenticates the user and redirects him to /signin-google
Since the path== signin-google, the middleware will use HandleRequestAsync() to proecess current request, and exchange code with google.
... do some other things

Redirect unauthorized requests to Azure AD for login

I've got a WebAPI instance running in Azure that is secured with Azure AD. A mobile app connects to this API using bearer tokens, works great. When I try calling the API from the browser, though, it returns a 401 because I'm not logged in. That's true because I'm not presented with a login screen.
My API doesn't have any UI so what I'd want it to do is to forward the user to Azure AD login and return to the API endpoint they were calling after authentication.
If I go to the Azure portal, there's a setting that says "Action to take when the request is not authorized". If I set that one to "Log in with Azure Active Directory", it behaves the way I want it to. But... I have some endpoints which need to be accessed anonymously, and this setting catches all requests, not caring about any [AllowAnonymous] attributes.
So any request to an endpoint labeled Authorize that is not authorized yet should be forwarded to Azure AD login, all others should be allowed.
Add a DelegatingHandler to your web api project and register it in WebApiConfig.cs:
config.MessageHandlers.Add(new UnAuthorizedDelegatehandler());
public class UnAuthorizedDelegatehandler: DelegatingHandler
There you can check for 401 status codes and do the redirect to whatever and also apply a redirect url as querystring parameter.
HttpResponseMessage rm = await base.SendAsync(request, cancellationToken);
if (rm.StatusCode == HttpStatusCode.Unauthorized)
{
// configure the redirect here
return rm;
}

Simple custom authenticator in JAX-RS

In JAX-RS (or Jersey) REST service, I'm trying to make custom authentication for my users in database.
Currently I have #GET annotated method which should interact with user asking for credentials just like it is done in Spring framework with authentication provider - no custom login form, just plain HTTP login with browser popup form.
Currently I can handle HTTP Basic Access Authentication provided in header, but I need to ask for credentials before accessing content interactively and then make token-based authentication on this base.
I have to keep the application light-weight but I don't know how can this "easy" task be done..
Edit: I found something in Wildfly configuration (I'm using 9 Final version) but I don't know how to use it for login using datasource..
If you already can handle HTTP Basic authentication, then you only need to get a a "login form" from the browser? We solved this by implementing an javax.ws.rs.ext.ExceptionMapper and overriding toResponse(Throwable ex). Our app throws a NotAuthenticatedException which gets mapped to javax.ws.rs.core.Response.Status.UNAUTHORIZED. Then we add a response header appropriately:
#Provider
public class RESTExMapper implements ExceptionMapper<Throwable>
{
#Override
public Response toResponse(Throwable ex)
{
//our application maps a not logged in exception to javax.ws.rs.core.Response.Status.UNAUTHORIZED in this Pair
Pair<Integer, ObjectMap> ret = buildResponse( unwrap( ex));
ResponseBuilder rb = Response.status( ret.left()).entity( ret.right()).type( "application/json");
if( ret.left() == UNAUTHORIZED.getStatusCode())
return rb.header( HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"YOUR SERVICE NAME\"").build();
else
return rb.build();
}
The important part is setting the response header if the user is not logged in, that makes the browser display the HTTP Basic Login Dialog.