I am trying to send GET request to Azure API for FHIR using Spring Boot.
I have created an application having permission as "FHIR Data Contributor", and have its client-id, tenant-id and client-secret-value.
I have written the code below to get the access_token. It is returning the access_token, but how do I make use of the token and send GET request to FHIR ?
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
#RestController
public class HelloController {
#GetMapping("/token")
#ResponseBody
public String token(#RegisteredOAuth2AuthorizedClient("graph") OAuth2AuthorizedClient client) {
String accessToken=client.getAccessToken().getTokenValue();
return accessToken;
}
}
The application.yml contains:
azure:
activedirectory:
tenant-id: XXXXXXX-xxxxx-XXXXXXXX
client-id: XXXXXXX-xxxxx-XXXXXXXX
client-secret: XXXXXXX-xxxxx-XXXXXXXX
authorization-clients:
graph:
scopes: https://graph.microsoft.com/User.Read
I am able to do all this using Postman.
Related
I Was following this tutorial on YouTube I have been able to successfully run the config server where I host two properties files here File hosted. and on the client side when I tried to consume the value I get an empty response here is the dummy controller I have created.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/test")
#RefreshScope
public class TestController {
#Value("${test.name}")
private String product;
#GetMapping
public String test() {
return product;
}
}
but when I send a get request to route /api/test, I get a response of 200OK with no actual test value. name, What am I doing wrong?.
The tutorial in youtube uses the default naming convetion but you probably have changed it and now spring cloud config server does not know which property file expects your service to have.
In cloud config server the file is saved as product.properties.
For this reason if your client service has some other name, this will not work. To correct it go to the client application and in bootstrap.yaml add the property spring.application.name: product.
I created an app in Micronaut using JWT tokens for security
mn create-app --features=security-jwt,data-jdbc,reactor,graalvm example.micronaut.micronautguide --build=gradle --lang=java
And now all my routes are forbidden. How to exclude certain routes (ie. login) from JWT token checking. I tried both without annotation and with annotation IS_ANONYMOUS
package logfetcher;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Produces;
import io.micronaut.http.annotation.Produces;
import io.micronaut.security.annotation.Secured;
import io.micronaut.security.rules.SecurityRule;
#Controller("/log")
public class LogFethcerContoller
{
#Get
#Secured(
SecurityRule.IS_ANONYMOUS )
#Produces(MediaType.TEXT_PLAIN)
public String index() {
return "Hello World";
}
#Get("log")
public String index1() {
return "Hello World";
}
}
I get 401 on both routes.
How can I have routes that do not need the JWT token.
I get 401 on both routes.
From micronaut-projects.github.io/micronaut-security/3.2.0/guide/#gettingStarted...
By default Micronaut returns HTTP Status Unauthorized (401) for any
endpoint invocation. Routes must be explicitly allowed through the
provided mechanisms.
Question:
How can I have routes that do not need the JWT token.
There are a number of ways.
From micronaut-projects.github.io/micronaut-security/3.2.0/guide/#securityRule...
The decision to allow access to a particular endpoint to anonymous or
authenticated users is determined by a collection of Security Rules.
#PermitAll is one way to do it. Others are documented at the links above.
when I use the RestTemplate and the Getforobject() method I get an error 500 code when running my spring boot. How can I consume this API using springboot?
package nl.qnh.qforce.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.List;
#RestController
public class PersonController {
private static String url = "https://swapi.co/api/people/";
#Autowired
private RestTemplate restTemplate;
#GetMapping("/people")
public List<Object> getPeople(){
Object[] people = restTemplate.getForObject(url, Object[].class);
return Arrays.asList(people);
}
}
I would advise first checking if manually calling provided url returns expected response. You can use curl, postman or any other similar tool. In case call on provided url returns response, provide us with more context from your application, so we can assess which part is responsible for 500 error.
I'm currently building a web-application using Spring-Boot-Web in combinition with OAuth2 using Amazon-Cognito on AWS.
My target setup looks like this:
In "My-Datacenter" I have 2 applications running:
Gateway-Application
Backend-Application
The Gateway-Application is accessible from the internet. It serves the frontend (html, css, js), contains some display logic and maybe some APIs that are accessible for everyone. It is the initiator for the "Authorization Code Grant"-OAuth 2.0 Flow (it redirects the user to Amazon-Cognito if the user isn't yet logged in).
The Backend-Application is only accessible by the Gateway-Application. It is not accessible from the outside. In the Backend-Application I want to retrieve user-details (Name, E-Mail) from Amazon-Cognito.
What I got to work at the moment:
I registered one client for the Gateway-Application in Amazon-Cognito. The Gateway-Application initiates the "Authorization Code Grant"-Flow and can access the user-information from Amazon-Cognito.
I configured the Gateway-Application to pass the OAuth2-Authorization-Details to the Backend-Application with all my HTTP-Requests. The Backend-Application can successfully check whether the user is authenticated or not.
Gateway-Application configuration
application.yml
spring:
security:
oauth2:
client:
registration:
cognito:
client-name: frontend-local
client-id: #######
client-secret: #########
scope: openid,backend/read,backend/write
redirect-uri: http://localhost:8080/login/oauth2/code/cognito
provider:
cognito:
issuer-uri: https://cognito-idp.eu-central-1.amazonaws.com/<my-aws-pool-id>
user-name-attribute: cognito:username
SecurityConfiguration.java
package io.share.frontend.configuration;
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 SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.and()
.authorizeRequests().antMatchers("/", "/webjars/**").permitAll().anyRequest().authenticated()
.and()
.oauth2Login()
.and()
.logout().logoutSuccessUrl("/");
}
}
OAuth2WebClientConfiguration.java
package io.share.frontend.configuration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
#Configuration
public class OAuth2WebClientConfiguration {
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
#Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("cognito");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
}
Backend-Application configuration
application.yml
server:
port: 8081
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://cognito-idp.eu-central-1.amazonaws.com/<my-aws-pool-id>
SecurityConfiguration.java
package io.share.backend.configuration;
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 SecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests().anyRequest().hasAuthority("SCOPE_backend/read")
.and()
.oauth2ResourceServer().jwt();
}
}
What I want to get to work
In my Backend-Application, I want to be able to access to User-Information from Amazon-Cognito. Currently, I have the following basic RestController in my Backend-Application:
package io.share.backend.web;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
#RestController
public class ObjectResource {
#GetMapping("/username")
public String greeting(#AuthenticationPrincipal Principal user) {
return user.getName();// this currently returns some UUID-Like string, I want to be able to access the username and E-Mail here, just like I can in the Gateway
}
}
But I want to be able to access the real Username and E-Mail in the Backend.
My question
Do I have to create a new client in Amazon-Cognito for the backend? Or do I have to configure the same client-id and client-secret for the Gateway and Backend?
Which additional Spring-Configurations do I have to make in my Applications (both Gateway and Backend) to make this work?
I'm using spring-boot 2.3.6.RELEASE
Good question and you are running into the following common OAuth issue with APIs:
Cognito access tokens contain very little user info, which is good practice - they are not customisable and only include a subject claim
Spring wants to create an AuthenticatedPrincipal from just the JWT - but your API also wants additional claims to be added to the AuthenticatedPrincipal
DESIGN PATTERN
There is a design pattern you can use, or borrow ideas from, and my blog post summarises the behaviour. The idea is to define a Claims Principal first, then to populate it with claims from multiple sources at runtime. In your case one source will be the Cognito User Info endpoint.
WORKING CODE
You can run my Sample Spring Boot API, which also uses AWS Cognito, and the key class is the Authorizer. In some setups you may be able to get an API Gateway to do some of this work for you:
How to Run the API
Spring Boot OAuth Integration with Custom Claims
PROs and CONs
This pattern will give you extensible claims but it will also add some complexity to your API, since you need to override the API technology stack's default OAuth behaviour.
I'm trying to invoke a spring web service, using below url in browser the service "myservice" should return XML, ie based on the #RequestMapping annotations is the below URL correct?
> http://localhost:8080/mywebapp/myservice/feeds/allFeeds.xml/
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
#Controller
#RequestMapping("myservice")
public class TheController {
private TheService TheServiceWS;
public TheController(TheService TheServiceWS) {
this.TheServiceWS = TheServiceWS;
}
#RequestMapping(value = "feeds/allFeeds.xml", produces = MediaType.APPLICATION_XML_VALUE)
#ResponseBody
public String getValues() {
return TheServiceWS.getAllFeeds();
}
}
The problem for me was :
The #RequestMapping annotation value "myservice" was incorrect
should have been "mywebservice"
If the web service return as XML, it is the original the SOAP web service. In this case, you couldn't build the web service with #RequestMapping. The #RequestMapping is used when you want to build a REST web service.
In this case, you should use the Spring WS. You have to annotate the class with #Endpoint to create an web service endpoint. In the this endpoint, you create your request mapping with #Payloadroot. Please refer to this