I am using Nimbus-JOSE-JWT in a spray.io app and am quite happy with it. The object performing authentication extends HttpAuthenticator and if it finds a correct JWT it returns the token subject and related info, otherwise None (authentication fails). With Play2 you can implement HTTP Basic Auth with something like this. Regarding token setting/getting which I assume is of more interest to you:
First, create a private/public key pair (I used parts of this code). Create the authentication object that loads the keys on initialization from the filesystem.
Create a a com.nimbusds.jose.crypto.MACSigner and a com.nimbusds.jose.crypto.MACVerifier using these keys.
Whenever you want to set a key, FIRST encrypt it, THEN sign it. Encryption:
private def encrypt(subject: String) = {
val header = new JWEHeader(JWEAlgorithm.RSA_OAEP, EncryptionMethod.A128GCM)
val jwt = new EncryptedJWT(header, claimSet(subject))
val encrypter = new RSAEncrypter(publicKey.asInstanceOf[java.security.interfaces.RSAPublicKey])
jwt.encrypt(encrypter)
jwt.serialize()
}
The claimSet method predictably returns a set of claims:
def claimSet(subject: String) = {
val jwtClaims = new JWTClaimsSet()
jwtClaims.setIssuer(Config.JWT.issuer)
jwtClaims.setSubject(subject)
jwtClaims.setJWTID(java.util.UUID.randomUUID().toString())
jwtClaims
}
The publicKey
property is the value returned from KeyFactory.getInstance("RSA").generatePublic
.
Signing:
private def sign(jwt: String) = {
val jwsObject = new JWSObject(new JWSHeader(JWSAlgorithm.HS256), new Payload(jwt))
jwsObject.sign(Tokens.signer)
jwsObject.serialize
}
Given the above, when you receive a key you need to verify the signature first, then decrypt it. For verification, first you try to parse it with com.nimbusds.jose.JWSObject.parse(my_token)
and as long as it doesn't throw a ParseException
you call verify
on the JWSObject that parse
returns, using as an argument the MACVerifier
that you created earlier. If verify
returns true
, you only need to call getPayload.toString
on the same JWSObject to get the verified payload.
To decrypt the verified payload you call com.nimbusds.jwt.EncryptedJWT.parse
on it, then something like:
val decrypter = new RSADecrypter(privateKey.asInstanceOf[java.security.interfaces.RSAPrivateKey])
jwt.decrypt(decrypter)
privateKey
is the value returned from KeyFactory.getInstance("RSA").generatePrivate
.
You can then get the claim set with jwt.getJWTClaimsSet
.
Finally, with regard to setting the Authorization header, I'm doing this on my AngularJS client using principles also mentioned in this post.