Manage Multiple Login page with Spring authorization server - spring

Is this possible to manage multiple login page with spring authorization server?
Suppose, we have 2 client and both client want a different login page
client 1 need /login url
client 2 need /login2 url..

I believe the question is how to brand a login page based on the current clientId. We can use any technique available in Spring Security, as it is fully available and not hidden when using Spring Authorization Server.
As you point out, one way to handle this would be to perform a custom redirect when authentication is required. This would be handled in a custom AuthenticationEntryPoint. You can build a delegate with a mapping of clientIds to login urls. Normally, I'd encourage you to try it yourself (learning new things is fun, right!?), but in this case, here's an example:
public class BrandedAuthenticationEntryPoint implements AuthenticationEntryPoint {
private final Map<String, AuthenticationEntryPoint> authenticationEntryPoints;
private final AuthenticationEntryPoint defaultEntryPoint = new LoginUrlAuthenticationEntryPoint("/login");
public BrandedAuthenticationEntryPoint(Map<String, String> loginUrls) {
Map<String, AuthenticationEntryPoint> map = new HashMap<>();
loginUrls.forEach((clientId, loginUrl) ->
map.put(clientId, new LoginUrlAuthenticationEntryPoint(loginUrl)));
this.authenticationEntryPoints = Collections.unmodifiableMap(map);
}
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
AuthenticationEntryPoint delegate = this.defaultEntryPoint;
// Attempt to resolve a specific login url based on clientId
String clientId = request.getParameter("clientId");
if (clientId != null) {
delegate = this.authenticationEntryPoints.getOrDefault(clientId, this.defaultEntryPoint);
}
delegate.commence(request, response, authException);
}
}
SAS and Form Login are two different filter chains in the default sample, so you would apply this in the normal way on both filter chains:
http.exceptionHandling(exceptionHandling -> exceptionHandling
.authenticationEntryPoint(new BrandedAuthenticationEntryPoint(...))
);
More information on AuthenticationEntryPoint is available in the reference docs.

Related

Spring Authorization server customizing the access token response to add additional params

