I have setup application and authorization server on Okta. I have added two groups, namely admin and users. The authentication flow is working fine but when I try to print the roles, I am getting the output as below
[SCOPE_address, SCOPE_phone, SCOPE_offline_access, SCOPE_openid, ROLE_USER, SCOPE_email, SCOPE_profile]
The Java code to print the roles is as below:
#RequestMapping("/securedPage")
public String securedPage(Model model, Principal principal) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Set<String> roles = authentication.getAuthorities().stream()
.map(r -> r.getAuthority()).collect(Collectors.toSet());
System.out.println(roles);
return roles.toString();
}
application.properties
okta.oauth2.client-id=<client-id>
okta.oauth2.client-secret=<client-secret>
okta.oauth2.issuer=<issuer-url>
okta.oauth2.redirect-uri=/login
okta.oauth2.roles-claim=groups
server.port=9222
logging.level.org.springframework.security=TRACE
When I access the login page and enter the username and password, the roles are not getting displayed. But strangely I am seeing ROLE_USER but I have added the uses into users group.
I am following this guide https://developer.okta.com/blog/2017/10/13/okta-groups-spring-security. I am not sure, how to configure the roles for authorization in Spring Security.
The below is the Spring Security Configuration
#Configuration
public class SpringSecurityWebAppConfig extends WebSecurityConfigurerAdapter{
#Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/").permitAll()
.anyRequest().authenticated()
.and()
.oauth2Login();
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example.demo</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.okta.spring</groupId>
<artifactId>okta-spring-boot-starter</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
uestMatcher : Checking match of request : '/securedPage'; against '/'
2021-01-09 00:41:57.674 DEBUG 21292 --- [nio-9222-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor : Secure object: FilterInvocation: URL: /securedPage; Attributes: [authenticated]
2021-01-09 00:41:57.678 DEBUG 21292 --- [nio-9222-exec-4] o.s.s.w.a.i.FilterSecurityInterceptor : Previously Authenticated: org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken#dffd3eb: Principal: Name: [00u3g2nr7ISCACRyk5d6], Granted Authorities: [[ROLE_USER, SCOPE_address, SCOPE_email, SCOPE_offline_access, SCOPE_openid, SCOPE_phone, SCOPE_profile]], User Attributes: [{at_hash=dHbIlPZ1tUzL-y5vmVb1cQ, sub=00u3g2nr7ISCACRyk5d6, zoneinfo=America/Los_Angeles, ver=1, email_verified=true, amr=["pwd"], iss=https://dev-9729512.okta.com/oauth2/edukart, preferred_username=kishore#gmail.com, locale=en-US, given_name=kishore, nonce=FxcHjdKHTsisyWG8jiLgEbH84H2AxyCdISv5U0JyVA8, aud=[0oa3eaj576gLDYwsh5d6], updated_at=2021-01-07T19:46:08Z, idp=00o3ct0plX9rgiTmB5d6, auth_time=2021-01-08T18:48:46Z, name=kishore kumar, exp=2021-01-08T20:11:54Z, family_name=kumar, iat=2021-01-08T19:11:54Z, email=kishore#gmail.com, jti=ID.xLH6W1loE_ELRLCWEuyGHV42-pkw3eCDqfNVlyQOfnc}]; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails#43458: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: D028931C57C35976D9AB6FC2C9543B4B; Granted Authorities: ROLE_USER, SCOPE_address, SCOPE_email, SCOPE_offline_access, SCOPE_openid, SCOPE_phone, SCOPE_profile
Where am I going wrong and how to debug with Okta groups to Spring Security Roles.
Update:
I updated the okta-spring-boot-starter version to 1.4.0 and now I am able to see the admins role that was assigned to the user.
roles :: [SCOPE_address, Everyone, SCOPE_phone, SCOPE_offline_access, SCOPE_openid, ROLE_USER, SCOPE_email, SCOPE_profile, admins]
Questions:
Why is the roles fetched only when configured the claims with ID-Token in include in token type and not while sending it as part of ACCESS_TOKEN in Okta
I do not see an option to set the claims to both ID-TOKEN and ACCESS TOKEN in the claims tab. Its a drop down and I can choose only one.
Why is the granted authority showing it as admins and not as ROLE_ADMINS
Principal :: Name: [00u40teh2owUKq5ZL5d6], Granted Authorities: [[Everyone, ROLE_USER, SCOPE_address, SCOPE_email, SCOPE_offline_access, SCOPE_openid, SCOPE_phone, SCOPE_profile, admins]], User Attributes: [{at_hash=uB-Gcqt-H6ezmv8KpIpx_g, sub=00u40teh2owUKq5ZL5d6, zoneinfo=America/Los_Angeles, ver=1, email_verified=true, amr=["pwd"], iss=https://dev-7858070.okta.com/oauth2/default, groups=["Everyone","admins"], preferred_username=ramesh#gmail.com, locale=en-US, given_name=ramesh, nonce=KXqGlhOj5ZVoChXo-ATjoHW-9ABAcEi5AnukAGXxg78, aud=[0oa3mz4mtisXjRJf85d6], updated_at=2021-01-20T03:17:42Z, idp=00o3myy20pqywuN5o5d6, auth_time=2021-01-21T03:02:46Z, name=ramesh kumar, exp=2021-01-21T04:02:49Z, family_name=kumar, iat=2021-01-21T03:02:49Z, email=ramesh#gmail.com, jti=ID.XQ4cKdIMuQKJv941EkYyDFJDCtKFAzaItPdyLPkMXPQ}] roles :: [SCOPE_address, Everyone, SCOPE_phone, SCOPE_offline_access, SCOPE_openid, ROLE_USER, SCOPE_email, SCOPE_profile, admins]
Take a look at the "Authorization Server" section in the blog post you mentioned:
https://developer.okta.com/blog/2017/10/13/okta-groups-spring-security#authorization-server
The post used an older version of these libraries, but make sure you have the "groups" claim defined. You likely need to set the "include in token type" value to "both" (or follow the same steps to create one for the "ID Token" as well)
This post likely predated OIDC support in Spring Security.
Keep us posted, if that was the issue I'll tweak the post to mention that.
If that doesn't help, use the "Token Preview" tab on the "Authorization Server" configuration page in your Okta Admin/Developer Console.
You should see a "groups" claim listed, once everything is configured correctly.
Update (answering other questions):
ID Token vs Access Token claims
This gets into the weeds a bit related to the OAuth 2.0 and OIDC specs. But the TL;DR is older versions of Spring Security used OAuth 2.0 and Access Tokens, newer versions can use OIDC and ID Tokens.
There are other flows that would use Access Tokens too, for a more detailed description on the differences between the two and which flows use which tokens, checkout the Okta Dev YouTube Channel
This one was my fault, I miss remembered this page.
You can just create the claim for both token types.
It uses the names that are in group, if you want it to use ROLE_ADMIN instead you can create an Okta group with that name.
I know we can use this #ApiModelProperty annotation from this link to hide an id from the request when we use Swagger-Ui
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
But I am using this spring doc OpenApi dependency
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.2.32</version>
</dependency>
I am not getting this annotation #ApiModelProperty when I used springdoc-openapi-ui. What could be the annotation for this?
You have to replace swagger 2 annotations with swagger 3 annotations (it is already included with springdoc-openapi-ui dependency). Package for swagger 3 annotations is io.swagger.v3.oas.annotations.
It's all explained here:
https://springdoc.org/migrating-from-springfox.html
Actually i am facing a problem that Swagger-ui can't show me input for MediaType.APPLICATION_OCTET_STREAM, so lets imagine this next service:
#PUT
#Path("/performAudioQuery")
#Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response performAudioQuery(InputStream audioInputStream){
//Impl of the service
}
and here are the dependencies
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-jersey2-jaxrs</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
and i am using wildfly 9.x
So what should i do to make it possible for Swagger-ui working good with this previous service?
The current version of the Swagger/OpenAPI specification (2.0) doesn't deal kindly with file uploads as what's known as body parameter. We've formulated a workable solution that is covered at https://github.com/OAI/OpenAPI-Specification/issues/50 - however that solution is currently not fully supported by swagger-core and not supported at all in swagger-ui.
I'm trying to store portlet preferences in a backing bean (JSF) as mentioned in this tutorial
But, I can not understand how they imported Preference class here
Map<String, Preference> mutablePreferenceMap =
(Map<String, Preference>) elResolver.getValue(
facesContext.getELContext(), null, elExpression);
the package javax.portlet.* don't contain faces.preference.Preference
Anyone has an idea about that, specially how to save portlet preferences
thanks in advance
You have to add the Liferay Faces Bridge JAR to your project.
Add the following dependencies:
<dependencies>
<dependency>
<groupId>com.liferay.faces</groupId>
<artifactId>liferay-faces-alloy</artifactId>
<version>3.1.0-ga1</version>
</dependency>
<dependency>
<groupId>com.liferay.faces</groupId>
<artifactId>liferay-faces-bridge-impl</artifactId>
<version>3.1.0-ga1</version>
</dependency>
<dependency>
<groupId>com.liferay.faces</groupId>
<artifactId>liferay-faces-portal</artifactId>
<version>3.1.0-ga1</version>
</dependency>
</dependencies>
and the liferay-faces-bridge-api jar which is a dependency for liferay-faces-bridge-impl has this Preference interface.
More info:
http://www.liferay.com/community/liferay-projects/liferay-faces/download
I have something similar to that:
class Model {
private String field1;
private String field2;
//setters
}
class Action extends ActionSupport {
private Model model;
public String execute() {
//breakpoint
}
public void setModel(Model model){
this.model=model;
}
}
on jsp:
<s:form id="addCommentForm" method="POST" namespace="%{namespace}" action="addComment">
<input type="text" name="model.field1"/>
<input type="text" name="model.field2"/>
</s:form>
When I submit this form unfortunately only 1 field in Model class is set.
I debug code and find that actually setters are called for both fields (field1 and field2), but for different instances of Model class.
So it appears, that it executes with next steps when form is submitteed:
create new instance (instance1) of Model class, set this instance in Action class
set field1 to instance1
create new instance (instance2) of Model class, set this instance in Action class
set field2 to instance2
So as I see instanse2 replace instance1.
I need field1 and field2 in one instance of Model class.
What need to be modified?
list of dependenced:
<dependency>
<groupId>com.vercer.engine.persist</groupId>
<artifactId>twig-persist</artifactId>
<version>1.0.4</version>
</dependency>
<dependency>
<groupId>com.opensymphony</groupId>
<artifactId>xwork</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.struts.xwork</groupId>
<artifactId>xwork-core</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.19</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>opensymphony</groupId>
<artifactId>sitemesh</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-core</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>jetty</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1-6.0.2</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-spring-plugin</artifactId>
<version>2.1.6</version>
</dependency>
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-json-plugin</artifactId>
<version>2.1.8</version>
</dependency>
For complex types you need both a getter and setter so Struts2 can manipulate the object correctly, otherwise it will not be able to get the existing instance and will be forced to create a new instance of Model (new Model().setWhatever()) as opposed to seeing a model already exists and doing (getModel().setWhatever()).