Login via pre authentication filter or form login in spring security - authentication

I am looking for a way where you can have two ways to get access to my website.
1) You should be able to use form-login and get your authorities and use the website.
2) You should be able to login into another website and there you should be able to press a link with a token and be logged in to my website. (I control both websites and they use the same database)
Stage 1 is completed and works well and I have made stage 2 something similar to this https://stackoverflow.com/a/9919988/1915913 and that is working as well, I get a token and I am able to verify it and login.
But my problem is, how can I make them both work for me at the same time, for the same resources. I am pretty sure I know what the problem is, I create a custom filter and i try to use the form-login filter.
That does not work, but can it? Or is there some other way i can get this functionality?
This does not work in a way that the pre-auth filter seems to take over and i cant get the normal login to work and it seems to call the pre-auth filter everytime i go to a new page in the project.
The classes I use for this are all pretty simple.
My security-app-context:
<http pattern="/**" use-expressions="true" create-session="always">
<intercept-url pattern="/login.jsp*" access="permitAll" />
<intercept-url pattern="/**" access="denyAll" />
<custom-filter position="PRE_AUTH_FILTER" ref="PreAuthenticatedProcessingFilter" />
<form-login
username-parameter="idnumber"
password-parameter="password" login-processing-url="/processlogin"
login-page='/login.jsp'
authentication-failure-handler-ref="myAuthErrorHandler"
authentication-success-handler-ref="mySuccessHandler"
always-use-default-target='true'
authentication-failure-url="/login.jsp?login_error=true"/>
<logout logout-url="/logout/" logout-success-url="/login.jsp" delete-cookies="JSESSIONID"/>
<session-management invalid-session-url="/">
<concurrency-control expired-url="/" max-sessions="2" />
</session-management>
</http>
<!-- form login -->
<beans:bean id="mySuccessHandler" class="is.inna.rest.login.SuccessHandler"/>
<beans:bean id="myAuthErrorHandler" class="is.inna.rest.login.AuthentificationListener"/>
<beans:bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
<beans:bean name="myUserDetailsService" class="is.inna.rest.login.LoginUserDetailService" />
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="myUserDetailsService">
<password-encoder ref="passwordEncoder" />
</authentication-provider>
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
<!-- Pre auth -->
<beans:bean id="userDetailsServiceWrapper" class="is.inna.rest.login.AuthUserDetailService" />
<beans:bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="userDetailsServiceWrapper"/>
</beans:bean>
<beans:bean id="PreAuthenticatedProcessingFilter" class="is.inna.rest.login.PreAuthenticatedProcessingFilter">
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
My user details services
public class AuthUserDetailService implements AuthenticationUserDetailsService<Authentication> {
#Override
public UserDetails loadUserDetails(Authentication authentication) throws UsernameNotFoundException {
String id = (String) authentication.getPrincipal();
NotandiHelper notandi = UserDAO.getNotandiByToken(id);
return new User(notandi.getUsername(), notandi.getPassword(), notandi.getAuthorities());
}
}
My pre auth filter
public class PreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter {
#Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
if(request.getParameter("id") != null){
return request.getParameter("id");
}else if(request.getParameter("idnumber") != null){
return request.getParameter("idnumber");
}
return null;
}
#Override
protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
if(request.getParameter("kt") != null){
String[] credentials = new String[2];
credentials[0] = request.getParameter("token");
credentials[2] = request.getParameter("id");
return credentials;
}
if(request.getParameter("idnumber")!= null){
String[] credentials = new String[2];
credentials[0] = request.getParameter("idnumber");
credentials[1] = request.getParameter("password");
return credentials;
}
return null;
}

When you're extending a class, you really need to understand how the base class works (one of the problems with inheritance). In this case, you are returning "false" in the case where there is no token present, which is an arbitrary choice for a method returning an object. If you look at the base class, you will see that it checks for a non-null principal and returns immediately from doAuthenticate if the value is null, causing the filter chain to proceed immediately, as if the pre-authentication filter wasn't there at all. So that's not right. Try returning null instead.
Note that the debug log should also contain a log message saying
preAuthenticatedPrincipal = null, trying to authenticate
It's also unclear from your configuration how you authenticate the token. There doesn't seem to be anything there to do that.
Note that you'll need to use the entry point for form-login if you want the login form to be displayed automatically for unauthenticated users. The http403EntryPoint will just return immediately with a 403 code.

