REST API Security with OpenIG

This topic has 10 replies, 2 voices, and was last updated 5 years, 10 months ago by Joachim Andres.

  • Author
    Posts
  • #13070
     japearson
    Participant

    So, I went to the ForgeRock Summit in Sydney just over a month ago and it was awesome. While I was there I wanted to find out how I could implement REST API security in OpenAM and I chatted to Warren Strange and Joachim Andres.

    I’ve used OpenAM quite a lot since 2012, but I’ve never used OpenIG before or OAuth2/OpenID Connect.

    I’m putting together a small POC for a presentation next week to showcase REST API Security and I have a few questions that I’m hoping people can answer.

    The following is what I want to prove:

    1. Configuration of OpenID Connect in OpenAM, with group memberships, injected into the OpenID Connect JWT Token scopes (I think that is the right terminology, or should it be claims instead of scopes?)
    2. Sign into a test application using OpenID Connect (was thinking of using PAC4J for this bit
    3. Call a simple REST web service from the test application, with OpenIG performing authorisition, checking that the OIDC token contains certain group memberships in the scope

    So I have a few questions about this:

    • Is it possible to end up with a JWT token that is signed and contains my group memberships (or arbitrary attributes) that I can pass to OpenIG which can perform authorisation without needing to contact OpenAM? A stateless session maybe? Do I need to write an OIDC Claims Script? Or a plugin? Do I need to enable “Always return claims in ID Tokens”?
    • Will the OpenAM OIDC token be encrypted? If yes, are there easy ways to inspect the contents of the token for debugging purposes?
    • Does OpenIG have an administration console? I just started playing with OpenIG about an hour ago, and I can’t find an admin console, I’ve been using docker https://github.com/ForgeRock/docker/tree/master/openig for this, and this commit seems to suggest an admin console, but I’m not seeing anything once I start it.
    • How do I configure OpenIG to perform authorization for a REST web service based off a JWT token from the HTTP header? I would want it to check whether a specific group membership is in the OpenID Connect JWT token.

    Hopefully, that makes sense.

    #13079
     Joachim Andres
    Participant

    Glad you enjoyed the summit. Was good to meet you there.

    To your questions :
    1.) Yes, that would be via the OIDC Claims Script. This blog from Simon might be useful: https://forgerock.org/2015/12/scripted-openid-connect-claims-custom-jwt-contents/
    “Always return claims in ID Tokens” maps claims straight into the ID token, so you don’t have to go back to the userinfo endpoint/tokeninfo endpoint. The claims script allows you to set extra values.

    2.) OpenAM can issue signed id tokens, but not encrypted ones. See https://bugster.forgerock.org/jira/browse/OPENAM-9692

    3.) OpenIG does not have an administration console. However we are working on the OpenIG studio (UI based) which allows you to build configuration and rapidly prototype your config it against one instance. This is in development state at the moment. This is clearly not an administration console.

    4.) If you use OpenAM as the policy decision point, you can use the PolicyEnforcementFilter in OpenIG. OpenAM allows OIDC/JWT claims as subject conditions out of the box. The IG studio will provide support to build the corresponding IG configuration.
    However if you do not use OpenAM as the PEP, then you could use custom scripts to achieve what you need.

    You will soon be able to test the OpenIG Studio. Easiest is to follow the instructions here (always pulls the latest nightly) :
    http://identityrocks.blogspot.fr/2016/09/openig-on-docker-perfect-couple.html
    The studio will be available under openig/studio – I can demo it to you if this will help.

    Yes, makes perfect sense !

    #13103
     japearson
    Participant

    Thanks Joachim, it was great to meet you too.

    If I was to use OpenAM as the PDP, and use the OIDC claims as subjects will it handle multiple claims of the same name? Ie if I have:

    * group=GROUP1
    * group=GROUP2

    Does the OIDC Token support that? Or will it get compressed into one claim like GROUP1,GROUP2?

    I can see in OpenAM the OIDC Claim as a subject condition expects Claim Name and Value. Would it be expecting the entire Claim? Ie GROUP1,GROUP2 or could I have a subject condition separately for each group?

    The OpenIG studio sounds quite helpful, I’ll have to check it out. Was that demoed at the Summit on the Unconference day by the UI guy (sorry forgot his name)? I remember seeing some sort of XUI that looked a bit like OpenAM, but it wasn’t OpenAM.

    #13106
     japearson
    Participant

    I tried going to http://mydockerhost:8080/studio and I got “Welcome to OpenIG. Your path is /studio. OpenIG is using the default handler for this route.”

    When I go to http://mydockerhost:8080/openig/studio I get a 404.

    What’s the magic sauce to enable the studio? Is it a separate deployable?

    #13110
     japearson
    Participant

    I had a look at https://github.com/ForgeRock/openig/tree/master/openig-ui and followed to readme to run the UI standalone, but I’m not sure how to connect it to my docker instance. Maybe I should take you up on your demo offer.

    I had a look in the openig WAR and I found the ui jar in an odd location it was located in:

    /usr/local/tomcat/webapps/ROOT/WEB-INF/classes/openig-ui.jar

    instead of
    /usr/local/tomcat/webapps/ROOT/WEB-INF/lib/openig-ui.jar

    Is that expected?

    #13113
     Joachim Andres
    Participant

    At the moment (last nightly), the studio is still under /openig/console instead. We haven’t moved this to /openig/studio yet. We will to clearly mark the studio aspect in the coming days or weeks.

    Have you deployed with the Docker tutorial from the blog ?
    If yes, try http://localhost:8080/openig/console. Login with test/test. The login page will get removed as well eventually.

    If no, then you need an admin.json which makes the studio available on other network interfaces than the loopback, see https://stash.forgerock.org/projects/DOCKER/repos/docker/browse/openig/sample-config/config/admin.json . We will make this a little simpler going forward.

    #13127
     japearson
    Participant

    Hi Joachim,

    My docker machine is a different host, which probably explains why the console didn’t work. I did an ssh local port forward, and that made it work. Yes, I’m using the sample configuration from your blog post.

    From what I can tell the sample admin.json doesn’t allow non-localhost configuration. Your commit earlier this month suggests it only works localhost at the moment.

    https://stash.forgerock.org/projects/DOCKER/repos/docker/commits/c894be7ef8911849cd8694fbbc9ecf07d1d29670

    Also, does the console/studio actually do anything yet? It seems like it’s just fake at the moment? When I created a new application or changed existing settings and deployed/undeployed. The Chrome inspector didn’t show any REST web service traffic, and the config files didn’t change. Even though the UI said it saved successfully.

    Cheers,

    Joel

    #13156
     Joachim Andres
    Participant

    Hi Joel,

    The admin.json by default does indeed only allow connections via localhost. The admin.json from the git repo does mitigate this behavior though. Glad you got it to work.

    Once you constructed your route configuration, you can 2 things with the studio :
    1.) Export the route configuration in json and then either continue configuring manually for more complex scenarios and then drop it in your SCM system
    2.) Deploy the route configuration to your local instance via REST for the purpose of rapid prototyping.

    Concerning 2.), do you see a route file created on disk for your freshly deployed route ?

    Cheers,
    Joachim

    #13166
     japearson
    Participant

    Hi Joachim,

    You’re right, not sure what I did before, that admin.json does make it work for non-localhost. I have noticed that sometimes the UI doesn’t load and stays on a grey screen, I probably presumed it wasn’t working. After I reload it works fine. I also realised Chrome was caching a really old version of the Javascript, that didn’t have the actual persistence built in yet (maybe I tried something other than 5.0.0-SNAPSHOT first). Opening it up in incognito got past that issue.

    However, now when I click deploy it gets a 404. Here is the curl representation from the chrome inspector and the output.

    Appears as though the api isn’t deployed? I see no errors in any logs, how can I ramp up the logging?

    curl --noproxy localhost -v "http://localhost:8080/openig/api/system/objects/_router/routes/joeltest1474430840401" -X PUT -H "Cookie: i18next=en" -H "Origin: http://localhost:8080" -H "Acflate, sdch" -H "X-Requested-With: XMLHttpRequest" -H "Accept-Language: en-AU,en;q=0.8,en-US;q=0.6" -H "User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like .116 Safari/537.36" -H "Content-Type: application/json" -H "Accept: application/json, text/javascript, */*; q=0.01" -H "Cache-Control: no-cache" -H "If-None-Match: *" -H "Connection: keep-a://localhost:8080/openig/console/" --data-binary "{""name"":""joeltest1474430840401"",""baseURI"":""http://localhost:8080"",""condition"":""/joel"",""handler"":{""type"":""Chain"",""config""":""ThrottlingFilter"",""name"":""Throttling"",""config"":{""rate"":{""numberOfRequests"":60,""duration"":""1 m""}}}],""handler"":""ClientHandler""}}}" --compressed
    * STATE: INIT => CONNECT handle 0x600057780; line 1397 (connection #-5000)
    * Added connection 0. The cache now contains 1 members
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * STATE: CONNECT => WAITCONNECT handle 0x600057780; line 1450 (connection #0)
    * Connected to localhost (127.0.0.1) port 8080 (#0)
    * STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x600057780; line 1557 (connection #0)
    * Marked for [keep alive]: HTTP default
    * STATE: SENDPROTOCONNECT => DO handle 0x600057780; line 1575 (connection #0)
    > PUT /openig/api/system/objects/_router/routes/joeltest1474430840401 HTTP/1.1
    > Host: localhost:8080
    > Cookie: i18next=en
    > Origin: http://localhost:8080
    > Accept-Encoding: gzip, deflate, sdch
    > X-Requested-With: XMLHttpRequest
    > Accept-Language: en-AU,en;q=0.8,en-US;q=0.6
    > User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36
    > Content-Type: application/json
    > Accept: application/json, text/javascript, */*; q=0.01
    > Cache-Control: no-cache
    > If-None-Match: *
    > Connection: keep-alive
    > Referer: http://localhost:8080/openig/console/
    > Content-Length: 225
    >
    * upload completely sent off: 225 out of 225 bytes
    * STATE: DO => DO_DONE handle 0x600057780; line 1654 (connection #0)
    * STATE: DO_DONE => WAITPERFORM handle 0x600057780; line 1781 (connection #0)
    * STATE: WAITPERFORM => PERFORM handle 0x600057780; line 1791 (connection #0)
    * HTTP 1.1 or later with persistent connection, pipelining supported
    < HTTP/1.1 404
    < Transfer-Encoding: chunked
    < Date: Wed, 21 Sep 2016 04:14:22 GMT
    <
    * STATE: PERFORM => DONE handle 0x600057780; line 1955 (connection #0)
    * multi_done
    * Curl_http_done: called premature == 0
    * Connection #0 to host localhost left intact
    * Expire cleared

    The initial routes don’t load either:

    curl -v --noproxy localhost 'http://localhost:8080/openig/api/system/objects/_router/routes?_queryFilter=true&_fields=_id' -H 'Pragma: no-cache' -H 'Accept-Encoding: gzip, deflate, sdch' -AU,en;q=0.8,en-US;q=0.6' -H 'User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36' -H 'Accept: application/json, t0.01' -H 'Referer: http://localhost:8080/openig/console/' -H 'X-Requested-With: XMLHttpRequest' -H 'Cookie: i18next=en' -H 'Connection: keep-alive' -H 'Cache-Control: no-cache' --compressed
    * STATE: INIT => CONNECT handle 0x600057780; line 1397 (connection #-5000)
    * Added connection 0. The cache now contains 1 members
    *   Trying 127.0.0.1...
    * TCP_NODELAY set
    * STATE: CONNECT => WAITCONNECT handle 0x600057780; line 1450 (connection #0)
    * Connected to localhost (127.0.0.1) port 8080 (#0)
    * STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x600057780; line 1557 (connection #0)
    * Marked for [keep alive]: HTTP default
    * STATE: SENDPROTOCONNECT => DO handle 0x600057780; line 1575 (connection #0)
    > GET /openig/api/system/objects/_router/routes?_queryFilter=true&_fields=_id HTTP/1.1
    > Host: localhost:8080
    > Pragma: no-cache
    > Accept-Encoding: gzip, deflate, sdch
    > Accept-Language: en-AU,en;q=0.8,en-US;q=0.6
    > User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.116 Safari/537.36
    > Accept: application/json, text/javascript, */*; q=0.01
    > Referer: http://localhost:8080/openig/console/
    > X-Requested-With: XMLHttpRequest
    > Cookie: i18next=en
    > Connection: keep-alive
    > Cache-Control: no-cache
    >
    * STATE: DO => DO_DONE handle 0x600057780; line 1654 (connection #0)
    * STATE: DO_DONE => WAITPERFORM handle 0x600057780; line 1781 (connection #0)
    * STATE: WAITPERFORM => PERFORM handle 0x600057780; line 1791 (connection #0)
    * HTTP 1.1 or later with persistent connection, pipelining supported
    < HTTP/1.1 404
    < Transfer-Encoding: chunked
    < Date: Wed, 21 Sep 2016 04:26:53 GMT
    <
    * STATE: PERFORM => DONE handle 0x600057780; line 1955 (connection #0)
    * multi_done
    * Curl_http_done: called premature == 0
    * Connection #0 to host localhost left intact
    * Expire cleared
    
    • This reply was modified 5 years, 10 months ago by japearson.
    • This reply was modified 5 years, 10 months ago by japearson.
    #13171
     japearson
    Participant

    I noticed this in the logs on startup:

    [localhost-startStop-1] INFO o.f.o.handler.router.RouteBuilder - Monitoring endpoint available at '/openig/api/system/objects/mymainrouter/routes/20-simplethrottle/monitoring'

    I see that it uses mymainrouter instead of _router

    So I edited config.json

    So that instead of

    
    {
      "handler": {
        "name": "MyMainRouter",
        "type": "Router",
    

    It is

    
    {
      "handler": {
        "name": "_router",
        "type": "Router",
    

    And that meant that it could deploy apps properly. Although it doesn’t seem to render the routes properly when I refresh. It just shows the sample “Legacy Web App” and “WeatherAPI” apps. It looks like that comes from the mock/Data.js

    It also looks like the times it didn’t render was because there was an error in the console complaining about CommonConfig.js:121 Uncaught TypeError: Cannot read property 'url' of undefined:
    if (!Configuration.gotoURL && !hash.match(Router.configuration.routes.login.url)) {

    Sometimes I get socket timeouts for some reason:

    
    [http-nio-8080-exec-6] ERROR o.f.h.servlet.HttpFrameworkServlet - Failed to write response
    java.net.SocketTimeoutException: null
            at org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:134)
    Wrapped by: org.apache.catalina.connector.ClientAbortException: java.net.SocketTimeoutException
            at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:380)
    

    Although accessing it on localhost:8080 (via SSH) seems to be more reliable than via the hostname, otherwise, it seems to hang randomly, which is super weird.

    Any idea why it’s using “_router” instead of “mymainrouter”? Or why it’s using the mocked data?

    • This reply was modified 5 years, 10 months ago by japearson.
    #13175
     Joachim Andres
    Participant

    With IG5 we moved the default router’s routes endpoints to _router indeed. I just put in a PR to update the config.json in the git project on stash.forgerock.org. Thanks for pointing me to this one.

    Wrt to refresh, we have not implemented yet the capability to store the model you are working on. This is what we are working on in current and upcoming sprints. Idea is that you can continue to work on the route configuration you started working on earlier.

    Just FYI, when you re-build your docker container, make sure you use the –no-cache option so that it always pulls the latest nightly.

Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.

©2022 ForgeRock - we provide an identity and access platform to secure every online relationship for the enterprise market, educational sector and even entire countries. Click to view our privacy policy and terms of use.

Log in with your credentials

Forgot your details?