Spring Boot: Saving OAuth2 login data in DB using PrincipalExtractor

This post is a continuation of configuring OAuth2 login in Spring Boot. If you want to know how to configure the login itself, please take a look at the previous post.

As I mentioned previously, we want to save user login data in our database to be able to connect matches data with their users. We’re going to save only some basic data – the external id of the user from the provider to distinguish the user, her full name (that I want the user to be able to replace it with the nickname in the future), avatar and e-mail address. Let’s start with the database. As we already configured Liquibase, we are able to add a migration that will create the table in the DB:

As we have only one provider now, we could as well skip the login_type column. That would mean that if we add a new provider, we’d likely need to add this column and prepare migration that adds the value for existing records, so I decided that it’s a good idea to start with it. When we now start the application, the table is created in the database:

Next step is adding User entity class and its related Repository interface we’ll be using for DB access. As I wanted to use LocalDateTime for managing dates, but JPA doesn’t work that well with it yet, I needed to add Jsr310JpaConverters class to @Entityscan in my MatchloggerApplication class.

The last part was using PrincipalExtractor to obtain the data we want, actually save them into the database and mark that they will be used as principal data. The documentation of this class is virtually non-existent, but I found this example. I added a similar section to my WebSecurityConfiguration.java with just returning the same I received to check which data I am able to obtain.

That was the set of data provided by PrincipalExtractor. It appears that everything that I wanted to get is available here, but there is one potential issue I may come up with in future – as long as I have only one authentication provider, I know that those data come from Google, but if I have several providers and they provide different data, it might be the case that I’ll need to find out the provider based on some keys’ existence. Not the best option, but seems I need to stick with it for now (if anybody knows a better option, please let me know).

My WebSecurityConfiguration class looks like this now:

After adding the PrincipalExtractor, I am able to see that on successful login I have my first user in the database.

Let’s then modify the code that returns user data to use my new configuration:

After login, this is a current message that we see:

One note to add is that Java 8 LocalDateTime doesn’t look too nice in generated JSON response. I’ll likely convert the returned value into a timestamp in the future, depending on view layer’s needs.

Capturing and comparing entire objects with Mockito and AssertJ

Let’s say we have a project in which we have the following classes:

We don’t yet have an implementation of the Runway interface (that will be created by another team), but we would like to test if our code creates a correct Plane object and passes it to the Runway. We’ll be using Mockito to mock the Runway implementation to test only the Airport class (so, write an actual unit test). I really like AssertJ so I’ll be using that one for assertions instead of usual JUnit assertions.

Let’s start with testing if plane departs correctly:

We now know that a plane departed, but we don’t yet know if that was the right plane. Let’s try comparing to the plane we expect:

The result is a failing test – not that we wouldn’t expect that.

There are two separate instances of Plane created here. While their fields are all the same, they are different objects and Mockito’s verify method checks that. Moreover, if we wanted to check only if a specific field of passed object has an expected value, we wouldn’t have an ability to check that with Mockito’s verify. There is a class called ArgumentCaptor though that can help us. Also, AssertJ has a neat method for comparing if all fields of two objects are the same:

The dish is ready to be served, enjoy.