Add Role-Based Access Control to Your App with Spring Security and Thymeleaf

Add Role-Based Access Control to Your App with Spring Security and Thymeleaf

With Okta’s integration for Spring Boot and Spring Security you can wire up your Okta tenant to a Spring Boot app and take advantage of its built-in RBAC.

Jonathan Whitaker

The use of groups as roles seems to be a fundamental overlook in the Okta architecture. Certainly there are scenarios where the group membership of an individual should be different than the roles of that individual.

Going back to the fundamental model of users, groups and roles (often forgot)…

* A user is a member of one or more groups.
* A group is assigned one or more roles.

In modern applications there are certainly cases where certain groups of an organization should only be able to do certain things (e.g. have certain roles). Furthermore, there are definitely scenarios where an individual should be able to do everything and possibly more than their group membership allows. Put more succintly, the roles a group is granted is merely a subset of the joint intersection of all of the roles of the users contained within that group.

The problem with Okta’s philosophy on this matter is that Okta automatically creates a binding between an organizations group structure and the roles of the system. The system and organization are two different entities, and in a good RBAC design they really shouldn’t know anything about each other. That’s the whole reason why RBAC was invented! It was design to create a logical separation between access control and org structure.

Let me demonstrate this idea with a simple (emphasis on simple) example…

Consider a system with two roles “Stock Purchaser” and “Timecard Approver”. The Stock Purchaser role in the system allows all of the functions related to purchasing stocks. The Timecard Approver role allows a timecard to be approved.

Additionally, consider an organization that has two internal groups (e.g. titles within the company) “Shift Supervisor” and “Regional Manager”.

The organization may want to enforce a policy that only permits Regional Managers to be able to purchase stock and approve timecards. That same organization may also want to enforce a policy that only permits Shift Supervisors to be able to approve timecards but not purchase stocks.

Clearly there is a logical separation above between the policies of the organization and the access control expressed in the system. Separating groups and roles allows this logical separation to be effective. In the event a new role in the system is added (e.g. “PTO Request Approver”) - this can be added to all those groups in the organization who require this role. In a system only having roles it may be a laborious task adding the role to all people that require it. This is commonly known as “Role Bloat”.

What has Okta done or is Okta doing to address the example above? It doesn’t appear Okta has anything in their roadmap to express this type of RBAC access control.

Jonathan Whitaker

The use of groups as roles seems to be a fundamental overlook in the Okta architecture. Certainly there are scenarios where the group membership of an individual should be different than the roles of that individual.

Going back to the fundamental model of users, groups and roles (often forgot)…

* A user is a member of one or more groups.
* A group is assigned one or more roles.

In modern applications there are certainly cases where certain groups of an organization should only be able to do certain things (e.g. have certain roles). Furthermore, there are definitely scenarios where an individual should be able to do everything and possibly more than their group membership allows. Put more succintly, the roles a group is granted is merely a subset of the joint intersection of all of the roles of the users contained within that group.

The problem with Okta’s philosophy on this matter is that Okta automatically creates a binding between an organizations group structure and the roles of the system. The system and organization are two different entities, and in a good RBAC design they really shouldn’t know anything about each other. That’s the whole reason why RBAC was invented! It was design to create a logical separation between access control and org structure.

Let me demonstrate this idea with a simple (emphasis on simple) example…

Consider a system with two roles “Stock Purchaser” and “Timecard Approver”. The Stock Purchaser role in the system allows all of the functions related to purchasing stocks. The Timecard Approver role allows a timecard to be approved.

Additionally, consider an organization that has two internal groups (e.g. titles within the company) “Shift Supervisor” and “Regional Manager”.

The organization may want to enforce a policy that only permits Regional Managers to be able to purchase stock and approve timecards. That same organization may also want to enforce a policy that only permits Shift Supervisors to be able to approve timecards but not purchase stocks.

Clearly there is a logical separation above between the policies of the organization and the access control expressed in the system. Separating groups and roles allows this logical separation to be effective. In the event a new role in the system is added (e.g. “PTO Request Approver”) - this can be added to all those groups in the organization who require this role. In a system only having roles it may be a laborious task adding the role to all people that require it. This is commonly known as “Role Bloat”.

What has Okta done or is Okta doing to address the example above? It doesn’t appear Okta has anything in their roadmap to express this type of RBAC access control.

Marcelo Cavalini

Hi Micah, I’m trying to configure my app to use okta based roles following this steps but I don’t know why I receave just 403 response, please, can you help me ?

Micah

Hi Marcelo:

I’ll need a little more detail in order to help out. Can you give me some information on your Okta configuration? You can email me at: micah.silverman@okta.com

Marcelo Cavalini

Hi Micah,
Yesterday I sent you a mail, about my problem, you received it ?
Thank you

Micah

