How to authenticate Spring Boot rest API having POST method using Azure AD - spring-boot

In my spring boot rest api I am using a POST method. I am using Azure AD to authenticate api. When hitting an endpoint it is giving status as 200 OK but not doing the required POST operations. Even loggers are not getting printed from the controller #PostMapping
Can some help what needs to be fixed ...
In POM spring security and below dependency.
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-spring-boot-starter</artifactId>
</dependency>
Registered required properties in properties file.
azure.activedirectory.session-stateless
azure.activedirectory.tenant-id
azure.activedirectory.user-group.allowed-groups
spring.security.oauth2.client.registration.azure.client-id
spring.security.oauth2.client.registration.azure.client-secret
NOTE: There is no front end as of now.

If you use #PostMapping to authenticate for access token, you don't need to use azure-spring-boot-starter. You could refer the code sample based on auth code flow:
Controller:
#PostMapping("/access_token")
public AuthenticationResult authorizeToken(#RequestBody #Valid AuthorizationRequest authorizationCode) throws Exception {
return tokenService.getAccessTokenFromAuthorizationCode(authorizationCode.getCode(), authorizationCode.getRedirectUri());
}
Service:
public AuthenticationResult getAccessTokenFromAuthorizationCode(String authorizationCode, String redirectUri) throws Exception {
AuthorizationCode request = new AuthorizationCode(authorizationCode);
try {
return tokenGenerator.getAccessToken(request, redirectUri);
} catch (Throwable throwable) {
return throwException(throwable);
}
}
TokenGenerator function:
public AuthenticationResult getAccessToken(
AuthorizationCode authorizationCode, String currentUri)
throws Throwable {
String authCode = authorizationCode.getValue();
ClientCredential credential = new ClientCredential(clientId,
clientSecret);
AuthenticationContext context = null;
AuthenticationResult result = null;
ExecutorService service = null;
try {
service = Executors.newFixedThreadPool(1);
context = new AuthenticationContext(authority + tenant + "/", true,
service);
Future<AuthenticationResult> future = context
.acquireTokenByAuthorizationCode(authCode, new URI(
currentUri), credential, resource, null);
result = future.get();
} catch (ExecutionException e) {
throw e.getCause();
} finally {
service.shutdown();
}
if (result == null) {
throw new ServiceUnavailableException(
"authentication result was null");
}
return result;
}
pom.xml
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>adal4j</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.nimbusds</groupId>
<artifactId>oauth2-oidc-sdk</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
application.properties
security.oauth2.client.clientId=xxx
security.oauth2.client.clientSecret=xxx
security.oauth2.client.tenant=xxx
security.oauth2.client.accessTokenUri=https://login.microsoftonline.com/<tenant-id>/oauth2/token
security.oauth2.client.userAuthorizationUri=https://login.microsoftonline.com/<tenant-id>/oauth2/authorize
security.oauth2.client.authority=https://login.microsoftonline.com/
security.oauth2.client.resource=https://graph.windows.net/ // scope of API
security.oauth2.resource.userInfoUri=https://graph.windows.net/me?api-version=1.6 // call API
If you would like to authenticate in backend with Spring Boot Starter, please refer to this example based on implicit grant flow.

Related

spring boot with swagger OAuth not working

