AccesDenied request is not forwarded to AuthenticationEntryPoint if multiple AuthenticationProvider exist - spring-security

In a Spring Security project I have configured Keycloak via the regular KeycloakWebSecurityConfigurerAdapter method. Everything works fine with Keycloak itself.
I now have added one specific URL that needs to be authorized via basic-auth. For that, I have configured a second configuration, with an antMatcher to that URL that has higher priority than the Keycloak-configuration. This works fine, when I access that URL, basic auth is triggered and I can authenticate for it.
But after authenticating for this URL, I can no longer authenticate via my Keycloak-specific URL. The current logic is as follows: I have a Keycloak-login URL (/auth/login), which is handled by a controller in my project. The handling method is annotated with #Secured, requiring the user to have a role that he only gets after authenticating with Keycloak. So for the first login, the request to the /auth/login URL is denied, an AccessDeniedException is thrown.
What Spring then automatically does in ExceptionTranslationFilter: It delegates the request to the authentication entrypoint, which is a Keycloak specific implementation which does all the magic.
The problem in this specific case is, however, that in ExceptionTranslationFilter#handleSpringSecurityException Spring will not delegate to the authentication entry point, if the user is not "anonymous". Because the user has previously authenticated for a basic-auth URL, Spring finds this authentication object in the context and then does not consider the user as anonymous. I guess the logic here is that Spring considered this a failed login-attempt or something.
What can I do so that Spring also delegates the request to the configured authentication entry point if it finds some unrelated authentication in the context?

Related

What is the point of PostLogoutRedirectUri in Open Id/OAuth2.0 logout?

I've been using Identity Server 4.0 as my OpenId Connect provider. I can setup clients in Identity Server with Redirect Uris and Post Logout Redirect Uris. I've also been using the angular-auth-oidc-client to login/logout via the Identity server.
When logging in, my client library (angular-auth-oidc-client) does pass in the correct Uri specified in the config when calling the authorize endpoint. When I try to login with an incorrect Redirect Uri, Identity Server checks and validates that the Uri provided by the client is one of the accepted one for that client, and shows an error if it isnt (as expected).
When it comes to logout, none of it seems to be built in. My client library does not send the PostLogoutRedirectUri when calling the logout endpoint. Identity Server's sample code for logout does not except any URIs to be passed in. It's sample code simply gets the Post Logout Redirect Uri value from the database and creates a link on the logged out page. Not only does the sample code not allow the user to specify the Redirect Uri for logout, but it doesn't do any checks or even do a redirect (granted its only sample code and I can change it). I would expect my client library to pass the Uri along and Identity Server to redirect to the Uri after successful logout as long as its one of the "approved" Uris for the client.
My question is: What even is the point of PostLogoutRedirectUri? Neither Identity Server nor the OIDC client library I'm using do anything useful with it. There doesn't even seem to be an agreed upon convention for the name of the query string parameters to use to pass this Uri to Identity Server. And yet, both the Identity Server and the angular client library seem to have some support for it. So what's the point of this thing? Is it something that will be added or fleshed out later? Did I miss some documentation describing what its for and how to use it?
It's a draft standard and support differs between libraries and vendors, but here is a summary:
A client uses the post logout redirect URI to log out in a controlled way, typically redirecting to an application page that gives the user a link to sign in again
A client could potentially have more than one post_logout_redirect_uri and decide which to use based on runtime conditions
The post_logout_redirect_uri sent is meant to only be honoured if it is accompanied by an id_token_hint - and if it matches a configured value against the OAuth client. I believe OIDC will send the current id token but it is worth checking that this is happening in your browser tools.
If a post_logout_redirect_uri is not sent then the Authorization Server may use the default one configured
See the official IETF docs on how this is meant to work.
In my own application, I set it to the /Signout-callback-oidc URL of the client, like
PostLogoutRedirectUris = { "https://localhost:5001/signout-callback-oidc" },
The /signout-callback-oidc path is defined as in the source code here:
/// <summary>
/// The request path within the application's base path where the user agent will be returned after sign out from the identity provider.
/// See post_logout_redirect_uri from http://openid.net/specs/openid-connect-session-1_0.html#RedirectionAfterLogout.
/// </summary>
public PathString SignedOutCallbackPath { get; set; }
I hope this can give some clarification.
There are two sessions, one in the UI, and another on the server. It might need to call a URL on the backend to remove the user session on the server, otherwise the server won't know you are logged out on the UI.

Attempting to connect to a IdentityServer4 login page fails