Related

B2C Account Error - Customise Error Page or Adjust Custom Policy?

In the sample that links a Federated login against a pre-created Local Account. If a user does not exist, then an exception is thrown.
This redirects to https://<host>/MicrosoftIdentity/Account/Error
Which, as far as I understand, is this page here
At this stage, I assume there are two possibilities, either:
Customise the error page (somehow); or
Change the Custom Policy so that it doesn't throw an exception and shows a self-asserted page instead (preventing SendClaims)
With regards to option 1, I've tried to find documentation on how I might trap the error or customise this page - but I haven't found anything so far. There is documentation in asp.net core with regards to creating a custom error page - but it doesn't seem to apply in this case:
if (app.Environment.IsDevelopment())
{
//app.UseDeveloperExceptionPage();
app.UseExceptionHandler("/Error");
}
With regards to option 2, I tried changing the AAD-FindB2CUserWithAADOid technical profile so that RaiseErrorIfClaimsPrincipalDoesNotExist is false:
<TechnicalProfile Id="AAD-FindB2CUserWithAADOid">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="signInNames.oidToLink" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId"/>
<OutputClaim ClaimTypeReferenceId="extension_requiresMigrationBool"/>
<!-- account flagged for linking -->
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
But that resulted in a genuine exception - the api call becomes malformed. I'm not sure why this would be the case.
For this particular circumstance, I'd like to display an Access Denied message. But it would be nice if I could create a stylised page for any Account Error.
Is either strategy okay? Am I missing something?
I've introduced the following to my Program.cs:
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp( options => {
builder.Configuration.GetSection( "AzureB2C" ).Bind( options );
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = context =>
{
if (context.Failure?.Message.ToUpper().Contains("AADB2C99002") == true)
{
context.Response.Redirect("/account-access");
context.HandleResponse();
return Task.FromResult(0);
}
return Task.CompletedTask;
}
};
});
In the case where the user isn't found (in my case, they haven't been pre-created) then they'll be shown the "Access Denied" page. Which means other errors will come through as they always have.
I'm still not sure if this is an acceptable solution.

Spring Security: forward to original page after login with admin user after access was denied

I use Spring security to authenticate users. If a user requests a secured page, he has to authenticate over a login page. If the user is always authenticated, he will be redirected to the requested page immediatly. Moreover some pages need special access rights, and so I setup an access-denied-page temporarily. So far so good.
The scenario:
The scenario definies, that the user will get a login-form instead of a static access-denied page, so that a different user can authenticate and if authentication is successful the requested page that needs the higher privileges will open.
The actual spring configuration reads:
<security:http auto-config="true" use-expressions="true" disable-url-rewriting="true">
<security:intercept-url pattern="/index.jsp" access="permitAll" />
<security:intercept-url pattern="/loginView" access="permitAll" />
<security:intercept-url pattern="/accessDenied" access="permitAll"/>
<security:intercept-url pattern="/user" access="hasRole('ROLE_USER')" />
<security:intercept-url pattern="/admin" access="hasRole('ROLE_ADMIN')" />
<security:intercept-url pattern="/**" access="denyAll"/>
<security:form-login login-page="/loginView"
authentication-failure-url="/loginView"
default-target-url="/dirView" />
<security:logout />
<security:access-denied-handler ref="accessDeniedHandler" />
</security:http>
The accessDeniedHandler-Bean:
public class AccessDeniedServletRequestHandler implements AccessDeniedHandler {
/** {#inheritDoc} */
#Override
public void handle(HttpServletRequest req, HttpServletResponse resp,
AccessDeniedException accessDeniedException) throws IOException,
ServletException {
RequestDispatcher d = req.getRequestDispatcher("/loginView");
d.forward(req, resp);
}
}
But that implementation of AccessDeniedHandler only forwards to the loginView. After authentication of an admin the default-success-page is openend and not the original requested page. I also tried to save the original request by calling HttpServletRequest#getAttribute("javax.servlet.forward.servlet_path"), but I don't understand how to force spring security to use that original request instead of the default target url.
Besides I read about org.springframework.security.web.savedrequest.SavedRequest that is used inside spring authentication to remember the original request if an unauthenticated user requests a page. But I don't find a valid way how to use the SavedRequest in the same manner for my access denied scenario.
Thanks in advance for suggestions and solutions.
I think your requirements should be satisfied by using the RequestCache API.
If you modify your http configuration you can use the request-cache namespace element:
<security:http>
...
<security:request-cache ref="requestCache"
</security:http>
<bean id="requestCache" class="org.springframework.security.web.savedrequest.HttpSessionRequestCache" />
You can also inject it into your AccessDeniedHandler. Then all you should need is a simple to saveRequest to setup the cached request which should be restored post-authentication:
public class AccessDeniedServletRequestHandler implements AccessDeniedHandler {
// Inject this into your class.
private RequestCache requestCache;
#Override
public void handle(HttpServletRequest req, HttpServletResponse resp,
AccessDeniedException accessDeniedException) throws IOException, ServletException {
requestCache.saveRequest(req, resp);
RequestDispatcher d = req.getRequestDispatcher("/loginView");
d.forward(req, resp);
}
}
Strictly speaking, you don't actually need to do the bit with the namespace at all, since HttpSessionRequestCache is stateless (it's the internal implementation which is used if you don't override it in the namespace). So you could just create one directly in your AccessDeniedHandler class and it would still work.

