I am currently a budding Java developer that wants to get into Vaadin development and currently trying to implement User Session login for my application. I have read about the content regarding the usage of VaadinServlets to do so : https://vaadin.com/docs/v10/flow/advanced/tutorial-application-lifecycle.html.
After relentlessly digging through API documentations and sample codes, I still am not able to understand how to implement the User Sessions for a specific user that logs into my platform. From what I understand is that, I can initialize my user session by using what i have implemented below.
However my aims for the application are slightly different:
[Use Case]
1.User logs in with their specific credentials.
2.Gets Redirected to a SecuredPage (which will create a User Session storing the user's username and retrieves a token?)
3.After 2-3mins of inactivity, the User will get forced out of the SecuredPage and Session closes?
#WebServlet(urlPatterns = "/*", name = "VaadinFlowServlet", asyncSupported = true)
#VaadinServletConfiguration(heartbeatInterval = 5, productionMode = false)
public class LoginServlet extends VaadinServlet implements SessionInitListener, SessionDestroyListener {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginServlet.class);
// <Method> ::servletInitialized():: -> handles most of the servlet customization. (write my servlet customization under this function.
// ::getService():: -> returns a VaadinServletService type?
// ::addSessionInitListener(this):: -> An event listener that can be registered to a VaadinService to get an event -> when a new Vaadin service session is initialized for that service.
// ::addSessionDestroyListener(this):: -> A listener that gets notified when a Vaadin service session is no longer used.
#Override
protected void servletInitialized() throws ServletException {
super.servletInitialized();
getService().addSessionInitListener(this);
getService().addSessionDestroyListener(this);
}
// <Method> ::sessionInit:: -> Starts Session?
// <Parameter> ::SessionInitEvent:: -> Event gets fired when a new Vaadin service session is initialized for a Vaadin service.
#Override
public void sessionInit(SessionInitEvent event) throws ServiceException{
// Do Session start stuff here
// Creates a Session?
LOGGER.info("session init() "
+ " Session-ID: " + event.getSession().getSession().getId()
+ " CSRF: " + event.getSession().getCsrfToken());
}
// <Method> ::sessionDestroy:: -> Stops Session?
// <Parameter> ::SessionDestroyEvent:: -> Event fired when a Vaadin service session is no longer in use.
#Override
public void sessionDestroy(SessionDestroyEvent event) {
// Do session end stuff here
LOGGER.info("session destory()");
}
}
1
So I was wondering if anybody can help me understand this matter better? Fully Appreciated
tl;dr
The mere existence of a custom-defined user-login object stored as an attribute in the key-value store of your VaadinSession represents the user having successfully authenticated. No need for all the session-listener code you wrote.
Let Vaadin do the heavy-lifting
I suspect you are working too hard.
There is no need for your session listeners. Vaadin handles nearly all the Java Servlet details on our behalf.
No need for the redirects. As a Vaadin developer, you are in full control of the content displayed in the browser tab/window, so you can switch between login form and main app content. Caveat: I am new to the #Route feature in Vaadin Flow, so there may be a niftier way with that feature to flip between login and main-content. And if you are using #Route for multiple views, each of those views should test for the authentication as described below.
VaadinSession
At the entry point of your Vaadin app code, retrieve the current VaadinSession object. This VaadinSession is a wrapper around the javax.servlet.http.HttpSession class defined by the Java Servlet spec. Vaadin automatically instantiates a session when the user’s browser first connects to your Vaadin web app (actually, Vaadin wraps the session instantiated by your web container). The session is automatically closed when the browser closes its tab/window, a time-out of inactivity occurs, or you programmatically close the session.
VaadinSession vaadinSession = VaadinSession.getCurrent() ;
Session attributes (key-value store)
Interrogate that session object’s key-value store known as “attributes”. The key is of type String and the value is of type Object (the superclass of all Java objects). After retrieving the Object object, you cast to the known class. You know the class, because it is your code that stored the attribute.
Your user-login class
You would have defined a class to store your user-login related info. Perhaps you named it UserLogin.
Something like:
public class UserLogin {
// Member values.
String userName ;
Instant whenAuthenticated ;
// Constructor.
public UserLogin( String userNameArg , Instant whenAuthenticatedArg ) {
this.userName = userNameArg ;
this.whenAuthenticated = whenAuthenticatedArg ;
}
}
Attempt to retrieve object of your user-login class from the session’s key-value store
Retrieve such an object of that type from the session attributes key-value store.
String attributeName = "my-user-login" ;
UserLogin userLogin = vaadinSession.getAttribute( attributeName ) ;
Rather than invent an attribute name, you could just use the class name. The Class class lets you ask for the name of a class as text.
String attributeName = UserLogin.class.getName() ;
UserLogin userLogin = vaadinSession.getAttribute( attributeName ) ;
If you want to use the class name as the key in this way, the VaadinSession class provides a shortcut.
UserLogin userLogin = vaadinSession.getAttribute( UserLogin.class ) ;
Check to see if your UserLogin object is null. If you retrieved a null, then you know you have not yet stored an attribute (or willfully stored a null).
If not null, it means your user already has an active UserLogin object stored. How could they be logged-in already if the entry point of your app is executing? This can happen if the user hits the Reload button on their browser window. (Train your user not to do so on a single-page web app such as Vaadin.)
Outline of code to write
UserLogin userLogin = vaadinSession.getAttribute( UserLogin.class ) ;
if( Objects.isNull( userLogin ) ) {
… display login form …
… when authenticated, instantiate a `UserLogin` and store as attribute …
if( authenticationSuccessful ) { // Testing some did-user-authenticate variable you defined in your login-form.
Instant whenAuthenticated = Instant.now() ; // Capture the current moment in UTC.
UserLogin userLogin = new UserLogin( userName , whenAuthenticated ) ;
VaadinSession.getCurrent().setAttribute( UserLogin.class , userLogin ) ; // Using class name as the `String` key tracking this `userLogin` object.
… switch content of the tab/window from authentication form to your main app content …
}
} else { // Else not null. User already authenticated. User may have hit "Reload" button in browser.
… display app content …
… perhaps log this event … maybe user needs to be trained to not hit Reload on a Single-Page Web App …
}
By the way… the discussion above about sessions is scoped to each user’s own connection to your web app in a single web browser tab/window.
At some point you may look for a hook into the lifecycle of your entire web app, before the first user connects and/or after the last user disconnects, learn about the hook defined in the Java Servlet spec. This hook is the ServletContextListener interface, where “context” means your web app as a whole. This is standard Java Servlet stuff, not at all specific to Vaadin, but Vaadin is actually a Servlet (perhaps the most sophisticated Servlet ever) so this context listener paradigm applies.
You write a class implementing that interface, by writing the before-first-user and the after-last-user methods. Identify your class to the web container by annotating #WebListener (or alternative means). Search Stack Overflow as this has been covered multiple times already.
Related
I'm building a prototype using Vaadin8 starting from a single-module template.
I'm trying to assign a unique UI instance (a session) to each authenticated user, so that each user is presented with a particular type of content according to their own settings within the app. Here's my configuration:
#WebListener
public class Market implements ServletContextListener {
public static ArrayList<User>users;
public void contextInitialized(ServletContextEvent sce) {
users=new ArrayList<User>();
User hau=new User("hau");
users.add(hau);
User moc=new User("moc");
users.add(moc);
}
public void contextDestroyed(ServletContextEvent sce){}
}
public class User {
public String username;
public user(String username){this.username=username;}
}
public class MyUI extends UI {
User us3r;
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
String username;
if (this.us3r==null) {username="Guest";}else {username=us3r.username;}
Label who=new Label(username);
TextField userfield=new TextField();
Button login=new Button("login");
login.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
for (User user:Market.users) {
if (userfield.getValue().equals(user.username)) {
us3r=user;Page.getCurrent().reload();return;
}
}Notification.show("No user "+userfield.getValue());
}
});
Button logout=new Button("logout");
logout.addClickListener(new ClickListener() {
public void buttonClick(ClickEvent event) {
if(us3r!=null) {us3r=null; Page.getCurrent().reload();}
}
});
layout.addComponent(userfield);
layout.addComponent(login);
layout.addComponent(who);
layout.addComponent(logout);
setContent(layout);
}
After inputting one of the two usernames registered in the Database, I'd like the Label object to display the name of the authenticated user, instead of "Guest". Another effect I'm trying to achieve is if a user is logged in and there is another request to the server, it should generate a fresh UI with the uninstantiated us3r attribute.
Caveats: I have been using Vaadin Flow lately rather than Vaadin 8. So my memory is hazy, and my code may be wrong. And I have kept all the examples overly simple, not ready for production. Lastly, I am sure others would take a different approach, so you may want to do some internet searching to see alternatives.
UI is malleable
The UI of Vaadin is more plastic and malleable than you may realize. You can entirely replace the initial VerticalLayout with some other widget-containing-view.
The way I have handled logins with Vaadin is that my default UI subclass checks for an object of my own User class in the web session. Being based on Jakarta Servlet technology, every Vaadin web app automatically benefits from the Servlet-based session handling provided by the Servlet container. Furthermore, Vaadin wraps those as a VaadinSession.
If the User object is found to be existing as an "attribute" (key-value pair) in the session, then I know the user has already logged-in successfully. So I display the main content in that initial UI subclass object. By "main content", I mean an instance of a particular class I wrote that extends VertialLayout, or HoriontalLayout or some such.
If no User object is found, then my initial UI subclass object displays a login view. By "login view" I mean an instance of some other particular class I wrote that extends VertialLayout, or HoriontalLayout or some such.
When you switch or morph the content within a UI subclass instance, Vaadin takes care of all the updating of the client. The change in state of your UI object on the server made by your Java code is automatically communicated to the Vaadin JavaScript library that was initially installed in the web browser. That Vaadin JS
library automatically renders your changed user-interface by generating the needed HTML, CSS, JavaScript, and so on. There is no need for you to be reloading the page as you seem to be doing in your example code. As a single-page web app, the web page only loads once. In Vaadin, we largely forget about the HTTP Request/Response cycle.
Example app
First we need a simple User class for demonstration purposes.
package work.basil.example;
import java.time.Instant;
import java.util.Objects;
public class User
{
private String name;
private Instant whenAuthenticated;
public User ( String name )
{
Objects.requireNonNull( name );
if ( name.isEmpty() || name.isBlank() ) { throw new IllegalArgumentException( "The user name is empty or blank. Message # b2ec1529-47aa-47c1-9702-c2b2689753cd." ); }
this.name = name;
this.whenAuthenticated = Instant.now();
}
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
User user = ( User ) o;
return name.equals( user.name );
}
#Override
public int hashCode ( )
{
return Objects.hash( name );
}
}
The starting point of our app, our subclass of UI checks the session and switches content. Notice how we segregated the check-and-switch code to a named method, ShowLoginOrContent. This allows us to invoke that code again after login, and again after logout.
package work.basil.example;
import com.vaadin.annotations.Theme;
import com.vaadin.annotations.VaadinServletConfiguration;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinServlet;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.UI;
import javax.servlet.annotation.WebServlet;
import java.util.Objects;
/**
* This UI is the application entry point. A UI may either represent a browser window
* (or tab) or some part of an HTML page where a Vaadin application is embedded.
* <p>
* The UI is initialized using {#link #init(VaadinRequest)}. This method is intended to be
* overridden to add component to the user interface and initialize non-component functionality.
*/
#Theme ( "mytheme" )
public class MyUI extends UI
{
#Override
protected void init ( VaadinRequest vaadinRequest )
{
this.showLoginOrContent();
}
void showLoginOrContent ( )
{
// Check for User object in session, indicating the user is currently logged-in.
User user = VaadinSession.getCurrent().getAttribute( User.class );
if ( Objects.isNull( user ) )
{
LoginView loginView = new LoginView();
this.setContent( loginView );
} else
{
CustomerListingView customerListingView = new CustomerListingView();
this.setContent( customerListingView );
}
}
#WebServlet ( urlPatterns = "/*", name = "MyUIServlet", asyncSupported = true )
#VaadinServletConfiguration ( ui = MyUI.class, productionMode = false )
public static class MyUIServlet extends VaadinServlet
{
}
}
Here is that LoginView, a VerticalLayout. We have our username & password, with a "Sign in" button. Notice how on successful authentication we:
Instantiate a User and add to the automatically-created session as an "attribute" key-value pair. The key is the class User, and the value is the User instance. Alternatively, you can choose to use a String as the key.
Invoke that showLoginOrContent method on MyUI to swap out our login view with a main content view.
In real work, I would locate the user-authentication mechanism to its own class unrelated to the user-interface. But here we ignore the process of authentication for this demonstration.
package work.basil.example;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.*;
public class LoginView extends VerticalLayout
{
private TextField userNameField;
private PasswordField passwordField;
private Button authenticateButton;
public LoginView ( )
{
// Widgets
this.userNameField = new TextField();
this.userNameField.setCaption( "User-account name:" );
this.passwordField = new PasswordField();
this.passwordField.setCaption( "Passphrase:" );
this.authenticateButton = new Button( "Sign in" );
this.authenticateButton.addClickListener( ( Button.ClickListener ) clickEvent -> {
// Verify user inputs, not null, not empty, not blank.
// Do the work to authenticate the user.
User user = new User( this.userNameField.getValue() );
VaadinSession.getCurrent().setAttribute( User.class , user );
( ( MyUI ) UI.getCurrent() ).showLoginOrContent(); // Switch out the content in our `UI` subclass instance.
}
);
// Arrange
this.addComponents( this.userNameField , this.passwordField , this.authenticateButton );
}
}
Lastly, we need our main content view. Here we use a "customer listing" that is not yet actually built. Instead, we place a couple pieces of text so you know the layout is appearing. Notice how in this code we look up the user's name from our User object in the session attribute.
We include a "Sign out" button to show how we reverse the authentication simply by clearing our User instance as the value of our "attribute" on the session. Alternatively, you could kill the entire session by calling VaadinSession::close. Which is appropriate depends on your specific app.
package work.basil.example;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.Button;
import com.vaadin.ui.Label;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import java.time.Duration;
import java.time.Instant;
public class CustomerListingView extends VerticalLayout
{
Button logoutButton;
public CustomerListingView ( )
{
// Widgets
this.logoutButton = new Button( "Sign out" );
this.logoutButton.addClickListener( ( Button.ClickListener ) clickEvent -> {
VaadinSession.getCurrent().setAttribute( User.class , null ); // Pass null to clear the value.
( ( MyUI ) UI.getCurrent() ).showLoginOrContent();
}
);
User user = VaadinSession.getCurrent().getAttribute( User.class );
Duration duration = Duration.between( user.getWhenAuthenticated() , Instant.now() );
Label welcome = new Label( "Bonjour, " + user.getName() + ". You’ve been signed in for: " + duration.toString() + "." );
Label placeholder = new Label( "This view is under construction. A table of customers will appear here.\"" );
// Arrange
this.addComponents( this.logoutButton , welcome , placeholder );
}
}
The effect of the "Sign out" button is to remove the main content, and take the user back to the login view.
Separation of concerns
One of the aims of the approach to logins is separation of concerns. The concern of building an interactive user-interface (Vaadin widgets and code) should be kept largely separate from the business logic of how we determine if a user is who they claim to be (authentication code).
Our UI subclass knows almost nothing about user-authentication. We moved all the mechanics of logging-in to other non-Vaadin-specific classes. The Vaadin-related code only has two connection points to authentication: (a) Passing collected credentials (username, password, or such), and (b) Checking for the presence of a User object in the session’s key-value store.
Multi-window web apps
By the way, you should know that Vaadin 8 has amazing support for multi-window web apps. You can write links or buttons to open additional windows/tabs in the browser, all working within the same web app and the same user session. Each tab/window has its own instance of a UI subclass you wrote. All of these UI subclass instances share the same VaadinSession object.
So using the logic seen above applies to all such tab/windows: Multiple windows all belonging to one session with one login.
Fake dialog boxes are not secure
You might be tempted to put your login view inside a dialog box appearing over your main content. Do not do this. A web dialog box is “fake”, in that it is not a window created and operated by the operating-system. A web app dialog window is just some graphics to create the illusion of a second window. The pretend dialog and the underlying content are actually all one web page.
A hacker might gain access to the content on the page, and might be able to defeat your login dialog. This is mentioned in the Vaadin manual, on the page Sub-Windows.
In my example above, we have no such security problem. The sensitive main content arrives on the user’s web browser only after authentication completes.
Web app lifecycle hooks
By the way, your use of ServletContextListener is correct. That is the standard hook for the lifecycle of your web app launching. That listener is guaranteed to run before the first user’s request arrives, and again after the last user’s response is sent. This is an appropriate place to configure resources needed by your app in general, across various users.
However, in Vaadin, you have an alternative. Vaadin provides the VaadinServiceInitListener for you to implement. This may be more convenient than the standard approach, though you need to configure it by creating a file to make your implementation available via the Java Service Implementation Interface (SPI) facility. Your VaadinServiceInitListener as another place to setup resources for your entire web app. You can also register further listeners for the service (web app) shutting down, and for user-session starting or stopping.
Browser Reload button
One last tip: You may want to use the #PreserveOnRefresh annotation.
Vaadin Flow
In Vaadin Flow (versions 10+), I take the same approach to logins.
Well, basically the same. In Vaadin Flow, the purpose of the UI class was dramatically revamped. Actually, that class should have been renamed given how differently it behaves. We no longer routinely write a subclass of UI when starting a Vaadin app. A UI instance is no longer stable during the user's session. The Vaadin runtime will replace the UI object by another new instance (or re-initialize it), sometimes quite rapidly, for reasons I do not yet understand. So I do not see much practical use for UI for those of us writing Vaadin apps.
Now in Flow I start with an empty layout, instead of a UI subclass. Inside that layout I swap nested layouts. First the login view. After authentication, I swap the login view for the main content view. On logout, the opposite (or close the VaadinSession object).
Using this link if we register a call back in grails, how to access springSecurityService in plain groovy/java class, so that we can get the current user domain class and save the login time?
Update:
I have done this using the below:
appCtx.springSecurityService.currentUser.id
If you are using the callback closures you can get the information from the AuthenticationSuccessEvent.
grails.plugin.springsecurity.onAuthenticationSuccessEvent = { e, appCtx ->
// handle AuthenticationSuccessEvent
println "User id ${e.authentication.principal.id} was authenticated"
}
I am using Castle ActiveRecord in my Asp.net / MVC 2 / Multi-tenancy application with SQL Server as my backend.
For every user logging in, the app loads the corresponding DB, dynamically at run time like below:
IDictionary<string, string> properties = new Dictionary<string, string>();
properties.Add("connection.driver_class", "NHibernate.Driver.SqlClientDriver");
properties.Add("dialect", "NHibernate.Dialect.MsSql2005Dialect");
properties.Add("connection.provider", "NHibernate.Connection.DriverConnectionProvider");
properties.Add("proxyfactory.factory_class", "NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle");
properties.Add("connection.connection_string", strDBConnection);
InPlaceConfigurationSource source = new InPlaceConfigurationSource();
source.Add(typeof(ActiveRecordBase), properties);
ActiveRecordStarter.Initialize(new System.Reflection.Assembly[] { asm1 }, source);
The strDBConnection string comes from another small database that holds the user info, corresponding DB, etc.
Scenario:
When a user logs in, his DB gets loaded, he can do his CRUD jobs -- No Probs !
Another user logs in (from another remote machine) his DB gets loaded -- No Probs !
Now, when the first user reads from DB, he sees new data from the second user's DB
My little understanding for this behavious is : ActiveRecordStarter is a Static object.
Could someone help me with a solution for this situation ?
The expected behaviour:
each user should access his own DB only, securely, in parallel / at the same time.
Thanks a lot !
ActiveRecordStarter.Initialize should only be called once in your app (in Application_Start in Global.asax).
To achieve what you want, create a class that inherits from NHibernate.Connection.DriverConnectionProvider:
public class MyCustomConnectionProvider : DriverConnectionProvider
{
protected override string GetNamedConnectionString(IDictionary<string, string> settings)
{
return string.Empty;
}
public override IDbConnection GetConnection()
{
// Get your connection here, based on the request
// You can use HttpContext.Current to get information about the current request
var conn = Driver.CreateConnection();
conn.ConnectionString = ... // Retrieve the connection string here;
conn.Open();
return conn;
}
}
Then set the connection.provider property to the name of your class:
properties.Add("connection.provider", "MyCompany.Domain.MyCustomConnectionProvider, MyCompany.AssemblyName");
In my grails app I have customized the post authorization workflow by writing a custom auth success handler (in resources.groovy) as shown below.
authenticationSuccessHandler (MyAuthSuccessHandler) {
def conf = SpringSecurityUtils.securityConfig
requestCache = ref('requestCache')
defaultTargetUrl = conf.successHandler.defaultTargetUrl
alwaysUseDefaultTargetUrl = conf.successHandler.alwaysUseDefault
targetUrlParameter = conf.successHandler.targetUrlParameter
useReferer = conf.successHandler.useReferer
redirectStrategy = ref('redirectStrategy')
superAdminUrl = "/admin/processSuperAdminLogin"
adminUrl = "/admin/processAdminLogin"
userUrl = "/admin/processUserLogin"
}
As you can from the last three lines in the closure above, depending on the Role granted to the logging in User I am redirecting her to separate actions within the AdminController where a custom UserSessionBean is created and stored in the session.
It works fine for a regular login case which in my app is like so:
User comes to the app via either http://localhost:8080/my-app/ OR http://localhost:8080/my-app/login/auth
She enters her valid login id and password and proceeds.
The app internally accesses MyAuthSuccessHandler which redirects to AdminController considering the Role granted to this User.
The UserSessionBean is created and stored it in the session
User is taken to the app home page
I have also written a custom MyUserDetailsService by extending GormUserDetailsService which is correctly accessed in the above flow.
PROBLEM SCENARIO:
Consider a user directly accessing a protected resource (in this case the controller is secured with #Secured annotation) within the app.
User clicks http://localhost:8080/my-app/inbox/index
App redirects her to http://localhost:8080/my-app/login/auth
User enters her valid login id and password
User is taken to http://localhost:8080/my-app/inbox/index
The MyAuthSuccessHandler is skipped entirely in this process and hence my UserSessionBean is not created leading to errors upon further use in places where the UserSessionBean is accessed.
QUESTIONS:
In the problem scenario, does the app skip the MyAuthSuccessHandler because there is a target URL for it to redirect to upon login?
Can we force the process to always pass through MyAuthSuccessHandler even with the target URL present?
If the answer to 2 is no, is there an alternative as to how and where the UserSessionBean can still be created?
You can implement a customized eventListener to handle the post-login process, without disrupting the original user requested url.
In config.groovy, insert a config item:
grails.plugins.springsecurity.useSecurityEventListener = true
In you resources.groovy, add a bean like this:
import com.yourapp.auth.LoginEventListener
beans = {
loginEventListener(LoginEventListener)
}
And create a eventListener in src/groovy like this:
package com.yourapp.auth
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent
import org.springframework.web.context.request.RequestContextHolder as RCH
class LoginEventListener implements
ApplicationListener<InteractiveAuthenticationSuccessEvent> {
//deal with successful login
void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
User.withTransaction {
def user = User.findByUsername(event.authentication.principal.username)
def adminRole = Role.findByAuthority('ROLE_ADMIN')
def userRole = Role.findByAuthority('ROLE_USER')
def session = RCH.currentRequestAttributes().session //get httpSession
session.user = user
if(user.authorities.contains(adminRole)){
processAdminLogin()
}
else if(user.authorities.contains(userRole)){
processUserLogin()
}
}
}
private void processAdminLogin(){ //move admin/processAdminLogin here
.....
}
private void processUserLogin(){ //move admin/processUserLogin here
.....
}
}
Done.
1) Yes, because it is an "on-demand" log in.
2) Yes, you can set it to always use default. The spring security plugin has a setting for it "successHandler.alwaysUseDefault" change that to true it defaults to false.
Also if you need more details check out the spring docs look for the Setting a Default Post-Login Destination section.
3) If you want to still create the user session bean and then redirect to the original URL you have two options create the bean in an earlier filter or expose the needed data via a custom UserDetailsService. Personally I would go the route of a custom details service.
I'm getting a "Message signature was incorrect" exception when trying to authenticate with MyOpenID and Yahoo.
I'm using pretty much the ASP.NET MVC sample code that came with DotNetOpenAuth 3.4.2
public ActionResult Authenticate(string openid)
{
var openIdRelyingParty = new OpenIdRelyingParty();
var authenticationResponse = openIdRelyingParty.GetResponse();
if (authenticationResponse == null)
{
// Stage 2: User submitting identifier
Identifier identifier;
if (Identifier.TryParse(openid, out identifier))
{
var realm = new Realm(Request.Url.Root() + "openid");
var authenticationRequest = openIdRelyingParty.CreateRequest(openid, realm);
authenticationRequest.RedirectToProvider();
}
else
{
return RedirectToAction("login", "home");
}
}
else
{
// Stage 3: OpenID provider sending assertion response
switch (authenticationResponse.Status)
{
case AuthenticationStatus.Authenticated:
{
// TODO
}
case AuthenticationStatus.Failed:
{
throw authenticationResponse.Exception;
}
}
}
return new EmptyResult();
}
Working fine with Google, AOL and others. However, Yahoo and MyOpenID fall into the AuthenticationStatus.Failed case with the following exception:
DotNetOpenAuth.Messaging.Bindings.InvalidSignatureException: Message signature was incorrect.
at DotNetOpenAuth.OpenId.ChannelElements.SigningBindingElement.ProcessIncomingMessage(IProtocolMessage message) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\OpenId\ChannelElements\SigningBindingElement.cs:line 139
at DotNetOpenAuth.Messaging.Channel.ProcessIncomingMessage(IProtocolMessage message) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\Messaging\Channel.cs:line 992
at DotNetOpenAuth.OpenId.ChannelElements.OpenIdChannel.ProcessIncomingMessage(IProtocolMessage message) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\OpenId\ChannelElements\OpenIdChannel.cs:line 172
at DotNetOpenAuth.Messaging.Channel.ReadFromRequest(HttpRequestInfo httpRequest) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\Messaging\Channel.cs:line 386
at DotNetOpenAuth.OpenId.RelyingParty.OpenIdRelyingParty.GetResponse(HttpRequestInfo httpRequestInfo) in c:\Users\andarno\git\dotnetopenid\src\DotNetOpenAuth\OpenId\RelyingParty\OpenIdRelyingParty.cs:line 540
Appears that others are having the same problem: http://trac.dotnetopenauth.net:8000/ticket/172
Does anyone have a workaround?
Turns out this was an issue with using DotNetOpenAuth in a web farm environment.
When you create your OpenIdRelyingParty make sure you pass null in the constructor.
This puts your web site into OpenID stateless or 'dumb' mode. It's slightly slower for users to log in (if you even notice) but you avoid having to write an IRelyingPartyApplicationStore to allow DotNetOpenAuth to work across your farm;
var openIdRelyingParty = new OpenIdRelyingParty(null);
All this discussion revolves around the following question:
How does Relying Party (RP) make sure the request containing the authentication token is coming from the OP(OpenId Provider ) to which he forwarded the user’s request to?
Following steps explains how it happens
User Request comes to the Replying Party (RP), our website in our case
Application stores a unique signature corresponding to this user in a local signature store (LSS) and then embeds this signature in the Message and forward this Message to OpenId Provider(OP)
User types his credentials and the OP authenticates his Message and then forwards this Message, which has the signature still embedded in it, back to RP
RP compare the signature which is embedded in the Message to the signature which is in LSS and if they match RP authenticate the user
If the LSS vanishes (somehow) before the Message comes back from OP there is nothing for RP to compare the signature with thus it fails to authenticate user and throws error: Message signature was incorrect.
How can LSS Vanish:
ASP.net refreshes the application pool
IIS is restarted
In web farm the Message is served by application hosted on different server
Two solutions to this issue:
RP run’s in dumb mode
a. It does not store and signature locally and thus does not use signature comparison to make sure the Message is coming from the OP to which he forwarded the user to for authentication
b. Instead, once RP received the authentication Message from the OP it send the Message back to OP and ask him to check if he is the one who has authenticate this user and is the originator of the Message. If OP replies Yes I am the originator of this Message and I have created this message then the user is authenticated by RP
Implement your own persistence store that does not vanish, not matter what ASP.net does to the process, much like using SQL to store session state.
We fixed this issue by implementing IRelyingPartyApplicationStore (IOpenIdApplicationStore in newer versions of DotNetOpenAuth) and adding the store class name to the .config
<dotNetOpenAuth>
<openid ...>
<relyingParty>
...
<store type="some.name.space.MyRelyingPartyApplicationStore, some.assembly"/>
</relyingParty>
</openid>
...
</dotNetOpenAuth>
The interface is a composition of two other interfaces with five members all together.
/// <summary>
/// A hybrid of the store interfaces that an OpenID Provider must implement, and
/// an OpenID Relying Party may implement to operate in stateful (smart) mode.
/// </summary>
public interface IOpenIdApplicationStore : ICryptoKeyStore, INonceStore
{
}
We used dumb mode as a quick fix to get up an running, but in the end you'll probably want something like this.