I am implementing the spring authorization server and I want to add few custom properties to the token response json. Below is how I want the response to be.
{
"access_token": *jwt*,
"scope": "articles.read openid",
"token_type": "Bearer",
"expires_in": 299,
***"customvalue1":99***
}
I have seen multiple posts in stack overflow where similar topic is discussed, but in those scenarios the additional data is added either to the claim or header of jwt. My requirement is to add it outside of the jwt.
I tried to implement OAuth2TokenCustomizer, but this allows only the claims or headers of the jwt to be modified. Can anyone pls help?
To anyone coming here looking for answer:
I ended up overriding OAuth2TokenEndpointFilter. It has a authentication successhandler which can be injected to perform any additional token response manipulation.
#Bean
public Customizer<OAuth2TokenEndpointConfigurer> customizeTokenEndpoint() {
return tokenEndpoint -> tokenEndpoint
.accessTokenResponseHandler(success());
}
#Bean(name = "token")
public AuthenticationSuccessHandler success() {
return new TokenResponseSuccessHandler();
}
Then inside success handler,
#Override
public void onAuthenticationSuccess(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) throws IOException {
final OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication;
******
**
Map<String, Object> additionalParameters = accessTokenAuthentication.getAdditionalParameters();
if(additionalParameters.size()==0)
additionalParameters=new HashMap<>();
additionalParameters.put("hi","hi");
Finally use, OAuth2AccessTokenResponse.Builder to build a new response.
In case you are using the new authorization server then creating this bean will help you achieve your goal. The good thing, once the bean is detected it will automatically be applied.
#Bean
public OAuth2TokenCustomizer<JwtEncodingContext> tokenCustomizer() {
return context -> {
Authentication principal = context.getPrincipal();
//context.getTokenType().getValue().equals("access_token")
if (Objects.equals(context.getTokenType().getValue(), "access_token") && principal instanceof UsernamePasswordAuthenticationToken) {
Set<String> authorities = principal.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
User user = (User) principal.getPrincipal();
context.getClaims().claim("authorities", authorities)
.claim("user", user);
}
};
}
Duplicate of How to create custom claims in JWT using spring-authorization-server
This class and the method maybe help you.You can find the class init place

Spring SAML: Multiple ACS URLs?

I'm trying to configure Spring SAML to work with multiple ACS URLs. I'd like the ACS URL to be determined based on some input the user provides, and it will select one of two ACS urls.
For example:
The user passes in a value A in the request, the ACS URL will be http://server1.com/saml/response.
The user passes in a value B in the request, the ACS URL will be http://server2.com/saml/response in the SAML Response
Any ideas or pointers in the right direction would be appriciated.
You don't specify what version of Spring Security SAML you're using. This is an example based on 1.0.10.RELEASE and is available here.
This is one way to do it:
public class ConfigurableWebSsoProfile extends WebSSOProfileImpl {
#Override
protected AuthnRequest getAuthnRequest(final SAMLMessageContext context,
final WebSSOProfileOptions options,
final AssertionConsumerService acs,
final SingleSignOnService bindingService)
throws SAMLException, MetadataProviderException {
AuthnRequest request = super.getAuthnRequest(context, options,
acs, bindingService);
if (something == true) {
request.setAssertionConsumerServiceURL(...);
} else {
request.setAssertionConsumerServiceURL(...);
}
return request;
}
}

Wildfly Database Module Authentication : How to record logins [duplicate]

Given an authentication mechanism of type FORM defined for a Java web app, how do you capture the login performed event before being redirected to requested resource? Is there any kind of listener where I can put my code to be executed when a user logs in?
I feel like defining a filter is not the best solution, as the filter is linked to the resource and would be invoked even when the user is already authenticated and asking for a resource. I'm wondering if there's some class/method triggered only by login event.
There's no such event in Java EE. Yet. As part of JSR375, container managed security will be totally reworked as it's currently scattered across different container implemantations and is not cross-container compatible. This is outlined in this Java EE 8 Security API presentation.
There's already a reference implementation of Security API in progress, Soteria, developed by among others my fellow Arjan Tijms. With the new Security API, CDI will be used to fire authentication events which you can just #Observes. Discussion on the specification took place in this mailing list thread. It's not yet concretely implemented in Soteria.
Until then, assuming FORM based authentication whereby the user principal is internally stored in the session, your best bet is manually checking in a servlet filter if there's an user principal present in the request while your representation of the logged-in user is absent in the HTTP session.
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
HttpServletRequest request = (HttpServletRequest) req;
String username = request.getRemoteUser();
if (username != null && request.getSession().getAttribute("user") == null) {
// First-time login. You can do your thing here.
User user = yourUserService.find(username);
request.getSession().setAttribute("user", user);
}
chain.doFilter(req, res);
}
Do note that registering a filter on /j_security_check is not guaranteed to work as a decent container will handle it internally before the first filters are hit, for obvious security reasons (user-provided filters could manipulate the request in a bad way, either accidentally or awarely).
If you however happen to use a Java EE server uses the Undertow servletcontainer, such as WildFly, then there's a more clean way to hook on its internal notification events and then fire custom CDI events. This is fleshed out in this blog of Arjan Tijms. As shown in the blog, you can ultimately end up with a CDI bean like this:
#SessionScoped
public class SessionAuthListener implements Serializable {
private static final long serialVersionUID = 1L;
public void onAuthenticated(#Observes AuthenticatedEvent event) {
String username = event.getUserPrincipal().getName();
// Do something with name, e.g. audit,
// load User instance into session, etc
}
public void onLoggedOut(#Observes LoggedOutEvent event) {
// take some action, e.g. audit, null out User, etc
}
}
You can use Servlet filter on the j_security_check URI. This filter will not be invoke on every request, but only on the login request.
Check the following page - Developing servlet filters for form login processing - this works in WebSphere App Server, and WebSphere Liberty profile.
Having such filter:
#WebFilter("/j_security_check")
public class LoginFilter implements Filter {
...
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Filter called 1: " +((HttpServletRequest)request).getUserPrincipal());
chain.doFilter(request, response);
System.out.println("Filter called 2: " + ((HttpServletRequest)request).getUserPrincipal());
}
gives the following output:
// on incorrect login
Filter called 1: null
[AUDIT ] CWWKS1100A: Authentication did not succeed for user ID user1. An invalid user ID or password was specified.
Filter called 2: null
// on correct login
Filter called 1: null
Filter called 2: WSPrincipal:user1
UPDATE
Other possible way to do it is to use your own servlet for login, change the action in your login page to that servlet and use request.login() method. This is servlet API so should work even in Wildfly and you have full control over login. You just need to find out how wildfly passes the originally requested resource URL (WebSphere does it via cookie).
Servlet pseudo code:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String user = request.getParameter("j_username");
String password = request.getParameter("j_password");
try {
request.login(user, password);
// redirect to requested resource
} catch (Exception e) {
// login failed - redirect to error login page
}

