Play Framework & JSON Web Token - scala

Is there a Scala implementation of JWT or at least an example with Play? After having posted this question I searched further on the Internet and found some intros to JWT. Any suggestion would be really appreciated.

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.

Related

Verifying Hydra-generated JWT access_token signature in jwt.io?

I configured Hydra to return JWT in access_token (STRATEGIES_ACCESS_TOKEN=jwt). Pasting the resulting token[1] in jwt.io works—it's decoded successfully and the data looks right. Now I want to verify the signature.
So I open http://localhost:9000/.well-known/jwks.json and extract the public RSA keys[2][3], but none of them works—jwt.io gives me "Invalid Signature".
FYI the same error is also thrown by FusionAuth JWT library. My Scala code to get the keys:
private val hydraVerifiers = {
val keys = retrieveKeysFromJWKS("http://localhost:9000/.well-known/jwks.json")
keys.asScala.map(JSONWebKey.parse).map(_.asInstanceOf[RSAPublicKey]).map(RSAVerifier.newVerifier)
}.toSeq
(If you're not familiar with Scala/FusionAuth, the code above retrieves keys from a URL, parses them to java.security.PublicKey object, and creates JWT verifiers based on the public keys)
Any suggestions?
[1]
eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpjMzQ5NWJkMC0wZDFhLTRiOWMtOWZhNy1hZjE5ZWNlODMxMTEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOltdLCJjbGllbnRfaWQiOiJpZC5ldmVyeS5leHQuc3RlYW0iLCJleHAiOjE2Mjc3NTI0ODEsImV4dCI6e30sImlhdCI6MTYyNzc0ODg4MCwiaXNzIjoiaHR0cDovLzEyNy4wLjAuMTo5MDAwLyIsImp0aSI6IjAxNjRkMzk4LTY4OWQtNDUzZi05YmMxLWVjYTgyMGJkOWY1OSIsIm5iZiI6MTYyNzc0ODg4MCwic2NwIjpbImVpZC5leHRlbmQiLCJvZmZsaW5lX2FjY2VzcyJdLCJzdWIiOiIweDMyY2ZkMSJ9.h2XUovsF7e5OI60BnKakbHVQJoNJmxQSsmLMy2MxyxFHQ4VMDdISPbkJVD5fsPCdLVw3RcggSid8S7fHJpDWqgJD0UP1WZBqlYN6E2FYs25QRsn8tvOlb9RqDAq1sqhSA1DhPCSsg0OB37gqQq4M8TjYfH0gqrj6oiU9YvgvlKU2Q7uaXfUjxWNkW_6KCZsdUSAGsHtIT18BWudcry7xafVsbrSdp7HkdRhbbdGCh9-q7lTWx6dJB5gOUTI4TYWCzOZnos48NilIhrk_sZ6V7qk6cxkmGVQflQZ9sLmHGewY6IF7j5OKBDcl-fNGfOyvhn2HkTGzTb_H1uB4dxm1NkopPf4dQHU4A63CnoJLqS_Rhg7aYvdcSyJTtiEGXOQjApP9QkawePsd7JYlSHE7XvYxW7frM0VVO04Fu8HjftMliYKXHct0w9bVeykwRGF_m63VGaain0DjE6H5UKLNdNQvg5r4Azu-hBiR5sH5cL6j26ffEx_P-XnIK5cHyX2A9GHk9V_p6_SiLtKpEuKO4sZPBU4zc6z0J1_aFn-AAtFRpKvHKAfUNCnVacEkXO4k6EfJ3qLkrF2KNIfYCqcTTFl8tPdmbb_n020xdCZNpVKPW_G9qRqpf7okwx9ppGO6OD__uIXNEOcuKD-9pVk2qsdl4RqHAvVO6GNurbcAXQU
[2]
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAx33RsetBbPDnYSTJh6Y8esAxiqkWV5pawNUXoDvqZ46lPm1B2tTC0JDlpF5YmuoMEPIqFJJ5BiJpo8CN+5CGDnxSFfiamRomCa2ij6s+XmcPAC41POwpQn0+ijHd/wjh/lEFl+w6xhNhDoLTjxDYIDS8OQ5EhYH8vnHrNnOeh3X7f9kLC00M1S8f0uxbEzjqsfRjSUghIAn8NB3u/wj5/1G++guXDv8WRjadAWjzIhUy9MERIdhK6Mitp9xwPyXLFCQViYLeq9gTGYUqh4G9LzHPAlJW9qWwNM5ij3K+X4Qeu9jJaHCL2ZPywkSkoa6veDjnFs7uS695FqM+5NGyEWPeriCJ1zkL2n8PiGRDSk9jFlQi93+BFyIfUkTPD32CfLV5dYs+a3vV5Uy3cxC+JgxnqVlXY7k3MoaxU08oOMOuGNdCG9RoTtWuERtLa4wkGCKwfdnMzwhWQzKhQGPGRUTq+CxI4WCghfBfqBef74bldmmH/t42FfuCjWvVi6PlDi0YJtixrayJeVBy89dp0Xh1O+g4S9KRx5O1hiJPSNz6Q5O8lHSp4naQhsCGA53JoFofZSj9nBrL6o/Vlh0RPX0c9I2rpPr8YPGC3pxPvbMeP34hbW15F6JSSA9gACc+A2RYWhgZbR2QojJLhhQggSaWZ0g9TF+XG8WQsLISHDUCAwEAAQ==
[3]
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0+Xpe52O+qFGT4WHoJ2bsIC7/Vb51RulDKx/kNNchoERh1pg4TRL2WjXW7CK72CMz7cpNfsuxeC0RXLRMU+aAXyrsWkVm9u0IZsruOHOFjOeWFlqI5ZdnnkHYaH3GQhfXUcnJFQGHpzTn1hWlGT2UWuuSxqYcQ/SUeD7r5rvzOSAPAztLTAzsMmMrDPRz9KWDpmhLPh32WE903eooLAYsneNkGBvaWbc3PF+39P+5z730pWjgaeDsFHJug9c34AxmWP3NfudguW6/Ud9GR250iBHqPBcInafry9orWb+qndWXZLv74gCxovxdA0xuJnO+P/KeYZ5fCI4Lv8VT8H8ascy4LenUjyoR9dUsLpvOyYjNg7qPfZFyi0fN/feyh5M4MAkqPi3MmLzYWI9vlzmUBSzJ8TxHoonBHYIHwjif3ovRavmDSsuEQ2iZcz7P27hx569B50a66VUcXJVxoxSOgsjBD/WG6d97ACftZUaZxYk1SUrp5KGL1qoOlKGTDuIlJjP87/0Rg2TBMR8a6EdofSpAVC1RvB1AXupMK6xfMCRJzMAv4O54Y7esCT6u1hE/z9toQ5ArUPTg+rdskXg9eEQUaIykjJaxaFtzayCyhWFHBqdusZIDcU5e8lRaCzBXfMiXrlGj1E0wviynwfVf+Q8X/HZBXhEtlCfOm6ESe8CAwEAAQ==

How to open a JWT Token on Postman to put one of the claims value on a variable

To create a especific test on my application using Postman, after login and get the JWT token, I need to get a especific claim value to use in a variable in another POST on Postman.
Is that possible without develop a API to do it?
Thanks
Here is a simple function to do that.
let jsonData = pm.response.json();
// use whatever key in the response contains the jwt you want to look into. This example is using access_token
let jwtContents = jwt_decode(jsonData.access_token);
// Now you can set a postman variable with the value of a claim in the JWT
pm.variable.set("someClaim", jwtContents.payload.someClaim);
function jwt_decode(jwt) {
var parts = jwt.split('.'); // header, payload, signature
let tokenContents={};
tokenContents.header = JSON.parse(atob(parts[0]));
tokenContents.payload = JSON.parse(atob(parts[1]));
tokenContents.signature = atob(parts[2]);
// this just lets you see the jwt contents in the postman console.
console.log("Token Contents:\n" + JSON.stringify(tokenContents, null, 2));
return tokenContents;
}
The signature bit is still useless in this example, so you can not validate it with this, but it still addresses your question.
var jsonData = JSON.parse(responseBody);
postman.setEnvironmentVariable("token", jsonData.token);
Follow the:
https://blog.postman.com/extracting-data-from-responses-and-chaining-requests/
I've created a request in Postman that 'logs in' and, then, the tests section of the response contains the following
var data = JSON.parse(responseBody);
postman.clearGlobalVariable("access_token");
postman.setGlobalVariable("access_token", data.access_token);
This puts the access token in a global variable so you can use it anywhere. If you're looking to read something from the JWT's claim, it's a bit more complicated.Check out how to add a library at https://github.com/postmanlabs/postman-app-support/issues/1180#issuecomment-115375864. I'd use the JWT decode library - https://github.com/auth0/jwt-decode .

Using Proxy Authorization Header with ScalajHTTP

I'm getting a 407 error using scalajHTTP. I read through the repository and it seems like I should be able to pass the basic auth credentials as a base64 encoded value. I've also tried using the helper method described in the GitHub issues .proxyAuth but that is no longer part of HTTPRequest in ScalaJ according to error messages (as well as it not being in the documentation)
Any ideas? My endpoint URL is HTTPS as well as my proxy (for additional context)
val proxyHost= s"https://$forwardProxy"
val requestForward = Http(url).postData(redactedSecret)
.option(HttpOptions.allowUnsafeSSL)
.headers(("Content-Type", "application/json"), ("Proxy-Authorization", s"Basic $proxyAuth"))
.proxy(proxyHost, 8080).asString
val responseForward: HttpResponse[String] = requestForward
This issued posted in Github but still not resolved, https://github.com/scalaj/scalaj-http/issues/87
I found a solution to this problem. I researched around and after trying http client libraries, I kept getting 407 errors even though they all support proxy auth. Anyway, I ended up having to do the following.
add
import java.net.{Authenticator,PasswordAuthentication}
and the modified code body that I previously above looks like:
val requestForward: HttpRequest = Http(url).postData(data)
.header("Content-Type", "application/json")
.proxy(proxyHost, 8080)
.option(HttpOptions.allowUnsafeSSL)
Authenticator.setDefault(new Authenticator() {
override def getPasswordAuthentication(): PasswordAuthentication = {
new PasswordAuthentication( s"$username", s"$password".toCharArray())
}
})
So as you can see I removed the header from the original request object and instead overrode the credentials. Make sure you do this before you call on the response object.

jose4j JWT's claims set's attribute type other than string object

I have been using jose4j version 0.6.0 for Json Web Token(JWT) generation. All is good up-till token generation, token verification . JWT's claims payload can have number of elements like version, tokenId, issuer,permissions etc. I'm passing TokenPermissions object which is standard object in oneM2M release 2 specification i.e.
JwtClaims claims = new JwtClaims();
claims.setIssuer("DAS#ServiceProvider");
claims.setAudience("CSE001"); //
.....
.........
TokenPermissions tokenPerms = new TokenPermissions();
TokenPermission tokenPerm = new TokenPermission();
tokenPerm.getResourceIDs().add("RXYZ");
tokenPerm.setPrivileges(setOfAcr);// setOfACr is another object on oneM2M
tokenPerms.getPermission().add(tokenPerm);
claims.setClaim("permissions",tokenPerms);
Above snippet of code generates following JWT Claim Set
{iss=DAS#ServiceProvider, aud=CSE001, exp=1508999613, jti=H1wm_yaOe61Co-wND7wBAw#DAS#CDOT-SP, iat=1508996013, nbf=1508995953, sub=subject, email=mail#example.com, groups=[group-one, other-group, group-three], version=1.0.0, permissions=cdot.onem2m.resource.xsd.TokenPermissions#7f3b97fd}
Whole to the token passes the signature and claims validation but when is I try of typecast permission attribute to TokenPermissions it through error.
tokenPermsObject = jwtClaims.getClaimValue("permissions",TokenPermissions.class);
It through below error :
org.jose4j.jwt.MalformedClaimException: The value of the 'permissions' claim is not the expected type (xyz.xsd.TokenPermissions#7f3b97fd - Cannot cast java.lang.String to xyz.xsd.TokenPermissions.TokenPermissions)
What type of claims object could be passed in jose4j JWT, does I have to mandatorily pass text in claims set. Any help would be highly appreciated.
jose4j's JSON processing was derived from the JSON.simple toolkit and is fairly basic in how it converts between JSON and Java objects. It will do strings, numbers, booleans, maps and lists.
If you want/need to use a more sophisticated JSON library you can use setPayload(...) on JsonWebSignature when creating the JWT and give it the the JSON string you've produced elsewhere. And when consuming a JWT, String getRawJson() on JwtClaims will give you the JSON string payload that you can hand off to some other lib.

How to grab serialized in http request claims in a code using WIF?

ADFS 2.0, WIF (WS-Federation), ASP.NET: There is no http modules or any IdentityFoundation configuration defined in a web.config (like most WIF SDK samples show), instead everything is done via program code manually using WSFederationAuthenticationModule, ServiceConfiguration and SignInRequestMessage classes. I do http redirect to ADFS in a code and it seems to work fine, returning claims and redirecting user back to my web site with serialized claims in http request. So the question is how to parse this request using WIF classes, properties and methods and extract claims values from there? Thanks
Just in case want to share my experience, it might help somebody in the future. Well, solution I finally came to looks like this:
var message = SignInResponseMessage.CreateFromFormPost(Request) as SignInResponseMessage;
var rstr = new WSFederationSerializer().CreateResponse(message, new WSTrustSerializationContext(SecurityTokenHandlerCollectionManager.CreateDefaultSecurityTokenHandlerCollectionManager()));
var issuers = new ConfigurationBasedIssuerNameRegistry();
issuers.AddTrustedIssuer("630AF999EA69AF4917362D30C9EEA00C22D9A343", #"http://MyADFSServer/adfs/services/trust");
var tokenHandler = new Saml11SecurityTokenHandler {CertificateValidator = X509CertificateValidator.None};
var config = new SecurityTokenHandlerConfiguration{
CertificateValidator = X509CertificateValidator.None,
IssuerNameRegistry = issuers};
config.AudienceRestriction.AllowedAudienceUris.Add(new Uri("MyUri"));
tokenHandler.Configuration = config;
using(var reader=XmlReader.Create(new StringReader(rstr.RequestedSecurityToken.SecurityTokenXml.OuterXml)))
{
token = tokenHandler.ReadToken(reader);
}
ClaimsIdentityCollection claimsIdentity = tokenHandler.ValidateToken(token);
I found few similar code that uses SecurityTokenServiceConfiguration (it contains token handlers) instead of Saml11SecurityTokenHandler to read and parse token, however it did not work for me because of certificate validation failure. Setting SecurityTokenServiceConfiguration.CertificateValidator to X509CertificateValidator.None did not help coz Security Token Handler classes uses their own handler configuration and ignores STS configuration values, at least if you specify configuration parameters through the code like I did, however it works fine in case configuration is defined in web.config.