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
Related
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().
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
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.
Starting with Spring Boot 2.0.2-RELEASE actuator 'metrics' endpoint isn't available even using following configuration:
management:
endpoints.web.exposure.include: "*"
The same configuration exposes metrics endpoint with Spring Boot 2.0.0-RELEASE
pom.xml:
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
...
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
....
Any ideas how to resolve this issue?
Finally I found that there is an instance of org.springframework.boot.actuate.metrics.MetricsEndpoint should exist in Spring context in order to let Actuator show '/metrics' endpoint.
org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration
is responsible to create an instance of MetricsEndpoint but for some reason it never creates it.
So, I've created this bean in my own configuration:
#Bean
public MetricsEndpoint metricsEndpoint(MeterRegistry registry) {
return new MetricsEndpoint(registry);
}
It's fixed the problem but I'm not sure this this the best solution.
I made a similar sample . My application.yml is like this.
I was trying to deploy my own custom sink of spring cloud data flow onto cloud foundry.
My Dependency are below :
<dependencies>
<dependency>
<groupId>org.springframework.cloud.stream.app</groupId>
<artifactId>spring-cloud-starter-stream-sink-log</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
<version>1.2.0.RC1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud.stream.app</groupId>
<artifactId>app-starters-core-dependencies</artifactId>
<version>1.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud.stream.app</groupId>
<artifactId>log-app-dependencies</artifactId>
<version>1.2.0.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
And the custom code is very basic as below :
#EnableBinding(Sink.class)
public class SinkConfiguration {
private static final Log logger = LogFactory.getLog(SinkConfiguration.class);
#ServiceActivator(inputChannel=Sink.INPUT)
public void loggerSink(String payload) {
logger.info("Hello, Rahul. The time is: " + payload);
}
}
All I see when i deploy this application is that the Error channel subscriber is created , but no Input subscriber was created. Due to this no messages are being received on to this app. Source for this app is a custom source with rest controller. The default out of box streamer app -- LogSink works successfully. But i need to create a customsink to build things on top. Do anyone see an issue what I am missing here?
If your goal is to create an entirely new custom sink, you would develop that as a standalone application by following the Spring Initializr procedure. It is simple this way and you don't need to use the existing log-sink starter in this case.
If you are trying to patch any of the OOTB application; in this case, the log-sink, then follow the patching procedure. Pay attention to importing the configuration class. Unless you do that, the associated app-starter's behavior won't kick in.
Also, it seems you're using an old release for rabbit-binder. It is better to rely on the Spring Initializr generated artifact as opposed to handcrafting dependency versions.