Uploaded image for project: 'OpenIDM'
  1. OpenIDM
  2. OPENIDM-13055

Scope validation for request-specific client authorization

    XMLWordPrintable

    Details

    • Type: Story
    • Status: Open
    • Priority: Major
    • Resolution: Unresolved
    • Affects Version/s: 7.0.0
    • Fix Version/s: None
    • Component/s: Module - Authorization
    • Labels:
    • Target Version/s:

      Description

      As a developer of OAuth2 client applications, I want to ask my users for specific consent to perform actions on their behalf when interacting with IDM.

      For example, if a client application wants to monitor notifications for a user, then that application should only ask for the "fr:idm:notifications" scope. As a result, if this client app tries to make some non-notifications-related request (for example, to update the user's profile) then that request should be denied (even if that user would normally be allowed to make the request, if they were operating on their own behalf).

      The logic associated with checking scopes included in the access token should be done in addition to whatever additional authorization logic is normally evaluated for the subject of the access token. There is no reason that a client acting on a user's behalf should have any wider authorization than the user themselves would have.

      As a proof of concept implementation of this scope checking mechanism, here is some configuration that can be referred to:

      authentication.json:

      {
          "rsFilter" : {
              "clientId" : "rsFilterClient",
              "clientSecret" : "rsFilterClient",
              "tokenIntrospectUrl" : "http://openam/am/oauth2/introspect",
              "scopes" : [ ],
              "queryOnResource" : "managed/user",
              "propertyMapping" : {
                  "authenticationId" : "userName",
                  "userRoles" : "authzRoles"
              },
              "anonymousUserMapping" : {
                  "localUser" : "internal/user/anonymous",
                  "userRoles" : "authzRoles"
              },
              "staticUserMapping" : [
                  {
                      "idpUser" : "amadmin",
                      "localUser" : "internal/user/openidm-admin",
                      "userRoles" : "authzRoles"
                  },
                  {
                      "idpUser": "idm-provisioning",
                      "localUser": "internal/user/openidm-admin",
                      "userRoles": "authzRoles"
                  }
              ]
          }
      }
      

      router.json:

      {
          "filters" : [
              {
                  "condition" : {
                      "type" : "text/javascript",
                      "source" : "context.caller.external === true"
                  },
                  "onRequest" : {
                      "type" : "groovy",
                      "file" : "scopeCheck.groovy",
                      "globals" : {
                          "routes" : [
                              {
                                  "pattern": "^managed/user/.+$",
                                  "methods": [
                                      "read"
                                  ],
                                  "scopes": [
                                      "fr:idm:profile"
                                  ]
                              },
                              {
                                  "pattern": "(^notification)|(^internal/notification)|(^endpoint/usernotifications)",
                                  "methods": [
                                      "read"
                                  ],
                                  "scopes": [
                                      "fr:idm:notifications"
                                  ]
                              },
                              {
                                  "pattern": "^consent$",
                                  "methods": [
                                      "action"
                                  ],
                                  "actions": [
                                      "getConsentMappings"
                                  ],
                                  "scopes": [
                                      "fr:idm:consent_read"
                                  ]
                              },
                              {
                                  "pattern": "^managed/user/.+$",
                                  "methods": [
                                      "patch"
                                  ],
                                  "scopes": [
                                      "fr:idm:profile_update"
                                  ]
                              }
                          ]
                      }
                  }
              },
              {
                  "condition" : {
                      "type" : "text/javascript",
                      "source" : "context.caller.external === true"
                  },
                  "onRequest" : {
                      "type" : "text/javascript",
                      "source" : "require('router-authz').testAccess()"
                  }
              },
              {
                  "pattern" : "^(managed|system|internal)($|(/.+))",
                  "onRequest" : {
                      "type" : "text/javascript",
                      "file" : "policyFilter.js"
                  },
                  "methods" : [
                      "create",
                      "update"
                  ]
              },
              {
                  "pattern" : "^(managed|internal)($|(/.+))",
                  "condition" : {
                      "type" : "text/javascript",
                      "source" : "context.caller.external === true || context.current.name === 'selfservice'"
                  },
                  "onResponse" : {
                      "type" : "text/javascript",
                      "source" : "require('relationshipFilter').filterResponse()"
                  }
              }
          ]
      }
      

      The main block added here for this PoC is the first item in the "filters" array within router.json. This calls to a script which uses the declaration of request/scope patterns to verify that the required scope for the request is present. Any request which does not include the required scope should fail.

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              brmiller Brendan Miller
              Reporter:
              jake.feasel Jake Feasel
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

                Dates

                Created:
                Updated: