How to use Keycloak JAVA API with keycloak-spring-boot-starter package - java

I have used the Keycloak spring boot adapter for my spring boot server and I am able to get my user authenticated. But when i want to get user data like the groups he is in, I am unable to get that data through the KeycloakPrinciple object I get to access through the request. I am however able to get the ID of the user and I though it would be straight forward to access the user profile in the spring boot application as it has the keycloak adapter. Any idea as to how to get that data or if I should add or do something else?
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-security</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>12.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-test</artifactId>-->
<!-- <scope>test</scope>-->
<!-- </dependency>-->
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>12.0.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
applications.yaml
server:
port: 9000
keycloak:
auth-server-url: http://localhost:8080/auth
realm: test
resource: spring-boot-client
credentials:
secret: 41e7abe2-35a4-46c0-ba68-40dd16080b1a
bearer-only: true
public-client: true
securityConstraints:
- authRoles:
- CANDIDATE
- AUTHOR
- ADMIN
securityCollections:
- name: ping
patterns:
- /ping
- authRoles:
- AUTHOR
- ADMINN
securityCollections:
- name: Author
patterns:
- /author
logging:
level:
org.keycloak: TRACE
How i am accessing the ID of the user making the request to the spring boot server
#GetMapping("/ping")
public ResponseEntity<String> ping(KeycloakPrincipal<KeycloakSecurityContext> principal) {
String user_id = principal.getKeycloakSecurityContext().getToken().getSubject();
return ResponseEntity.ok(user_id);
}
Any guidance would be of help as I looked through all the documentation I could get my hands on. Yes keycloak does provide a JavaDoc which I did refer and also an ADMIN REST API, which I thought would be a bad way to access this data(I might be wrong). I want to know if just using the starter adapter would not give me access to the Java API, cause I can only access the KeycloakPrinciple.

Why should the Rest API be a bad way to get user specific Data?
You can configure a specific client with a Client Secret and minimal realm roles to get the data you need.
Did you have a Look at the token? Is the Information you need already in the token?
You can get Data Like realm roles token.getOtherClaims
https://www.keycloak.org/docs-api/10.0/javadocs/org/keycloak/representations/AccessTokenResponse.html
You have to make sure that the Information you mapped is in the token. Therefore have a Look a the admin console -> clients -> your Client -> mappers -> evaluate
Maybe you can Post a few Pictures of your current configuration

Related

Spring-Boot OAuth2 Strange Behavior When Authenticating with Twitch

I have been trying to set up OAuth2 with Twitch through Spring for a few days now. I have resolved quite a few problems in that process but this one has stumped me. When I attempt to access one of the endpoints that I am trying to require users to be authenticated with Twitch for I am getting redirected to localhost:8080/login and being shown a page that simply reads "Login with OAuth 2.0" and has nothing else on it. My expectation was that Spring would automatically redirect me to Twitch's authentication portal and then Twitch would send me back to the Spring application after going through the OAuth process. Instead I am simply being shown that page and nothing is happening.
As far as what has been done so far to remedy this problem... pretty much nothing. I have not been able to find anyone else running into this problem so I am thinking that there are a few possible issues... Based on this tutorial https://spring.io/guides/tutorials/spring-boot-oauth2/ it seems like Spring has out of the box functionality for a lot of different OAuth providers. I am guessing that Twitch is not one of them. That makes me concerned that something on Twitch's backend might be causing this problem for Spring (if that is the case then I will need to make a custom authenticator). The other possibility I have thought of is that Spring might need to be told where to redirect users to for them to get authenticated. If that is the case then I would appreciate some help with that as I have not been able to find any indication that that is something that needs to be done online (and I have no idea how to do it).
Some important files from my project:
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.5.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.redacted</groupId>
<artifactId>redacted</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>redacted</name>
<description>redacted</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.44</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.4.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties:
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
spring.security.oauth2.client.registration.twitch.client-id=redacted
spring.security.oauth2.client.registration.twitch.client-secret=redacted
spring.security.oauth2.client.registration.twitch.client-authentication-method=post
spring.security.oauth2.client.registration.twitch.redirect-uri=http://localhost:8080/login/oauth2/code/twitch
spring.security.oauth2.client.registration.twitch.provider=twitch
spring.security.oauth2.client.registration.twitch.scope=user:read:email
spring.security.oauth2.client.registration.twitch.authorization-grant-type=AUTHORIZATION_CODE
spring.security.oauth2.client.provider.twitch.authorization-uri=https://id.twitch.tv/oauth2/authorize
spring.security.oauth2.client.provider.twitch.token-uri=https://id.twitch.tv/oauth2/token
spring.security.oauth2.client.provider.twitch.user-info-uri=https://id.twitch.tv/oauth2/userinfo
spring.security.oauth2.client.provider.twitch.user-name-attribute=redacted
and also my SpringSecurityConfiguration file in the event it matters:
package com.redacted;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
#EnableWebSecurity
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
//This allows users to access the "/" and "/Info" endpoints without authenticating with Twitch. To go anywhere else they will have to authenticate.
httpSecurity.antMatcher("/**").authorizeRequests().antMatchers("/", "/Info").permitAll().anyRequest().authenticated().and().oauth2Login();
}
}
My project structure:
Thank you for your time and consideration, I appreciate the help.
-Epoch
Edit:
Something I have recently found that I thought might be prudent to add to this discussion - when I am attempting to access a secured page in my current setup I see this screen:
It looks as if the intended way Spring normally displays this screen is like this:
It's almost as if Spring simply is not seeing my Oauth provider. Not sure if this information is helpful but I thought I would include it. Springs intended behavior for Oauth is that when there is only one provider configured the /login page is skipped altogether. I would actually prefer this behavior to be exhibited (but given it sees no providers I presume it shows the page).
I was able to recreate the issue. You have specified:
spring.security.oauth2.client.registration.twitch.authorization-grant-type=AUTHORIZATION_CODE
While it should be:
spring.security.oauth2.client.registration.twitch.authorization-grant-type=authorization_code
Spring Boot is unable to load your configuration properties properly, so it's as if you have 0 client registrations configured.
Try adding the .oauth2Client() in your configure method instead of .oauth2Login().