How to get request attributes in authentication-success-handler

I am trying to do few things in authentication-success-handler and I need to access few values which was part of initial request data being posted to Spring security.
I am posting following information when user trying to do login
j_username
j_password
storeCode
Spring security is able to authenticate user successfully and is calling "authentication-success-handler".
public class WebshopAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler
{
public WebshopAuthenticationSuccessHandler() {
}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request,
final HttpServletResponse response, final Authentication authentication)
throws IOException, ServletException {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
request.getAttribute( "storeCode" );
attr.getRequest().getAttribute( "storeCode" );
}
}
But in all way, I am not able to get value of storeCode and its coming as null.
Not sure what I am doing wrong.
I am assuming that Spring is creating a new instance of Request and response while calling onAuthenticationSuccess, but how can I pass/ retrieve values which passed passed from the login page?
If the data is from an HTTP POST request, you should be using getParameter, not getAttribute. Attributes are server-side state only, not submitted by the client.

Store url when #Controller is accessed Spring MVC

Is there a way to access the URL resolved from a Spring MVC controller - e.g.
#RequestMapping("/{language}/news/{articleId}")
public String newsPage(...) {
}
Resolves to:
/en/news/63421
I'd like to store this with the session so I can keep a track of last place visited. The motivation here is if the page is secured the login filter will come into play and we have used
SavedRequestAwareAuthenticationSuccessHandler
to route users back to the page they were trying to access.
However if they are viewing unsecured pages and choose to log in using a form that drops down from the top of the screen (the page's menu bar) the 'last page' seems to be the login form so the success handler drops them back to the root context.
I'd like to intercept controller calls and store a single URL with the session, override SavedRequestAwareAuthenticationSuccessHandler to allow us to modify the RequestCache and then let Spring redirect on login success.
Ideally we'd like a generic way to do this across all controllers but not sure if there is a filter we can use to pick this up - filtering requests gets all sorts of noise like css, js, images and html fragment pages so we're hoping someone knows a way to do this just with the controllers themselves.
There are two questions:
1) obtain the url in a controller method
#RequestMapping("/{language}/news/{articleId}")
public String newsPage(..., HttpServletRequest request) {
String uri = request.getRequestUri();
...
}
If you need this very often then you can implement a HandlerMethodArgumentResolver. *See this answer https://stackoverflow.com/a/8769670/280244 for an example (it implements a HandlerMethodArgumentResolver for the current user, but you can easyly adapt it for urls)
2.) store the url for each request in the session
You can implement a Servlet Filter or Spring HandlerInterceptor, both get a HttpServletRequest (In a Servlet Filter you need to cast the ServletRequest to an HttpServletRequest first.
Then you can obtain the url and the Session httpServletRequest.getSession() and then store the url in the session.
public class MyFilter implements Filter {
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
//do nothing
}
#Override
public void doFilter(ServletRequest requ, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (requ instanceof HttpServletRequest) {
HttpServletRequest httpServletRequest = (HttpServletRequest) requ;
httpServletRequest.getSession().setAttribute(
"myFilter.LAST_URL",
httpServletRequest .getRequestURI());
}
chain.doFilter(request, response);
}
#Override
public void destroy() {
}
}
To get the URL path you can use the HttpServletRequest - so for example you have:
www.mysite.com/en/news/63421
req.getPathInfo() = /en/news/63421
Storing it in the session though could cause problems if someone is to use your site with multiple tabs open.

Resources