FYI - I’ve updated the code repo here: https://github.com/oktadeve…. Blog update will follow soon. TL;DR - we’re now using Spring Boot 2.0 in our SDK and I switched from the implicit to the authorization code flow as this is best practice for this kind of example.

Jack

Hi Micah,when I try to login as you said,the page prompts me to encounter an internal error,then I receive 403 response.Can you help me?

Micah

micah.silverman@okta.comFYI - I’ve updated the code repo here: https://github.com/oktadeve…
Blog update will follow soon. TL;DR - we’re now using Spring Boot 2.0
in our SDK and I switched from the implicit to the authorization code
flow as this is best practice for this kind of example.

Let me know if that helps you in working out the issue. If not, shoot me an email at: micah.silverman@okta.com with some more detail.

Chatmewsad Chatmeasdwa

got error …

2019-01-05 18:53:17.983 DEBUG 13662 — [nio-8080-exec-4] o.apache.coyote.http11.Http11Processor : Error parsing HTTP request header

java.io.EOFException: null
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1250) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1190) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:717) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.http11.Http11InputBuffer.parseRequestLine(Http11InputBuffer.java:366) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:687) ~[tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459) [tomcat-embed-core-8.5.29.jar:8.5.29]
at org.apache.tomcat.util.net…(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.29.jar:8.5.29]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_191]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.29.jar:8.5.29]
at java.lang.Thread.run(Thread.java:748) [na:1.8.0_191]

2019-01-05 18:53:17.983 DEBUG 13662 — [nio-8080-exec-4] o.apache.coyote.http11.Http11Processor : Socket: [org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@4ea5359d:org.apache.tomcat.util.net.NioChannel@7d7eff76:java.nio.channels.SocketChannel[connected local=/0:0:0:0:0:0:0:1:8080 remote=/0:0:0:0:0:0:0:1:55806]], Status in: [OPEN_READ], State out: [CLOSED]
2019-01-05 18:53:17.990 DEBUG 13662 — [nio-8080-exec-4] o.apache.tomcat.util.threads.LimitLatch : Counting down[http-nio-8080-exec-4] latch=1

Alexander Pilch

You might want to add our rbac implementation on top of it in order to map those roles to privileges internally. The provided Oauth2 integration could be used for that, so instead of using hasAuthority(‘admins’), you’d use has_authority(‘some_internal_privilege’). This approach does have the advantage, that you keep your role mapping within okta, but the mapping to privileges is kept in the application. It’s much easier to extend your app, if you just have to modify those privileges instead of modifying the roles. Roles are mostly stable for a long time, privileges do change more often due to refacoring of roles or modifications of the codebase (e.g. new features). This is especially true, when you don’t have admin/any access to okta. There’s often a dedicated team in larger companies in order to handle the setup/administration of the auth servers. So if you don’t implement an additional mapping, you’ll have to deal with them for each minor change.

indika

I just downloaded the code from git repo and tried to run the mvn build. I am getting the following error
----------------------------------------------------------------
Parameter 2 of method oktaSsoFilter in com.okta.okta.spring.example.OktaSpringSecurityRolesExampleApplication required a bean of type ‘org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails’ that could not be found.

Action:

Consider defining a bean of type ‘org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails’ in your configuration

----------------------------------------------------------
Can anyone point me in the right direction

Micah

The app requires an application.yml file (for which there is a sample provided: src/main/resources/application.yml.sample) OR the proper environment variables to be set.

Try copying the application.yml.sample file to application.yml, setting the values accordingly, and running again.

shruthi bezgam

I downloaded the folder from git repo and tried to run mvn spring-boot:run . But am getting the following error. Any help would be appreciated.Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.0.1.RELEASE:run (default-cli) on project okta-spring-security-roles-example: Unable to find a suitable main class, please add a ‘mainClass’ property -> [Help 1]

Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.0.1.RELEASE:run (default-cli) on project okta-spring-security-roles-example: Unable to find a suitable main class, please add a ‘mainClass’ property -> [Help 1]

Stephen Rice

Hi,

I tried following the above instructions (Using IntelliJ IDE), however when I clicked the login button in the example web-app, I get the following error:

400: bad request,
Error Code: invalid_request
Description: The ‘redirect_uri’ parameter must be an absolute URI that is whitelisted in the client app settings
redirect_uri=http://localhost:8080/login/oauth2/code/okta

Any clues as to why this is not working would be helpful. Thanks.

Cheers,
Stephen.

Alex Fan

I met the same problem. Did you solve it?

Stephen Rice

No.

Matt Raible

You need to add this URL to as a Login redirect URI in your Okta app. If you’ve already done that, can you please add a screenshot of your app’s Login redirect URIs, as well as the error?

Alex Fan

I made this work by changing this redirect URL to
http://localhost:8080/signin.

Alex Fan

Hi,

My code works but the group claim part. Every user will ended up with 403 unauthorized.

Can you help me with this?

Thanks.