Why MongoRepository expose rest crud api and how to disable when using restController

I have a spring application exposing rest api using a restController. It calls a layer service and then a layer Dao where i'm using MongoRepository to get crud stuffs.
public interface ProjectDao extends MongoRepository<Project, String> {
}
#CrossOrigin
#RestController
#Slf4j
#RequestMapping("/api/data")
public class DataController {
...
#GetMapping(value = "projects/{id}")
public Project findProjectById(#PathVariable String id) throws ResponseStatusException {
Optional<Project> project = projectService.findById(id);
if (project.isEmpty()) throw new ResponseStatusException(HttpStatus.NOT_FOUND, "product with id " + id + " does not exists");
return project.get();
}
...
}
I just configured swagger to generate the documentation and have discovered i can call directly all crud methods exposed from the mongorepository!!! But i do not want this behaviour as i want to expose only api from my rest controller.
I have my method GET /api/data/projects/{id} that call the findProjectById of my controller but i have also a method GET /projects/{id} which call directly the mongorepository. And all the other like delete, put, post which is really dangerous.
I'm using this dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
I can't find how to configure that and why by default all rest api of mongorepository is exposed!
I someone know how to disable that, thanks!
Ok, i have found the solution only modifying maven dependencies.
I should use
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Instead of
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
Which publish rest service based on data model.

restapi with feignclient + spring security 5 + oauth2.0 from custom provider

I have read many posts and articles and documentation but I'm very confused about all this.
What I try to do is an apirest with springboot, using feignclient, and I must send a bearer token in my petitions, I get this bearer token from a custom provider.
My client class with feign looks like this:
#FeignClient(name="test-client", url = "https://testurl/")
public interface TestClient {
#GetMapping("/auth/me")
public ResponseEntity<String> testLoginInformation() throws FeignException;
}
my application.properties:
server.port=8082
spring.security.oauth2.client.registration.custom.client-id=mylargeID
spring.security.oauth2.client.registration.custom.client-secret=mylargePassword
spring.security.oauth2.client.registration.custom.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.custom.scope=access_token_only
spring.security.oauth2.client.registration.custom.provider=custom-provider
spring.security.oauth2.client.registration.custom.client-authentication-method=basic
spring.security.oauth2.client.provider.custom-provider.authorization-uri=https://testurl/auth/token
spring.security.oauth2.client.provider.custom-provider.token-uri=https://testurl/auth/token
In which, I think I indicate all the necesary data for making the requesto to get the token... I don't understand what is the difference between "authorization-uri" and "token-uri"
In my springboot application main class, I only have these two annotations:
#SpringBootApplication
#EnableFeignClients
and last but not least important, the dependencies in my pom file look like these:
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.4-RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<version>2.2.1-RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.0-RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
<version>5.3.4-RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<version>2.3.4-RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
</dependency>
</dependencies>
Right now, what I'm getting is Invalid Authorization Grant Type (client_credentials) for Client Registration with Id: custom.
What am I doing wrong?
Authorization url
The /authorization endpoint is used to interact with the resource owner and get the authorization to access the protected resource. To better understand this, imagine that you want to log in to a service using your Google account. First, the service redirects you to Google in order to authenticate (if you are not already logged in) and then you will get a consent screen, where you will be asked to authorize the service to access some of your data (protected resources); for example, your email address and your list of contacts.
Token url
An access token is an opaque string or a JWT that denotes who has authorized which permissions (scopes) to which application. It is meant to be exchanged with an access token at the /oauth/token endpoint.
Check here for more: https://auth0.com/docs/protocols/protocol-oauth2