I added swagger dependency and enabled it, and am able to see all the API but authorize API isn't working.
am using below version of swagger:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
Below is my code :
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Value("${security.oauth2.client.client-id}")
public String CLIENT_ID;
#Value("${security.oauth2.client.client-secret}")
public String CLIENT_SECRET;
public String AUTH_SERVER = "https://login.microsoftonline.com/common/oauth2/v2.0";
#Bean
public Docket swaggerConfiguration() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
//.apis(RequestHandlerSelectors.any())
//.paths(PathSelectors.ant("/api/v1/**/**"))
.apis(RequestHandlerSelectors.basePackage("edu.mayo.ima.ccs.rpc_backend.controller"))
.paths(PathSelectors.any())
.build()
.securitySchemes(Arrays.asList(securityScheme()))
.securityContexts(Arrays.asList(securityContext()))
.apiInfo(getApiInfo());
}
#Bean
public SecurityConfiguration security() {
return SecurityConfigurationBuilder.builder()
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)
.scopeSeparator(" ")
.useBasicAuthenticationWithAccessCodeGrant(true)
.build();
}
private SecurityScheme securityScheme() {
GrantType grantType = new AuthorizationCodeGrantBuilder()
.tokenEndpoint(new TokenEndpoint(AUTH_SERVER + "/token", "oauthtoken"))
.tokenRequestEndpoint(
new TokenRequestEndpoint(AUTH_SERVER + "/authorize", CLIENT_ID, CLIENT_SECRET))
.build();
SecurityScheme oauth = new OAuthBuilder().name("spring_oauth")
.grantTypes(Arrays.asList(grantType))
.scopes(Arrays.asList(scopes()))
.build();
return oauth;
}
private ApiInfo getApiInfo() {
return new ApiInfo(
"Protocol Catalag ",
"",
"1.0.0",
"",
null,
"",
"",
Collections.emptyList()
);
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(
Arrays.asList(new SecurityReference("spring_oauth", scopes())))
.forPaths(PathSelectors.any())
.build();
}
private AuthorizationScope[] scopes() {
AuthorizationScope[] scopes = {
new AuthorizationScope("access_as_user", "access for application")
};
return scopes;
}
}
With the above configuration all Api are showing on the swagger but Authorize them give error.
Below is the screen when Authorize buttton is clicked.
Help is Appreciated.!
Please make sure to add the access_as_user permission under API permissions in the portal and make sure the API is exposed.
Application id uri is in the format api://, you can give other name to use in app.
In the Example here I gave app id uri : api://my_spring_boot_api
You should then be able to see added scope under scopes.
Then select the access_as_user permission you have added .(API Permissions>add permission>My APIs > select the required app >check the permission> add permissions)
Then you may grant consent as below
Here I exposed scope >> api://my_spring_boot_api/access_as_user. Make sure to use the same scope configured in portal is included in application configuration.
The scope should include the exposing resource's identifier (the Application ID URI) in the code too.
Here Ex:
scopes: "api://my_spring_boot_api/access_as_user "
and when you call web app please make sure to send Id_token and if you call graph api you may send access token.

Spring Boot OAuth2 Resource Server: How library can verify JWT token without public key?

I have following spring boot app with minimal configuration
application.properties
server.port=8081
spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/auth/realms/master
pom.xml
<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.nurgasemetey</groupId>
<artifactId>springboot-keycloak</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-keycloak</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
controller
#RestController
#RequestMapping("/users")
public class UsersController {
#GetMapping("/status/check")
public String status(#AuthenticationPrincipal Jwt principal) {
return "working";
}
}
It seems that Spring Boot Oauth2 doesn't use public key, as I see in code:
OAuth2ResourceServerProperties
/**
* JSON Web Key URI to use to verify the JWT token.
*/
private String jwkSetUri;
/**
* JSON Web Algorithm used for verifying the digital signatures.
*/
private String jwsAlgorithm = "RS256";
/**
* URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0
* Authorization Server Metadata endpoint defined by RFC 8414.
*/
private String issuerUri;
/**
* Location of the file containing the public key used to verify a JWT.
*/
private Resource publicKeyLocation;
But I didn't give publicKeyLocation, but app can verify without public key.
Under the hood it uses JwtIssuerValidator and JwtTimestampValidator validators.
On other hand, with express-jwt, it requires public key for offline verification
const express = require('express');
const jwt = require('express-jwt');
const app = express();
const secret = 'secret';
const fs = require('fs');
var publicKey = fs.readFileSync('public.pub');
app.get('/protected', jwt({ secret: publicKey, algorithms: ['RS256'] }), (req, res) => {
res.send('protected');
})
app.listen(3000, () => console.log('server started'));
How the Spring Boot Oauth verifies without public key?
Self answer.
Firstly, it seems that http://localhost:8080/auth/realms/master exposes public key. As said in this Generate JWT Token in Keycloak and get public key to verify the JWT token on a third party platform - Stack Overflow and in this comment to this question by #Thomas Kåsene
Secondly, I digged spring boot oauth2 code and stumbled to this code in
ReactiveOAuth2ResourceServerJwkConfiguration
#Bean
#Conditional(IssuerUriCondition.class)
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
}
JwtDecoderProviderConfigurationUtils
private static Map<String, Object> getConfiguration(String issuer, URI... uris) {
String errorMessage = "Unable to resolve the Configuration with the provided Issuer of " +
"\"" + issuer + "\"";
for (URI uri : uris) {
try {
RequestEntity<Void> request = RequestEntity.get(uri).build();
ResponseEntity<Map<String, Object>> response = rest.exchange(request, typeReference);
Map<String, Object> configuration = response.getBody();
if (configuration.get("jwks_uri") == null) {
throw new IllegalArgumentException("The public JWK set URI must not be null");
}
return configuration;
} catch (IllegalArgumentException e) {
throw e;
} catch (RuntimeException e) {
if (!(e instanceof HttpClientErrorException &&
((HttpClientErrorException) e).getStatusCode().is4xxClientError())) {
throw new IllegalArgumentException(errorMessage, e);
}
// else try another endpoint
}
}
throw new IllegalArgumentException(errorMessage);
}
which seems to fetch public key from issuer-uri given in application.properties. After it fetched it verifies jwt tokens with fetched public key.
To test
Close your jwt provider, keycloak in my case and run spring boot application, then it gives
Caused by: java.lang.IllegalArgumentException: Unable to resolve the Configuration with the provided Issuer of "http://localhost:8080/auth/realms/master"

