Now we are token - Authorization using JSON Web Token in Domino
The typical flow:
- you access a web resource
- provide some identity mechanism (in the simplest case: username and password)
- in exchange get some prove of identity
- that allows you to access protected resources.
I tend to compare this with a movie theater: if you want to enter the room where the movie plays you need a ticket. The guy checking it, only is interested: is it valid for that show. He doesn't care if you paid in cash, with a card, got it as a present or won in a lucky draw. Did you buy it just now, or online or yesterday, he doesn't care. He cares only: is it valid. Same applies to our web servers.
In the IBM world the standard here is an LTPA token that gets delivered as cookie. Now cookies (besides being fattening) come with their own little set of trouble and are kind of frowned upon in contemporary web application development.
The current web API token darling is JSON Web Token (JWT). They are an interesting concept since they sign the data provided. Be clear: they don't encrypt it, so you need to be careful if you want to store sensitive information (and encrypt that first).
Now how to put that into Domino?
The sequence matches the typical flow:- User authenticates with credentials
- server creates a JWT
- stores JWT and credentials in a map, so when the user comes back with the token, the original credentials can be retrieved
- delivers JWT to caller
- Caller uses JWT for next calls in the header
keytool
. To make my life easier I created a small script to generate all I need:
# !/bin/sh
# Generate a keystore file with a given password
keytool -genseckey -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg HMacSHA256 -keysize 2048 -alias HS256 -keypass $1
keytool -genseckey -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg HMacSHA384 -keysize 2048 -alias HS384 -keypass $1
keytool -genseckey -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg HMacSHA512 -keysize 2048 -alias HS512 -keypass $1
keytool -genkey -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg RSA -keysize 2048 -alias RS256 -keypass $1 -sigalg SHA256withRSA -dname "CN=,OU=,O=,L=,ST=,C=" -validity 360
keytool -genkey -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg RSA -keysize 2048 -alias RS384 -keypass $1 -sigalg SHA384withRSA -dname "CN=,OU=,O=,L=,ST=,C=" -validity 360
keytool -genkey -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg RSA -keysize 2048 -alias RS512 -keypass $1 -sigalg SHA512withRSA -dname "CN=,OU=,O=,L=,ST=,C=" -validity 360
keytool -genkeypair -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg EC -keysize 256 -alias ES256 -keypass $1 -sigalg SHA256withECDSA -dname "CN=,OU=,O=,L=,ST=,C=" -validity 360
keytool -genkeypair -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg EC -keysize 256 -alias ES384 -keypass $1 -sigalg SHA384withECDSA -dname "CN=,OU=,O=,L=,ST=,C=" -validity 360
keytool -genkeypair -keystore keystore.jceks -storetype jceks -storepass $1 -keyalg EC -keysize 256 -alias ES512 -keypass $1 -sigalg SHA512withECDSA -dname "CN=,OU=,O=,L=,ST=,C=" -validity 360
you call the function with the password you would like to use:
makekeys Sup3rs3cr3t
. In vert.x you provide a route protection (e.g. /api) and a login route. This is done quite easily. To get the whole picture about vert.x and verticles, you might want to check the tutorial first (comes in JavaScript flavour too).
// Getting the parameters from the environment, so it's not hardcoded
final String jwtPassword = System.getenv("JWT_PASSWORD");
final String jwtKeyStore = System.getenv("JWT_KEYSTORE");
// The JWT provider
final JsonObject keyStore = new JsonObject()
.put("type", "jceks")
.put("path", jwtKeyStore)
.put("password",jwtPassword);
// Needs to be instance level.. this.jwt
JWTAuth jwt = JWTAuth.create(vertx, new JsonObject().put("keyStore", keyStore));
// Setting up the routes
this.router = Router.router(vertx);
// Homepage - no authentication
this.router.route("/").handler(this::rootHandler);
// Body handling
router.route().handler(BodyHandler.create());
// Secured routes, except the login root
router.route("/api").handler(JWTAuthHandler.create(jwt, "/api/login"));
router.post("/api/login").handler(this::loginHandler);
In case you wonder about the syntax. That's Java8 goodness. The missing piece now is the loginHandler. There we face an extra challenge. Rule number one for all event loop driven enviornments (like NodeJS too): You do not block the event queue. Now an authentication can take a little longer, so we need to wrap that call into a future (Java's way of saying: I promise. Sounds much more complicated than it actually is:
public void loginHandler(final RoutingContext ctx) {
final JsonObject user = ctx.getBodyAsJson();
final HttpServerResponse response = ctx.response();
final String userName = user.getString("username");
final String password = user.getString("password");
this.vertx.executeBlocking(future -> {
final String execResult = this.validateNotesUser(userName, password);
if (execResult != null) {
future.complete(execResult);
} else {
future.fail("Failure");
}
}, res -> {
if (res.failed()) {
response.setStatusCode(401).end("Sorry, auth didn't work");
} else {
response.setStatusCode(200).end(res.result());
}
});
}
public String validateNotesUser(final String userName, final String password) {
String result = null;
NotesThread.sinitThread();
try {
// Throws an error if the user is not valid
final session = NotesFactory.createSession((String) null, userName, password);
final JWTOptions jwtOptions = new JWTOptions().setExpiresInSeconds(86400);
result = this.jwt.generateToken(new JsonObject(), jwtOptions);
} catch (final Exception e) {
result = null;
}
NotesThread.stermThread();
return result;
}
I skipped the part where token and user gets stored in a lookup map. That's an story for another time. Now the result coming back from successfully posting to /api/login is a bearer token. This token needs to be send in all future requests as a header:
Authorization: Bearer token
. There will be more about vert.x and DominoPosted by Stephan H Wissel on 24 February 2016 | Comments (1) | categories: IBM Notes Identity Management JWT vert.x