WebService Authorization - Demo

WebService Authorization - Demo

In the last post [https://www.dhirubhai.net/pulse/webservice-authorization-common-pain-wael-eldoamiry] we discussed some of the WebServices authorization challenges, We also explained how a generic approach based on open specifications can save lots of efforts and provide a clean, decoupled implementation of authorization for web workload in general and more specifically the WebServices implementation.

In this demo we will show how authorization services can be configured in Red Hat Single Sign-on - RHSSO, and then integrated in spring boot application with zero code changes, using a declarative security method relying on external configuration files to enable/disable authorization and associated rules and control it both at deployment and runtime.

Don't forget, by end of this demo we will see how to secure Rest APIs using role based access control in addition to script (rule) based access control in the form of api-key, this is done declaratively and with zero code changes!.

Steps

  • Create a realm in RHSSO
  • Create openid-connect client 
  • Enable Authorization services for this client 
  • Define following resource
  1. Admin resource mapped to /api/admin uri
  2. User resource mapped to /api/user uri
  3. Claim resource mapped to /api/claim uri
  • Define following policies
  1. Admin Role based policy
  2. User Role based policy
  3. Script claim based policy
  • Define following permissions
  1. Admin Role policy to access Admin resource
  2. User Role policy to access User resource
  3. Script claim policy to access Claim resource
  • Add following Roles in the current realm
  1. Admin
  2. User
  • Finally create two users and assign then to following roles
  1. wael/ admin role
  2. alice/ user role

Now the moment of truth, How to integrate (consume the above in ) a spring boot application?

We will use a simple Spring boot application having a RestController to create a rest API with three endpoints as follows:

@RestController
public class ApplicationController {

    @RequestMapping(value = "/api/user", method = RequestMethod.GET)
    public String userRoleProtectedResource() {
        return createResponse();

    }

    @RequestMapping(value = "/api/claim", method = RequestMethod.GET)
    public String claimProtectedResource() {
        return createResponse();
    }

    @RequestMapping(value = "/api/admin", method = RequestMethod.GET)
    public String adminRoleProtectedResource() {
        return createResponse();
    }

    private String createResponse() {
        StackTraceElement[] stacktrace = Thread.currentThread().getStackTrace();
        StackTraceElement e = stacktrace[2];
        String methodName = e.getMethodName();
        return "Access Granted to --> " + methodName;
    }
}

With the above controller all the rest API is already created and exposed with public access (Neither authentication nor authorization), when we run this spring boot application, All users can access the three endpoints:

  • https://localhost:8080/api/user
  • https://localhost:8080/api/admin
  • https://localhost:8080/api/claim

Lets enable the authorization services, open the application.properties file and add the following lines:

keycloak.realm=auth-demo
keycloak.auth-server-url=https://localhost:8180/auth
keycloak.ssl-required=external
keycloak.resource=api-service
keycloak.bearer-only=true
keycloak.credentials.secret=bfeb73d5-51ba-4b4e-b45f-a60309e0f3f9
keycloak.securityConstraints[0].authRoles[0]=admin
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/api/admin

keycloak.securityConstraints[0].authRoles[1]=user
keycloak.securityConstraints[0].securityCollections[1].patterns[0]=/api/user

keycloak.securityConstraints[0].securityCollections[2].patterns[0]=/api/claim
keycloak.policy-enforcer-config.lazy-load-paths=true

keycloak.policy-enforcer-config.paths[0].path=/api/claim
keycloak.policy-enforcer-config.paths[0].claimInformationPointConfig.claims[some-claim]={request.parameter['api-key']}

With the above properties file we identified the following

RHSSO realm name: auth-demo

RHSSO server URL: https://localhost:8180/auth

RHSSO openid-client (client id): api-service

RHSSO client secret (secret of api-service client): bfeb73d5-51ba-4b4e-b45f-a60309e0f3f9

Roles will be used in the application: admin, user

URLs will be protected: /api/admin, /api/user, /api/claim

Mapping the claim policy to query parameter (api-key): {request.parameter['api-key']}

Now, start the spring boot application again, Hit the three endpoints, you will get 403 response code (HTTP ERROR 403, You don't have authorization)

How do we get the authorization token?

We will need to generate the openid-connect token using users accounts (username/password) 

First 

Generate JWT token using the following CURL command:

curl -s -X POST https://localhost:8180/auth/realms/auth-demo/protocol/openid-connect/token \
   -H 'Authorization: Basic YXBpLXNlcnZpY2U6YmZlYjczZDUtNTFiYS00YjRlLWI0NWYtYTYwMzA5ZTBmM2Y5' \
   -H 'content-type: application/x-www-form-urlencoded' \
   -d 'username=wael&password=wael&grant_type=password'

Second

Extract the .access_token from the JSON response 

Third

Hit the endpoints using the following CURL

curl -s -X GET https://localhost:8080/api/user -H "Authorization: Bearer TOKEN_VALUE"

Consider the following 

  • User wael who is mapped to admin role will be able access /api/admin, while wael user will be denied access to /api/user because the user is not mapped to user role
  • User alice who is mapped to user role will be able access /api/user, while alice user will be denied access to /api/admin because the user is not mapped to admin role
  • No matter what is the user role, any user can access the endpoint /api/claim as long as a valid api-key is provided as configured in the Script claim based policy

Focusing more on the script claim policy that is used to introduce a simple api-key mechanism to protect your /api/claim endpoint, below is the policy implementation:

var context = $evaluation.context;
var attributes = context.attributes;
if (attributes.containsValue('some-claim', '123456')) {
    $evaluation.grant();
}

In spring boot application.properties file, we mapped the claim to request parameter of name “api-key” as follows 

keycloak.policy-enforcer-config.paths[0].claimInformationPointConfig.claims[some-claim]={request.parameter['api-key']}

So once users provide the “api-key” parameter with the right value “123456”, they will be granted access to the api as follows:

https://localhost:8080/api/claim?api-key=123456

Note: for all above policies - including the script claim - users are still required to provide the token that was generated with the first call to the openid-connect token service, while the Role based access control (RBAC) requires nothing bur “Authorization: Bearer TOKEN_VALUE” header, the script claim requires extra query string parameter “api-key” as explained.

In the demo all above steps are explained in details in addition to a helper bash script that is used to automate all the testing use cases, you can find it on https://github.com/wael2000/webservice-authorization/blob/master/test.sh

Below is a screenshot of the outcome of running the script

No alt text provided for this image

Finally, Find the RHSSO realm configuration file along with the spring boot application and testing script "test.sh" on below Github repo:

https://github.com/wael2000/webservice-authorization


Faisal Banaeamah

Senior Solutions Architect, Alexa International at Amazon

5 年
Wael Eldoamiry

Principal Solutions Architect at Red Hat

5 年

The first part, introduction to WebServices authorization https://www.dhirubhai.net/pulse/webservice-authorization-common-pain-wael-eldoamiry/

回复

要查看或添加评论,请登录

Wael Eldoamiry的更多文章

社区洞察

其他会员也浏览了