Need help testing a REST controller that requires oauth2

I am following this example for how to test my REST controller with oauth2. Testing an OAuth Secured API with Spring MVC
The code that I am stuck on is this line .with(httpBasic("fooClientIdPassword","secret"))
Does anyone know where is httpBasic method coming from? How is it instantiated, etc.? Thank you.
private String obtainAccessToken(String username, String password) throws Exception {
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("grant_type", "password");
params.add("client_id", "fooClientIdPassword");
params.add("username", username);
params.add("password", password);
ResultActions result
= mockMvc.perform(post("/oauth/token")
.params(params)
.with(httpBasic("fooClientIdPassword","secret"))
.accept("application/json;charset=UTF-8"))
.andExpect(status().isOk())
.andExpect(content().contentType("application/json;charset=UTF-8"));
String resultString = result.andReturn().getResponse().getContentAsString();
JacksonJsonParser jsonParser = new JacksonJsonParser();
return jsonParser.parseMap(resultString).get("access_token").toString();
}
The httpBasic method comes from SecurityMockMvcRequestPostProcessors
I suppose you cannot find it cause you have not imported the dependency in your project. Once you add
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
in your pom you will be able to import and use it.

spring boot with restcontroller using jsonobject not working

I am using spring boot, and while making a restcontroller or controller if I use the jsonobject type request then it doesnt work, whereas same works when I change type to string.
#Controller
#RequestMapping("rest/dummy")
public class CustomerController {
#GetMapping("test")
public ResponseEntity test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return new ResponseEntity(inputData.toString(), HttpStatus.OK);
}
pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20171018</version>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0.2</version>
</dependency>
I do want to use it both GET and POST type and also I want to use jsonobject for both request and response as the data can change on fly and its type.
In RequestParam , we send key values which added in URL,
To send Json object send it in RequestBody .
Use #RequestBody and send your Json in body part of your request.
Using real POJOs as params and return values is the better approach imo. Use Jackson annotations to configure those POJOs.
Anyways. This should work:
#GetMapping("test")
public ResponseEntity<String> test(#RequestParam("req") JSONObject inputData) {
org.json.JSONObject response = new org.json.JSONObject();
response.put("abc", "123");
return ResponseEntity.ok(inputData.toString());
}
alternatively
#GetMapping("test")
public ResponseEntity<SomeOutputDto> test(#RequestParam("req") String inputData) {
SomeOutputDto out = new SomeOutputDto();
out.setAbc(123);
return ResponseEntity.ok(dto);
}
this requires a additional class: SomeOutputDto, but on the other hand, you have more control over your code.
public class SomeOutputDto {
private int abc = 0;
public void setAbc(int v) {
this.abc = v;
}
public int getAbc() { return this.abc; }
}
Got it working by using apache-tomcat 8.0.15, the same doesnt work with apache-tomcat 8.0.49

Spring MVC 4 response body serialization works with JSON but not with XML

I am working on setting up REST API with Spring 4. The HTTP Message converters are present by default for JSON & XML. I try to setup two end-points, one for returning JSON & another for XML. The JSON object seems to be returned as expected but when i try to hit xml, i end up with 406 exception,
The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.
I have included the Maven dependencies for both JSON & XML. Below is the Snippet of pom.xml,
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${jaxb-api.version}</version>
</dependency>
Below is the controller code,
#RestController
#RequestMapping(value="/employee")
public class HelloController {
#RequestMapping(method=RequestMethod.GET , produces="application/json",value="/hello.json")
public List<Employee> getEmployeeJson(){
Employee emp = new Employee();
emp.setId(1);
emp.setName("x");
Employee emp1 = new Employee();
emp1.setId(2);
emp1.setName("y");
List<Employee> res = new ArrayList<Employee>();
res.add(emp);
res.add(emp1);
return res;
}
#RequestMapping(method=RequestMethod.GET , produces="application/xml",value="/hello.xml")
public List<Employee> getEmployeeXml(){
Employee emp = new Employee();
emp.setId(1);
emp.setName("x");
Employee emp1 = new Employee();
emp1.setId(2);
emp1.setName("y");
List<Employee> res = new ArrayList<Employee>();
res.add(emp);
res.add(emp1);
return res;
}
}
Do share your thoughts on what am missing here
According to the documentation you should add jackson-dataformat-xml dependency to enable response body XML serialization. In case you are using Maven just add:
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

Resources