IBM Verify

IBM Verify

Join this online user group to communicate across Security product users and IBM experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only
  • 1.  JWT as access token for userinfo endpoint

    Posted Mon December 02, 2019 09:11 AM
    Edited by Michael Boey***** Mon December 02, 2019 09:27 AM
    Hi,

    In the OIDC spec, the following is said about what the userinfo request should look like: 
    "The Client sends the UserInfo Request using either HTTP GET or HTTP POST. The Access Token obtained from an OpenID Connect Authentication Request MUST be sent as a Bearer Token.The OIDC spec says nothing about the format of the access token. 

    I am doing a test to make the access tokens in ISAM JWT by default, and I have followed this blog post of Leo Farrell to achieve just that: https://www.ibm.com/blogs/security-identity-access/oauth-jwt-access-token/
    I also have 'improved' that setup using the resources made available by Jack Yarborough: https://github.com/IBM-Security/isam-support/blob/master/config-example/federation/ws-trust/mapping/token-router.js , which is explained at the end of this video: https://www.youtube.com/watch?v=PglIsmf1ae8

    That all works. I can call the /demo/mobile-demo/diag endpoint using either an opaque token in the default format of EsvB2X3ae06dYWve3uku, or in the JWT format of eY........

    What do I want to achieve
    What I want to achieve is to be able to call the userinfo endpoint using the JWT token. I thought this should be a simple task, since I thought I could consider the /sps/oauth/oauth20/userinfo endpoint as just another back-end (similarly as /demo/mobile-demo/diag). However, this does not seem to be the case. 

    What error I get
    {
        "error_description""FBTOAU211E The [access_token] received of type [bearer] does not exist.",
        "error""invalid_token"
    }

    The userinfo endpoint seems to ignore the authorization decision made previously, and directly uses the token present in the authorization header to get userinfo claims. 

    What I have tried
    1) I generate an opaque token and I then put that opaque token in the JWT. The reason is that the userinfo endpoint works fine with an opaque token. I am able to retrieve the opaque token from the JWT, so I thought I would use that to forward it to the back-end /userinfo endpoint. However, replacing Authorization headers in ISAM is not straightforward, and I was not able to do so (tried http transformation rules and HTTP tags). Note that retrieving the opaque token from my JWT and using that against the /userinfo endpoint directly from curl works fine. 

    2) In the logs, right before the error is thrown, I see the following: 
    [12/2/19 14:51:01:617 CET] 00032849 id=00000000 .fim.trustserver.sts.modules.oauth20.OAuth20STSContextHelper < getFederationId RETURN https://localhost/sps/oauth/oauth20 2706 [12/2/19 14:51:01:618 CET] 00032849 id=00000000 com.tivoli.am.fim.oauth20.util.OAuth20ContextHelper I getOAuth20Token com.tivoli.am.fim.oauth20.exception.OAuth20InvalidTokenException: FBTOAU211E The [access_token] received of type [bearer] does not exist.

    So, it seems somewhere inside the ISAM oauth/oidc logic, it does use STS chains to validate the token using an AppliesTo of 'https://localhost/sps/oauth/oauth20'. I suppose this internal STS chain expects an opaque token. I hoped that I could somehow 'overwrite' this STS chain by creating a custom one which would successfully validate JWT tokens. So I created a 'Validate' chain with
    • appliesto = https://localhost/sps/oauth/oauth20
    • issuer address = urn:ibm:ITFIM:oauth20:client::* / .* / * (tried these three)
    This STS chain would then point to the token-router logic, which works for other calls (e.g. to the diag endpoint). 

    3) Changing the logic in the token mapping rules, but only the token-router logic is called, which successfully allows the request on the proxy. However, the userinfo endpoint seems to make its own decision again based on the authorization header (which contains a JWT, and not an opaque token). 

    None of the above options seem to work. I suspect the userinfo endpoint is encapsulated inside ISAM, but I do hope I'm overlooking some options which would allow me to call the userinfo endpoint using a JWT. 

    Anyone who sees options / knows how to do this?
    Kr,
    Michael

    ------------------------------
    Michael
    ------------------------------


  • 2.  RE: JWT as access token for userinfo endpoint

    Posted Wed December 18, 2019 05:09 PM
    Hi Michael, 

    What you could consider is to store the hash of the JWT token in the HVDB - this means ISAM will still be able to look up the token value in the userinfo request. 

    To do this you will need to: 

    1. Enable hashed tokens by setting the advanced configuration   oauth20.hashedTokenStorageEnabled to true

    2. Change the JWT rule to store the access token. This is controlled by the property set in the jwt_at_pre.js rule:

    stsuu.addContextAttribute(new com.tivoli.am.fim.trustserver.sts.uuser.Attribute("urn:ibm:ITFIM:oauth20:custom:token:access_token","urn:ibm:ITFIM:oauth20:custom:token:persistent","false"));

    Remove this line, or change false to true and the token will now be stored hashed, and userinfo requests should work. 

    This does mean the tokens are now being stored, so be sure check your configured access token lifetime matches the lifetime of the JWT. You may also see an increase in size / load on your database as tokens are now being stored and cleaned up. 

    Hope this helps
     


    ------------------------------
    Leo Farrell
    ------------------------------



  • 3.  RE: JWT as access token for userinfo endpoint

    Posted Thu December 19, 2019 03:42 AM
    Edited by Peter Volckaert Thu December 19, 2019 03:44 AM
    Hi,

    I now remember I saw this problem passing by a while ago...Sorry!
    FYI: in the cases I saw, this had to do with the size of the JWT: if it's too big, it cannot be stored in the HVDB.​ So most of the time I guess you must set oauth20.hashedTokenStorageEnabled to true when using JWT's as access tokens.

    For completeness:
    - The hashing algorithm used is SHA-256 by default, but it can be set to SHA-1 or SHA-512 by configuring advanced parameter runtime.hashAlgorithm
    - the JWT will be digested with the selected hash algorithm first, then the hashed bytes will be Base64 encoded into a string, the Base64 encoded string is what's being stored.
    - A fairly easy way to verify if an access token has been stored in the HVDB, is to use the REST API "Retrieve a list of OAuth grants"; by doing a GET to <ISAM LMI>/iam/access/v8/grants/. (Most probably your JWT was not stored in the HVDB, and that explains the "not found" error)

    Kind regards, Peter

    ------------------------------
    Peter Volckaert
    Senior Sales Engineer
    Authentication and Access
    IBM Security
    ------------------------------



  • 4.  RE: JWT as access token for userinfo endpoint

    Posted Thu December 19, 2019 11:25 AM
    Hi Peter, Leo,

    Thank you very much for the solution. The userinfo endpoint works now. Turned out to be easier than I thought :). 

    Enjoy the holidays!

    ------------------------------
    Michael
    ------------------------------



  • 5.  RE: JWT as access token for userinfo endpoint

    Posted Thu December 19, 2019 06:17 PM

    Hi

    Just sharing some past experience here: play gently (or not at all) with the following RESTAPI:

    https://{appliance_hostname}/iam/access/v8/grants/


    Instead, if that is possible, try to use a more scoped version of the same:

    https://{appliance_hostname}/iam/access/v8/grants/userIds/{user_id}


    The reason being that the first one will attempt to return you all "Grants" in your DB. If you have a couple millions of them in your DB, then your Liberty runtime will likely have an indigestion problem and likely crash (out of memory) or then become so slow it will become unusable. That was the case at least in past versions of ISAM.

    The thing is: you really don't know how many grants you have until you try to retrieve them from your HVDB.

    Cheers


    ------------------------------
    Sylvain Gilbert
    ------------------------------