How does Seam 3 handles the "redirect to capture view" feature after login?

Here is my use cases.
I have a login page which is /public/login.xhtml. All my other pages are required to log-in before reaching them. They are in /pages/ directory.
I want that :
If my user access to http://host/myapp/pages/* it redirects him first to the login page, and then, to the URL he has firstly entered.
If my user access to http://host/myapp/, it redirects him first to the login page, and then, to /pages/home.xhtml.
If my user access to http://host/myapp/public/login.xhtml, it redirects him first to the login page, and then, to /pages/home.xhtml.
If my user access to http://host/myapp/public/login.xhtml and is already logged in, it redirects to /pages/home.xhtml.
What is working currently?
With Seam 3 (v3.1.0.Final) and the Security + Faces module, my use case n°1 is automagically working with :
#ViewConfig
public interface PagesConfig {
static enum Pages {
#ViewPattern("/pages/*")
#LoginView("/public/login.xhtml")
#LoggedIn
LOGGED_IN_PAGES,
}
}
My problem is that I don't understand how Seam's working to do that redirection to the "capture view".
With Seam 2, it was easy to understand, in components.xml we had
<event type="org.jboss.seam.security.notLoggedIn">
<action execute="#{redirect.captureCurrentView}" />
</event>
<event type="org.jboss.seam.security.loginSuccessful">
<action execute="#{redirect.returnToCapturedView}" />
</event>
So we captured the events notLoggedIn and loginSuccessful to handle that with a redirect component.
In Seam 3, I didn't found that configuration : nothing seems to #Observes LoggedInEvent, and there is no Redirect class...
The point n°2 is achieved with that /index.htm file :
<html><head>
<meta http-equiv="Refresh" content="0; URL=pages/home.xhtml">
</head></html>
But for my point n°3, I've tried solutions which don't fully work.
First I tried that in login.xhtml :
<f:metadata>
<s:viewAction action="#{loginAction.redirectToHome}" if="#{identity.loggedIn}" immediate="true" />
</f:metadata>
And with or without onPostback="true", after I login, I'm still in the login page with that error message (twice) : "Unable to find matching navigation case with from-view-id «/public/login.xhtml» for action «#{identity.login}» with outcome «success».". It's only if I now re-access to http://host/myapp/public/login.xhtml that my viewAction redirects me to the home.
I also tried that navigation-rule in faces-config.xml :
<navigation-rule>
<from-view-id>/public/login.xhtml</from-view-id>
<navigation-case>
<if>#{identity.loggedIn}</if>
<to-view-id>/pages/home.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>
But then, my use case n°1 was disabled : every time I logged-in, I was redirected to the home.
Finally, for my point n°4, the s:viewAction does the job.
So does somebody knows the best practices in order to correctly handle those 4 use cases (which I think are common use cases), especially the point n°3?
Use case No. - 1 SeamFaces stores the originally requested viewId in the user Session, then re-routes to that view after the successful login. It does this by intercepting the navigation from the Seam Security login button, and fires a PostLoginEvent with the data stored in the SessionMap.
Use case No. 2 - nice solution with the redirect! You could also do this with a #UrlMapping in your ViewConfig.
Use case No. 3 - Your viewAction solution should work, but I believe you are coming across SEAMFACES-179. There are a couple of solutions you can use:
1) In your login method, you can manipulate the seesion map stored by the Seam Faces, as demonstrated in this gist -- (this solution courtesy of Cody Lerum)
2) Use PrettyFaces to intercept the request for the login view, and rediret you if you are not logged in.
Finally here is what I did.
<f:metadata>
<s:viewAction action="#{loginAction.redirectToHome}" immediate="true" />
</f:metadata>
So I removed the if="#{identity.loggedIn}" in order to call my redirectToHome method which redirects to
the /pages/home.xhtml.
If the user is already authenticated, then he is redirected to the home page.
If he's not, then it is redirected to the home page, which redirects him to the login page thanks to my #ViewConfig
Here is the loginAction :
public void redirectToHome() throws IOException {
externalContext.redirect(externalContext.encodeActionURL(externalContext.getRequestContextPath()+"/pages/home.xhtml"));
}
The problem I faced then was when I logged out.
Here is my logout action :
<h:commandLink action="/public/login" actionListener="#{securityAction.logout()}" value="Disconnect" immediate="true" />
And the securityAction.logout() method :
public void logout() {
identity.logout();
if (!conversation.isTransient()) {
conversation.end();
}
}
The problem is that I was redirected to the login page (thanks to the #ViewConfig I think), but no PreLoginEvent were thrown, so the Seam LoginListener.observePreLoginEvent wasn't called, and so my previous URL wasn't put in session. So when I logged in (immediatly after logout), I was stuck on the login page, but was logged in.
Thanks to Brian Leathem and he's previous answer, here is what I did : in my authenticate method of my BaseAuthenticator, I called that method after authentication :
private void overrideRedirectToLogin() {
final String PRE_LOGIN_URL = LoginListener.class.getName() + "_PRE_LOGIN_URL";
final ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
final Map<String, Object> sessionMap = externalContext.getSessionMap();
String redirectURL = (String) sessionMap.get(PRE_LOGIN_URL);
if (redirectURL == null) {
final HttpServletRequest request = (HttpServletRequest) externalContext.getRequest();
redirectURL = request.getRequestURL().toString();
}
sessionMap.put(PRE_LOGIN_URL, redirectURL.replace("/public/login.xhtml", "/pages/home.xhtml"));
}
With that solution, my previous URL wasn't set in session, but at least, my user is redirected to the home page.

spring security stay on single url at login

here is my case.
when first time user land to site, they will pointed to login page. (lest say http://ex.com/) and when they successfully login, they'll see the other page with the same url (http://ex.com/
but, when they open the site on other tab (http://ex.com) they will pointed back to login page.
how to implement this case in my site with spring security ?
its easy to do when deal with conventional servlet. i just need to have 2 method (doGet for showing login page, and doPost for authenticating user and if its valid it will call another view).
here is my configuration :
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="/login"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>
** Edited (remove unrelated answer)
It appears you need to add a concurrent session management using Spring Security. See the following link: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html
You can inject the SessionRegistry and see if the principal is already logged-in. If he is, call the expireNow()
Or you can implement a filter on or before SessionManagementFilter in the FilterChainProxy:
The SessionManagementFilter checks the contents of the SecurityContextRepository against the current contents of the SecurityContextHolder to determine whether a user has been authenticated during the current request, typically by a non-interactive authentication mechanism, such as pre-authentication or remember-me [19]. If the repository contains a security context, the filter does nothing. If it doesn't, and the thread-local SecurityContext contains a (non-anonymous) Authentication object, the filter assumes they have been authenticated by a previous filter in the stack. It will then invoke the configured SessionAuthenticationStrategy.
- http://static.springsource.org/spring-security/site/docs/3.1.x/reference/session-mgmt.html
I think your configuration has a problem
<security:http auto-config="true">
<security:intercept-url pattern="/login*" access="IS_AUTHENTICATED_ANONYMOUSLY" />
<security:form-login login-page="/login"
login-processing-url="/loginProcess"
default-target-url="<home-page-url. ex: /home>"
authentication-failure-url="/login?login_error=1" />
<security:logout logout-url="/logout" logout-success-url="/logoutSuccess" />
</security:http>
The default-target-url should point to the default page to which the application has to redirect after a successful login.
EDITED
After going through the required posted again, I think the approach is to make the controller handling /login request to handle both cases
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;
#Controller
public class AppsController {
#RequestMapping("/login")
public ModelAndView view(HttpServletRequest request,
HttpServletResponse response) {
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
User user = authentication != null
&& authentication.getPrincipal() instanceof User ? (User) authentication
.getPrincipal() : null;
return user == null ? getLoginModelAndView() : getHomeModelAndView();
}
private ModelAndView getHomeModelAndView() {
return null;
}
private ModelAndView getLoginModelAndView() {
return null;
}
}
If there is no authenticated user present in the session the controller will return the log-in page, but once the user is logged-in then it will return a different page.
Spring security will cache the logged used to the user session and it can be retrieved using the SecurityContextHolder.

Redirect to another action in an interceptor in struts 2

I am currently in the process of learning Struts 2 and I am currently building a simple application where unverified users are redirected to a login form.
I have a login form and action functional which takes the users credentials, verifies them and stores a User object in the session however I am now trying to prevent access to pages before the login has taken place and I am trying to do this with an interceptor.
My problem is that I have written an interceptor that checks whether the User object has been saved in the session but if it has not I want to redirect to the login page and can't find any way of doing this without bypassing struts and using the HttpServletResponse.sendRedirect method
Configuration:
<package name="mypackage" extends="struts-default" namespace="/admin">
<interceptors>
<interceptor name="login" class="my.LoginInterceptor" />
</interceptors>
<default-interceptor-ref name="login"/>
<action name="login" class="my.LoginAction">
<result name="input">/admin/login.jsp</result>
<result name="success" type="redirect">/admin</result>
</action>
<action name="private" class="my.PrivateAction">
<result>/admin/private.jsp</result>
</action>
</package>
The interceptor code:
#Override
public String intercept(ActionInvocation inv) throws Exception {
Map<String, Object> session = inv.getInvocationContext().getSession();
Object user = session.get("user");
if(user == null) {
// redirect to the 'login' action here
}
else {
return inv.invoke();
}
}
The standard way is to return a special global result (eg "login") and define a global mapping from that result to your admin/login.jsp. So you just must add this line:
if(user == null) {
return "login";
}
And in your struts.xml:
<global-results>
<result name="login">/admin/login.jsp</result>
</global-results>
BTW, I'm afraid that you are replacing the default Struts2 interceptor stack with your single interceptor, normally you want to add your interceptor to the stack. Eg:
<interceptors>
<interceptor name="login" class="my.LoginInterceptor" />
<interceptor-stack name="stack-with-login">
<interceptor-ref name="login"/>
<interceptor-ref name="defaultStack"/>
</interceptor-stack>
</interceptors>
<default-interceptor-ref name="stack-with-login"/>
BTW2: You must NOT apply the interceptor to your login action, of course.
You can find the complete example of struts2 with a custom Login Interceptor here
http://sandeepbhardwaj.github.io/2010/12/01/struts2-with-login-interceptor.html
great tutorial.
If you need to use send redirect, return null to avoid this problem (example redirecting from www.domain.com to domain.com):
public String intercept(final ActionInvocation invocation) throws Exception {
String url=RequestUtil.getURLWithParams(); //you should implement this
int index=url.indexOf("www");
if (index!=-1 && index<10) {
//Note: <10 to check that the www is in the domain main url
//https://localhost:8443/mycontext/myaction.action?oneparam=http://www.youtube.com/user/someuser
String redirection=url.replaceFirst("www\\.", "");
LOG.debug("Redirection from "+url+" to "+redirection);
RequestUtil.getResponse().setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
RequestUtil.getResponse().sendRedirect(redirection);
return null;
}
return invocation.invoke();
}