I have an existing website that I want to do a proof of concept with OAuth2 / OIDC. To this end I've configured a locally running IdentityServer4 MVC app as my demo OIDC server following the IdentityServer4 quick setup guidelines. This works fine and navigating to:
http://localhost:5000/.well-known/openid-configuration
Lets me see the discovery document.
I have created a fake login page on this OIDC app which consists of just a login button with no user credentials required.
There's no actual user database and I'm just hard coding some user details to return when the 'authentication' occurs.
In my pre-existing site I've added the OWIN middle wear and am configuring OIDC using the OpenIdConnectAuthenticationOptions. The clientId, scopes, secret etc all match as required and the authority is set to point to my locally running demo OIDC app (http://localhost:5000). The redirect url is set to return to my pre-existing site once authentication is complete.
This all appears to be fine but here's what I want to achieve and can't get working. On my pre-existing site when I navigate to any page that requires authentication I want the user to be redirected to the login page I created on OIDC app. They click the login button (no user details required) and are authenticated and redirected back to the original page.
Currently when I navigate to a protected page I am successfully redirected to the OIDC app but I am redirected to an error page and I don't know why. The error page gives me no detail, it's actually hard coded in the app.
When I look at the discovery document I see that the setting for the 'authorization_endpoint' is set to:
http://localhost:5000/connect/authorize
So I thought maybe I needed to either change that to point to Home/Login which is where I've created the dummy login form, or else I needed to actually create that connect/authorize endpoint and put my form there. Creating the end point makes no difference, it never gets hit and instead I just get the error page on my OIDC app. Changing it to home/login also appears to be ignored.
I am away from my main PC at the moment hence the lack of code snippets but essentially the set up is as per the IdentityServer4 quick setup guide and the OIDC app does appear to be working.
The issue is getting my pre-existing site to properly redirect to the login page.
I've been stuck on this for quite a while now and would like to even get to the stage of seeing the dummy login page. Any pointers are appreciated and again apologies for the lack of sample code.
UPDATE
I've got the login form appearing by setting the openidconfiguration like so:
Configuration = new OpenIdConnectConfiguration()
{
AuthorizationEndPoint = "http://localhost:5000/home/login"
}
However, this isn't logging me in when I click login. On that login action I'm doing this:
await HttpContext.SignInAsync("subjectId","username", authenticationProps);
And then redirecting back to my existing site. However it's not authenticating me and the redirect ends up being redirected back again to the login page.
UPDATE 2
I think the redirect URI should possibly be doing something more. Currently I do the following:
Try and access a restricted page -> Redirected to OIDC server -> Click Login (this sets the subject and user successfully) -> Am redirected to redirect URI which immediately bounces me back to the OIDC server.
So maybe the redirect URI is supposed to confirm login or otherwise do something?
So in the open id connect protocol, the authorize endpoint is used to validate the client information passed as query parameters (client_id, scopes, redirect_uri, etc). In your authentication server, none of that is being checked if all the endpoint does is return a form. Then again the validation can be tedious so keeping the authorize endpoint separate from the endpoint for logging in might be worth a thought.
The developers of Identity Server thought the same thing which is why they set up the endpoint (and endpoint validation) for you as part of the middleware. The validation uses the components that were injected (primarily the client_store, and your defined scopes) to be used by Identity Server.
After the framework validates your authorize request using your client store implementation, it will redirect the user to whichever login page you specify. The login page can be specified by changing it with the a delegate that can be passed in as the second parameter of 'AddIdentityServer' (that takes in something of type IdentityServerOptions that we'll refer to as just 'options'). In the delegate you can specify the login url of the page by changing the value of 'options.UserInteraction.LoginUrl' to the url of the login page.
After the user logs in and you call the signInAsync method on the HttpContext, you're actually supposed to redirect back to a query parameter passed to the login page referred to as the 'return_url' (which is basically the initial authorize endpoint request). This authorize endpoint further validates the cookie and will send the user back to the 'redirect_uri' (if consent on the client is set to false) with a code (if using the authorization code flow) or the id_token and optionally the access token (if using the implicit flow).
Assuming the implicit flow for simplicity, the tokens can be found in the request to the 'redirect_uri' and from there it's all up to you. Commonly the client will issue some kind of cookie (which can potentially contain the id or access token) to mark the successful authentication by the identity provider.

How The Spring Security is redirecting to Login page if User is unauthorized

What is the Internal mechanism of authorization by spring-security?
How it is redirecting the URL to /Login if a user does not enters valid credentials?
Who is responsible for this Action?
What is the internal flow of project ?
All the important magic is in the security filter chain.
At some point some component checks whether the request is authenticated. Usually it is the last filter - FilterSecurityInterceptor (checking access conditions based on request path), but it can be MethodSecurityInterceptor (#Secured annotation check) or even your own custom code. The important thing is that this check will end in AuthenticationException or AccessDeniedException being thrown.
The next important component is ExceptionTranslationFilter. This filter checks if the thrown exception is AuthenticationException or AccessDeniedException and calls configured AuthenticationEntryPoint. Depending on the configured authentication mechanism, this might send redirect to login page or 401 response with WWW-Authenticate header (e.g. BASIC auth).
So the overall process for FORM based login looks like this (it might look like a mess, but it is actually pretty well designed and not that complex once you understand the principles):
unauthenticated user makes request to /foo.html
request is checked by FilterSecurityInterceptor against your security expression isAuthenticated() and throws AccessDeniedException
thrown exception is catched by ExceptionTranslationFilter, which in turn calls LoginUrlAuthenticationEntryPoint
entry point sends 301 redirect response to /login.html
user makes another request to /login.html
request is checked by FilterSecurityInterceptor and for this path, anonymous access is allowed
login form is presented to user
user fills the login form and submits it as POST /login.html
this request is intercepted by authentication processing filter UsernamePasswordAuthenticationFilter
username and password is extracted and AuthenticationManager is called to perform actual authentication (actual authentication is delegated to other components - usually DaoAuthenticationProvider and UserDetailsService)
authentication is successful and the Authentication token is marked as authenticated and put on SecurityContextHolder (so it is available to other components)
AuthenticationSuccessHandler is being called, which might just redirect user back to /foo.html
on the way up, the successful authentication is being picked up by SecurityContextPersistenceFilter (checks SecurityContextHolder), which stores authentication on the HTTP session
user makes yet another request to /foo.html
SecurityContextPersistenceFilter restores authentication from session onto SecurityContextHolder
FilterSecurityInterceptor checks your access rule again and allows further processing for the freshly authenticated request (i.e. dispatcher servlet is called)
user enjoys his /foo.html

OWIN OAuth 2.0 Authorization Server + custom membership provider

I have a working site aaa.com with custom membership provider
connected as http module.
I need a create OAuth 2.0 Authorization
Server + Resource Server
Main scenario of using is Authorization
Code Grant.
Here is a good example of creating what I need http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server (see source code of example)
What is my problem:
I want to bind Authorization Server to subdomain of main site -
oauth.aaa.com and when user came from external site to my
Authorization Server, this server check if user is already
authenticated on main site(aaa.com) and if yes - he must just press
Grant button and allow external site access, if no - he must enter user name and password and after this he must automatically be log in
to main site and Authorization Server must be authenticated too.
I've already connected my custom membership provider as http
module(another variant of using not impossible now) to Authorization
Server and Authorize action of this server is already authenticated
when user already logged in to main site.
I don't understand how I must configure Authorization Server(or create some customizations) that it can accept a cookie from main
site(or automatically synchronize and create a new cookie for
Authorization Server using info from already authenticated request).
I was try to configure CookieAuthenticationOptions with same cookie name as cookie name of main site and check that in cookie of main site domain is .aaa.com ...but nothing happend and variable ticket in code = null
var authentication = HttpContext.GetOwinContext().Authentication;
var ticket = authentication.AuthenticateAsync(Auth.AuthenticationType).Result;
Or may be I need another library, not based on OWIN? I saw DotNetOpenAuth library but seems its OAuth client, not server.

Spring Security "exceptionMappings"

Using Spring Security preauthentication, my web app re-directs to /login_disabled.html upon hitting a InsufficientAuthenticationException.
sample of applicationContext-security-preauth.xml
<beans:property name="exceptionMappings">
<beans:props>
<beans:prop key="org.springframework.security.
InsufficientAuthenticationException">
/login_disabled.html
Based on this post, it seems that I should be able to re-direct the user to log in again.
Would I just need to re-direct the user to the webpage responsible for authentication?
It's not really clear for me what's the problem here. The redirection to the login page is automatically done without any further configuration if you have form-login set up. If the user tries to access a secured page without being authenticated, the ExceptionTranslationFilter invokes the AuthenticationEntryPoint to initiate authentication.
Using ExceptionMappingAuthenticationFailureHandler to map InsufficientAuthenticationException to a redirect-url won't work anyway because:
It's not indicating an authentication failre. It indicates the condition that the user is only anonymously authenticated while trying to access a secured resource. (As opposed to an auth failure such as entering bad credentials, or user has disabled status.)
It never even gets thrown. (Only instantiated and passed as a parameter in the above linked code.)

Resources