Implementation of Oauth token generator with feign client #1
Merged
amalka.subasinghe
merged 9 commits from tharusha/oauth-extensions:feign_client_impl
into master
1 month ago
@ -0,0 +1,102 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|||||||
|
|||||||
<!--
|
|||||||
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
|||||||
*
|
|||||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
|||||||
* Version 2.0 (the "License"); you may not use this file except
|
|||||||
* in compliance with the License.
|
|||||||
* You may obtain a copy of the License at
|
|||||||
*
|
|||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||||||
*
|
|||||||
* Unless required by applicable law or agreed to in writing,
|
|||||||
* software distributed under the License is distributed on an
|
|||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|||||||
* KIND, either express or implied. See the License for the
|
|||||||
* specific language governing permissions and limitations
|
|||||||
* under the License.
|
|||||||
-->
|
|||||||
<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>3.3.4</version>
|
|||||||
<relativePath/> <!-- lookup parent from repository -->
|
|||||||
</parent>
|
|||||||
<groupId>o.entgra.oauth.token.generator</groupId>
|
|||||||
<artifactId>oauth-token-generator</artifactId>
|
|||||||
<version>1.0-SNAPSHOT</version>
|
|||||||
<name>oauth_token_generator</name>
|
|||||||
<url/>
|
|||||||
<licenses>
|
|||||||
<license/>
|
|||||||
</licenses>
|
|||||||
<developers>
|
|||||||
<developer/>
|
|||||||
</developers>
|
|||||||
<scm>
|
|||||||
<connection/>
|
|||||||
<developerConnection/>
|
|||||||
<tag/>
|
|||||||
<url/>
|
|||||||
</scm>
|
|||||||
<properties>
|
|||||||
<java.version>21</java.version>
|
|||||||
<spring-cloud.version>2023.0.3</spring-cloud.version>
|
|||||||
</properties>
|
|||||||
<dependencies>
|
|||||||
<dependency>
|
|||||||
<groupId>org.springframework.boot</groupId>
|
|||||||
<artifactId>spring-boot-starter</artifactId>
|
|||||||
</dependency>
|
|||||||
<dependency>
|
|||||||
<groupId>org.springframework.boot</groupId>
|
|||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|||||||
</dependency>
|
|||||||
<dependency>
|
|||||||
<groupId>org.springframework.cloud</groupId>
|
|||||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
|||||||
</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>
|
|||||||
</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>
|
|||||||
<configuration>
|
|||||||
<excludes>
|
|||||||
<exclude>
|
|||||||
<groupId>org.projectlombok</groupId>
|
|||||||
<artifactId>lombok</artifactId>
|
|||||||
</exclude>
|
|||||||
</excludes>
|
|||||||
</configuration>
|
|||||||
</plugin>
|
|||||||
</plugins>
|
|||||||
</build>
|
|||||||
|
|||||||
</project>
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.entgra.auth_token_generator;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableFeignClients
|
||||
public class AuthTokenGeneratorApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AuthTokenGeneratorApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.entgra.auth_token_generator.client;
|
||||
|
||||
import feign.Param;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@FeignClient(name = "auth-token-getter", url = "${auth_token_generation_uri}")
|
||||
public interface AuthFeignClient {
|
||||
|
||||
@RequestMapping(value = "/oauth2/token", method = RequestMethod.POST, consumes = "application/x-www-form-urlencoded")
|
||||
Map<String, String> getToken(@Param("body") String body, @RequestHeader("Authorization") String authHeader);
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.entgra.auth_token_generator.exceptions;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ErrorResponse {
|
||||
|
||||
private int code;
|
||||
private String description;
|
||||
private String message;
|
||||
|
||||
public static ErrorResponse createErrorResponse(int code, String message, String description) {
|
||||
ErrorResponse errorResponse = new ErrorResponse();
|
||||
errorResponse.setCode(code);
|
||||
errorResponse.setMessage(message);
|
||||
errorResponse.setDescription(description);
|
||||
return errorResponse;
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the specific language governing
|
||||
* permissions and limitations under the License.
|
||||
*/
|
||||
package io.entgra.auth_token_generator.service;
|
||||
|
||||
import feign.FeignException;
|
||||
import io.entgra.auth_token_generator.client.AuthFeignClient;
|
||||
import io.entgra.auth_token_generator.util.TokenDataHolder;
|
||||
import io.micrometer.common.util.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.entgra.auth_token_generator.exceptions.ErrorResponse.createErrorResponse;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class TokenService {
|
||||
|
||||
@Autowired
|
||||
private AuthFeignClient authFeignClient;
|
||||
|
||||
@Autowired
|
||||
private TokenDataHolder tokenDataHolder;
|
||||
|
||||
@Value("${client_id}")
|
||||
private String clientId;
|
||||
|
||||
@Value("${client_secret}")
|
||||
private String clientSecret;
|
||||
|
||||
@Value("${user_name}")
|
||||
private String userName;
|
||||
|
||||
@Value("${password}")
|
||||
private String password;
|
||||
|
||||
public ResponseEntity<Object> fetchToken(String scope, String grantType) {
|
||||
if (StringUtils.isBlank(clientId)) {
|
||||
return new ResponseEntity<>(createErrorResponse(400,
|
||||
"Missing client ID",
|
||||
"clientId is not configured."), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(clientSecret)) {
|
||||
return new ResponseEntity<>(createErrorResponse(400,
|
||||
"Missing client secret",
|
||||
"clientSecret is not configured."), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(grantType)) {
|
||||
return new ResponseEntity<>(createErrorResponse(400,
|
||||
"Missing grant type",
|
||||
"grantType is not configured."), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(scope)) {
|
||||
return new ResponseEntity<>(createErrorResponse(400,
|
||||
"Missing scope",
|
||||
"Scope is required to fetch the token."), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// Base64 encode client credentials for Authorization header
|
||||
String auth = clientId + ":" + clientSecret;
|
||||
String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
|
||||
String authHeader;
|
||||
String body;
|
||||
|
||||
switch (grantType) {
|
||||
case "password" -> {
|
||||
authHeader = "Basic " + encodedAuth;
|
||||
body = "grant_type=" + grantType + "&username=" + userName + "&password=" + password + "&scope=" + scope;
|
||||
}
|
||||
case "client_credentials" -> {
|
||||
authHeader = "Basic " + encodedAuth;
|
||||
body = "grant_type=client_credentials&scope=" + scope;
|
||||
}
|
||||
case "iwa-ntlm" -> {
|
||||
return new ResponseEntity<>(createErrorResponse(501,
|
||||
"Not Implemented",
|
||||
"IWA-NTLM grant type not handled directly."), HttpStatus.NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
default -> {
|
||||
return new ResponseEntity<>(createErrorResponse(400,
|
||||
"Invalid grant type",
|
||||
"Unsupported grant type: " + grantType), HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> response;
|
||||
|
||||
try {
|
||||
response = authFeignClient.getToken(body, authHeader);
|
||||
log.info("Request sent to OAuth2 server to get the token");
|
||||
|
||||
if (response != null) {
|
||||
String accessToken = response.get("access_token");
|
||||
if (accessToken != null) {
|
||||
tokenDataHolder.setAccessToken(accessToken);
|
||||
tokenDataHolder.setTokenType(response.get("token_type"));
|
||||
tokenDataHolder.setExpiresIn(Integer.parseInt(response.get("expires_in")));
|
||||
tokenDataHolder.setScope(response.get("scope"));
|
||||
log.info("Access token stored in the DataHolder");
|
||||
return new ResponseEntity<>(accessToken, HttpStatus.OK);
|
||||
} else {
|
||||
log.error("Access token not found in response");
|
||||
return new ResponseEntity<>(createErrorResponse(500,
|
||||
"Token Error",
|
||||
"Access token not found in the response."), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
} catch (FeignException e) {
|
||||
String errorMessage = e.getMessage();
|
||||
|
||||
if (errorMessage != null && errorMessage.contains("[401]")) {
|
||||
log.error("Client credentials or client secret is incorrect: {}", errorMessage);
|
||||
return new ResponseEntity<>(createErrorResponse(401,
|
||||
"Client Credentials Error",
|
||||
"Client credentials or client secret is incorrect."), HttpStatus.UNAUTHORIZED);
|
||||
} else if (errorMessage.contains("Connection refused")) {
|
||||
log.error("Resource server is not working: {}", errorMessage);
|
||||
return new ResponseEntity<>(createErrorResponse(503,
|
||||
"Resource Server Error",
|
||||
"Resource server is not working."), HttpStatus.SERVICE_UNAVAILABLE);
|
||||
} else {
|
||||
log.error("Error while fetching token: {}", errorMessage);
|
||||
return new ResponseEntity<>(createErrorResponse(500,
|
||||
"Feign Client Error",
|
||||
errorMessage), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("An unexpected error occurred: {}", e.getMessage());
|
||||
return new ResponseEntity<>(createErrorResponse(500,
|
||||
"Unexpected Error",
|
||||
e.getMessage()), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
return new ResponseEntity<>(createErrorResponse(500,
|
||||
"Unknown Error",
|
||||
"Failed to fetch the token for unknown reasons."), HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.entgra.auth_token_generator.util;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Data
|
||||
@Component
|
||||
public class TokenDataHolder {
|
||||
|
||||
private String accessToken;
|
||||
private String scope;
|
||||
private int expiresIn;
|
||||
private String tokenType;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
#spring.application.name=auth_token_getter
|
||||
auth_token_generation_uri=https://mgt.sg.local/
|
||||
client_id=nmttDlrcDoNt0qE7p8rXxHiX2AMa
|
||||
client_secret=H2xaw_kZ77JpmlX8cQmyAjVPK6Ma
|
||||
grant_type=client_credentials
|
||||
user_name =""
|
||||
password = ""
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (c) 2018 - 2024, Entgra (Pvt) Ltd. (http://www.entgra.io) All Rights Reserved.
|
||||
*
|
||||
* Entgra (Pvt) Ltd. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package io.entgra.auth_token_generator;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class AuthTokenGeneratorApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in new issue
no licence header