Device Authentication: Notify User of Login From New Device or Location
You can now determine if a user logs in from a new device or location.
Join the DZone community and get the full member experience.
Join For FreeSpring Security is the framework most used framework along with Spring MVC for authentication and authorization purposes. It provides ways to create an authentication manager for authenticating different providers like OpenId, LDAP, database, etc.
In this tutorial, we are going to learn how to manage security: Whenever a user logs in from a new device or new location, we need to send an email to the user. For this, we need to get the location of the user and the device used to authenticate.
Getting User Device Location
If the user is behind the proxy, then the Proxy servers add the X-FORWARD-FOR header to know the originating request IP.
If the user is not behind the proxy, then HTTPServletRequest.getRemoteAddr()
is the method to return the IP address of the client.
Getting the Device Details
When the browser sends a request to the server, it will send device details in the user-agent header. It contains the software vendor and the version of the browser/software sending the request.
Example: If we are using Windows as an Operating System, and Chrome as a browser, below is the user-agent that the browser sent to the server.
Also, please note that the version of Chrome we are using is 72.0:
user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36
We need to parse this to get the device information. Rather than implementing from scratch, we can use the ua-parser library to parse the user-agent information.
Add the below dependency in pom.xml file.
<dependency>
<groupId>com.github.ua-parser</groupId>
<artifactId>uap-java</artifactId>
<version>1.4.0</version>
</dependency>
We can use the below method to get the device information using the user-agent header.
private String getDeviceDetails(String userAgent)
{
String deviceDetails = null;
Parser uaParser = new Parser();
Client client = uaParser.parse(userAgent);
if (client!=null) {
deviceDetails = client.userAgent.family
+ " " + client.userAgent.major + "."
+ client.userAgent.minor + " - "
+ client.os.family + " " + client.os.major
+ "." + client.os.minor;
}
return deviceDetails;
}
Extracting User`s Location
Once we know the IP address of the device, we can estimate the location of the IP address using maxmind
:
Maven Dependency:
<dependency>
<groupId>com.maxmind.geoip2</groupId>
<artifactId>geoip2</artifactId>
<version>2.8.0</version>
</dependency
Add above dependency in the pom.xml file and download the Geolite2 Database from https://geolite.maxmind.com/download/geoip/database/GeoLite2-City.tar.gz.
Extract the archive and copy GeoLite2-City.mmdb to application resources. This file is used for detecting the location based on the IP address using the geopi2 API.
The following code is used for getting the device location:
File citytDatabase = new File("<path-to geolite2-city.mmdb-file>");
DatabaseReader citytDatabaseReader = new DatabaseReader.Builder(citytDatabase).build();
CityResponse response = citytDatabaseReader.city(ipAddress);
String countryName = response.getCountry().getName();
String cityName = response.getCity().getName();
String postal = response.getPostal().getCode();
String state = response.getLeastSpecificSubdivision().getName();
By combining the above code, we can write a method to return user city as below:
private String getdeviceLocationUsingIpAddress(String deviceIPAddress)
{
String deviceLocation = UNKNOWN;
InetAddress ipAddress = InetAddress.getByName(deviceIPAddress);
DatabaseReader citytDatabaseReader = new DatabaseReader.Builder(citytDatabase).build();
CityResponse cityResponse = citytDatabaseReader.city(ipAddress);
if (Objects.nonNull(cityResponse) &&
Objects.nonNull(cityResponse.getCity()) &&
!Strings.isNullOrEmpty(cityResponse.getCity().getName()))
{
deviceLocation = cityResponse.getCity().getName();
}
return deviceLocation;
}
Create an entity called DeviceInformation
. This is used to store the userId, device, location, date of LoggedIn
, etc. on the table.
You can read more here: Spring Custom Serializers with @JsonIdentityInfo.
We will use this information and send an email if the user is logged from a different IP address or from a new location.
We can write the Spring Data Repository for the DeviceInformation
entity database interaction to check the current device IP address, location, and whether they are approved by the user or not.
List<DeviceMetadata> approvedDevices = deviceMetadataRepository.findByUserId(userId);
for (DeviceMetadata approvedDevice : approvedDevices)
{
if (approvedDevice.getDeviceDetails().equals(deviceDetails)
&& approvedDevice.getLocation().equals(location))
{
return approvedDevice;
}
}
If the above approvedDevice
is null, it means the user logged in from a new location or device. Then, we can trigger the email to a user email address using any of the email servers, like Gmail, etc.
Customizing the Spring Security Login SuccessHandler
If we are using Spring Security for authentication and authorization purposes, then we will use the AuthenticationSuccessHandler
interface for sending the email to the user if logged in from a new device or location.
Implement the onAuthenticationSucess
method and verify the device and location of the user. Then, you will need to send an email if the entries are not found in the database.
Conclusion
In this article, we learned how to find the device location using Geolite2, parse the user-agent header and ua-parser Java library, and send an email if the user logs in from a new device or location.
Thanks for reading! The source code for this project can be found on GitHub.
Opinions expressed by DZone contributors are their own.
Comments