Spring unknown property 'spring.cloud.config.server'

I am attempting to connect to a git config server in my spring project following the example below in an eclipse IDE
https://cloud.spring.io/spring-cloud-static/Camden.SR5/
However I am running into issues being able to resolve the properties needed as eclipse is not able to recognize the server property in my bootstrap.yml (unknown property 'spring.cloud.config.server')
spring:
cloud:
config:
*server*:
git:
uri: .....config_server.git
I have included all the dependencies listed in the example above (spring-cloud-dependencies, spring-cloud-starter-config, spring-boot-starter-test) however I still receive the error.
Is there limitations as to when this property can be used? Or is there an additional dependency that is needed?
This was added to the question by the OP and has been moved here.
Turns I just needed to add a spring-cloud-starter-config-server dependency. Not sure why it did not get pulled in by by the others but that did the trick.
here is a small example of how we use it using the spring cloud config:
bootstrap.yml
spring:
profiles:
# this will tell spring to pick the correct profile file
active: sheba
# u must specify your application name for it to be found in the git repository!
application:
name: OpscI2aClient
# now we telling our git client where to locate our config files
cloud:
config:
server:
bootstrap: true
git:
# this the full uri to our repository where the config file
# wich its name is ApplicationName-profileName.yml
# are found
uri: https://some.company.git.com/config/config-repo.git
username: someGitUserName
password: thePasswordOfTheGitUser
# clone the whole config repository on startup
the whole files in the repository are cloned!
clone-on-start: true
# this tell spring the name of the local repository directory
# which in this case it shall create a directory called git_local
relatively to where the application started
basedir:
git_local
so, assuming you have a git repo, it must contain a file OpscI2aClient-sheba.yml
this file is just a simple spring boot configuration file, however, should not contain active profile entry.
that is it.
Just to clarify:
Assuming the application named OpscI2aClient have two profiles, dev and prod,
your git configuration repositoty should actually contain, two files:
1. OpscI2aClient-dev.yml
2. OpscI2aClient-prod.yml
Hope this helps.
I am just adding the POM in which you might find some hints as well
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<project.scm.id>opsc-scm-server</project.scm.id>
<java.version>1.8</java.version>
<slf4j.version>1.7.2</slf4j.version>
<logback.version>1.1.3</logback.version>
<spring-cloud.version>Edgware.RELEASE</spring-cloud.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</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>
First of all, is it just Eclipse doesn't see server and you can run it from command line with no errors?
In case if it's just Eclipse it could be just wrong indention or tabs. Eclipse is not so smart sometimes.
Try to set it all to plain application.properties file.
Try to run it from command line
Also, I think you have to add credentials section for git + encrypted or not.
Here is valid example of YML that works fine in STS Eclipse:
https://github.com/zobarov/spring-cloud/blob/master/edu-springcloud-configserver/src/main/resources/application.yml

Netflix Zuul cannot resolve configuration properties

So I'm trying to use Zuul as an API gateway and one of my requests takes a while. I am getting a socket timeout exception, but when I try to change things related to timeout in my application.yml my IDE tells me it cannot resolve the configuration properties
For example, this does not exist:
hystrix:
command:
default:
execution:
isolation:
strategy: THREAD
thread:
timeoutInMilliseconds: 10000
I must be missing something in my pom.xml, but I don't know what. Here are the dependencies in my pom.xml:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
</dependencies>
Any help would be greatly appreciated
EDIT:
This was simply an issue with the IDE not recognizing the config. It works as intended. The IDE used is intelliJ
change to this
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 20000

Categories