In a series of posts we’ll do “Firebase From Scratch”, an introduction to Firebase and its concepts and ideas. Hopefully, reading this series will give you a firm grasp of what Firebase is and what it can do.
- Part 1: Introduction
- Part 2: Diving In
- Part 3: A Maven Interlude
- Part 4: Games, Services and Tournaments
- Part 5: What’s In a Server Game Anyway?
- Part 6: Activating Games
- Part 7: Actions On The Table
- Part 8: Services
- Part 9: Custom Authentication
The for service you are likely to want to write is a so called “login handler”. And that’s because it’s how you customize authentication in Cubeia Firebase. So this post will walk you through how to write a simple authentication mechanism of your choice.
Introduction
In the last installment we mentioned plugin services. These are ready-made extension points in Firebase, they are services where the Contract is already defined but the distribution does not come with any default implementation. The Contract for customizing the authentication is called LoginLocator, and it is your task to write one when you want your own authentication plugged in.
The intersting method of the login handler looks like this:
public LoginHandler locateLoginHandler(LoginRequestAction request);
That’s fairly straight forward so let’s look at the login handler immediately:
public LoginResponseAction handle(LoginRequestAction reuqest);
So that’s were the action actually takes place. Why then have we split this into two different interfaces? This is because it is quite possible that different users should be authenticated different ways. For example, in gambling there is usually several “operators” per game, and a Firebase installation authenticates users against these “operators”. It is important that even if one operator should be down or slow to answer the authentication request, the other operators should not be affected.
- The login handler determines what kind of user the request is for, what “operator” it belongs to.
- It then creates a login handler per operator to make sure they are kept separate.
In this tutorial we’ll assume that all players belong to the same “operator” for simplicity.
Setup Project
We’re going to use Apache Maven for this example, so we’ll start with creating the service project like so (without line breaks please):
mvn archetype:generate -DarchetypeGroupId=com.cubeia.firebase.tools -DarchetypeArtifactId=firebase-service-archetype -DarchetypeVersion=1.8.0 -DarchetypeRepository=http://m2.cubeia.com/nexus/content/groups/public
This gives us an example project ready to be imported into your favorite IDE such as Eclipse or IntelliJ. However, we need to change it a bit to make it suitable for a login handler.
- Delete the created Contract. We don’t need it since we’ll be implementing a login locator instead. If you specified the package as “example” when you setup the project, the file to delete is: src/main/java/example/api/ServiceContract.java.
rm -f src/main/java/example/api/ServiceContract.java
- Make the service implement LoginLocator. Instead of implementing the contract we just removed, specify the login locator instead. Again, if your package is “example”, then the file to edit is: src/main/java/example/impl/ServiceImpl.java
public class ServiceImpl implements Service, LoginLocator { @Override public void init(ServiceRegistry serviceRegistry) { // ignore this method, won't be used! } @Override public LoginHandler locateLoginHandler(LoginRequestAction request) { // TODO Auto-generated method stub return null; } [...]
The first method “init” won’t be used. It’s a deprecated method, so just ignore it! The second method you recognize. But before we get to the implementation we need to edit the service meta data since we’ve changed the Contract we’re implementing.
- Edit the service metadata and point out the LoginLocator interface instead of the deleted Contract. The file to edit is: src/main/resources/firebase/META-INF/service.xml and you want to change the content of the “contract” element, like so:
<contract>com.cubeia.firebase.api.login.LoginLocator</contract>
Having done all this you should now be able to compile:
mvn package firebase:run
The above will package your service as a service archive and even start a firebase with it deployed! Of course, you can’t login yet since there’s no actual implementation, but let’s fix that!
Implementation
For the example, we’ll do it extremely simple: we’ll accept any username and ignore the password. However, we need to assign a user ID on login and for that we’ll simply take the hash code of the username.
So, we’ll start with implementing the login handler:
public class Handler implements LoginHandler { @Override public LoginResponseAction handle(LoginRequestAction request) { String username = request.getUser(); int userId = username.hashCode(); System.out.println("Loggin in '" + username + "' with ID " + userId); return new LoginResponseAction(true, username, userId); } }
That’s pretty basic huh? Now just return the handler in the login locator:
public class ServiceImpl implements Service, LoginLocator { private final Handler handler = new Handler(); @Override public LoginHandler locateLoginHandler(LoginRequestAction request) { return handler; }
You’ll notice that I create the handler first, in this instance you could actually create a new handler for each request. As the handler is stateless this would be perfectly fine and you wouldn’t need to handle concurrency. But if your handler has state, for example a database connection or a thread pool, then you need to keep one instance only.
- NB: If you have a state-full handler, you need to synchronize the code. The login handler might be called concurrently!
And we’re done! Just package and run one more time:
mvn clean package firebase:run
But of course we would like to test it as well. And we can do that using a shortcut: Our Wiki has a tool for checking a Firebase connection. On the first screen, just click “connect”, and on the second fill in any and username and password and click “login” and hey presto! We’re in. The lobby will be empty as we don’t have any games deployed, but you should be able to see text in the console indicating that you are now in fact authenticated.
That’s it. Now go ahead and add some real authentication logic!