service code
public void call(String number, String text) {
try {
StartOutboundVoiceContactRequest request = StartOutboundVoiceContactRequest.builder()
.instanceId(properties.getInstanceId())
.contactFlowId(properties.getContactFlowId())
.queueId(properties.getQueueId())
.destinationPhoneNumber(number)
//.attributes(Map.of("promptText", text))
.build();
System.err.println(connectClient.toString());
connectClient.startOutboundVoiceContact(request);
}catch(Exception e){
e.printStackTrace();
}
software.amazon.awssdk.services.connect.model.ConnectException: The security token included in the request is invalid. (Service: Connect, Status Code: 403, Request ID: xxxxxxxxxxxxxxxxx, Extended Request ID: null)
how to fix above error
aws config
#Bean
public ConnectClient connectClient(AWSConnectConfigurationProperties properties) {
return ConnectClient.builder()
.region(Region.US_EAST_1)
.build();
}
Related
Hello I'm new to Java and Springboot. I'm currently working with an API where before making a POST request, I would need to generate a Bearer token. In order to generate a Bearer token, I would need to pass in my basic auth credentials to the "/oauth/token" endpoint. My application is having trouble passing my basic auth credentials since by the time I hit the "/v1/some-endpoint", I'm denied authorization because the Bearer token is null.
Here's my initial solution thinking I could check the url in the interceptor, then executing the following line but after debugging, it doesn't seem to be hitting that line.
Is there something I'm missing or not implementing correctly? Am I not implementing the Basic Auth endpoint correctly? Let me know if you need more information. Thanks
#Profile("!offline")
#FeignClient(
value = "someClient",
url = "${someProperty.url}",
configuration = SomeClient.SomeClientConfig.class)
public interface someClient {
#PostMapping("/v1/some-endpoint")
void redeemSomething(someRequestBody data);
#PostMapping("/oauth/token")
static BasicAuthResponse getBasicAuthToken() {
return new BasicAuthResponse();
}
#AllArgsConstructor
class SomeClientConfig extends BaseClientConfig {
private final SomeProperties properties;
private final SomeAuthTokenSupplier tokenSupplier = new SomeAuthTokenSupplier();
#Bean
#Override
public CloseableHttpClient apacheClient() {
return apacheClientFactory(properties.getUseProxy());
}
#Bean
public RequestInterceptor someAuthInterceptor() {
return template -> {
if(template.url().equals("/oauth/token")) {
String authToken = Base64Utils.encodeToString((properties.getCredentials().getUser() + ":" + properties.getCredentials().getUser()).getBytes(Charset.forName("UTF-8")));
template.header("Authorization", authToken);
}
template.header("Authorization", String.format("Bearer %s", tokenSupplier.getToken()));
};
}
private class SomeAuthTokenSupplier {
private volatile String token;
private volatile long retrievedOn = -1L;
String getToken() {
if (updateTokenRequired()) {
synchronized (this) {
if (updateTokenRequired()) {
BasicAuthResponse tokenResponse = getBasicAuthToken();
token = tokenResponse.getAccess_token(); // new token from some api should be assigned here
retrievedOn = Instant.now().toEpochMilli();
}
}
}
return token;
}
private boolean updateTokenRequired() {
return token == null || LocalDateTime.now().minusHours(8L).isAfter(LocalDateTime.ofInstant(Instant.ofEpochMilli(retrievedOn), ZoneId.systemDefault()));
}
}
#Override
public Retryer retryer() {
return new ClientRetry(250L, 2, 3) {
#Override
public void continueOrPropagate(RetryableException e) {
if (e.status() == 401 || e.status() == 403) {
tokenSupplier.token = null;
}
super.continueOrPropagate(e);
}
};
}
}
}
It worth using standard Spring Security OAuth2 Client feature instead in order to support authorization in Feign clients
See docs and code samples: https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client
UPD
See another code sample: https://github.com/int128/feign-oauth2-example
If several service endpoints require different authentication, then it's worth having several Feign clients, each with own configuration
My goal is to authenticate the WebSocket CONNECT frame. I wish to be able to initialize Authentication user = ... by using X-Auth-Token.
TL:DR
I use the X-Auth-Token header. How the current authentication works:
User hit POST /login endpoint with Form Data username and password.
The response header will contain the key X-Auth-Token.
If you hit any REST endpoint with X-Auth-Token the server will recognize the user.
The issue is how to get Authentication from X-Auth-Token in the WebSocket CONNECT frame.
The current solution is to use JWT, however, one of the requirements for this project is a user should be able to invalidate the session. For JWT to be able to do that, the JWT should be a stateful, reference to this SO's the question
#Configuration
#EnableWebSocketMessageBroker
// see: https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-authentication-token-based
#Order(Ordered.HIGHEST_PRECEDENCE + 99)
public class WebSocketAuthenticationConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String sessionId = accessor.getFirstNativeHeader("X-AUTH-TOKEN");
// Authentication user; // How to get Authentication from X-Auth-Token?
// accessor.setUser(user);
}
return message;
}
});
}
}
What I did:
I change Cookie-based authentication by letting the session be provided in a header.
// see: https://docs.spring.io/spring-session/docs/current/reference/html5/#httpsession-rest
#Configuration
// Override HttpSession's Filter, in this instance Spring Session is backed by Redis.
#EnableRedisHttpSession
public class HttpSessionConfig {
// Default connection configuration, to localhost:6739.
#Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
// Tell Spring to use HTTP headers, X-Auth-Token.
#Bean
public HttpSessionIdResolver httpSessionIdResolver() {
return HeaderHttpSessionIdResolver.xAuthToken();
}
}
logic to CONNECT and SUBSCRIBE
const X-Auth-Token = "" // get from the POST `/login` endpoint
const onConnectCallback = () => {
const destinations = ["/topic/channel/1", "/user/queue/messages"];
for (let i = 0; i < destinations.length; i++) {
stompClient.subscribe(destinations[i], (payload) => {
// receiveMessageCallback
});
}
};
const stompConfig = {
brokerURL: "ws://localhost:8080/chat",
connectHeaders: {
"X-Auth-Token": X_Auth_Token,
},
onConnect: onConnectCallback,
};
const stompClient = new StompJs.Client(stompConfig);
stompClient.activate();
Reference
I am worried that X-Auth-Token is not supported in WebSocket because based on SO's answer there is no API to retrieve session by id
I tried to run typescript code that send email using EWS, it works fine.
But when, I run the java code its throws unauthorized error,
Caused by: microsoft.exchange.webservices.data.core.exception.http.HttpErrorException: The remote server returned an error: (401) Unauthorized
microsoft.exchange.webservices.data.core.exception.service.ServiceRequestException: The request failed. The request failed. The remote server returned an error: (401) Unauthorized
I assume it because the Ts code include this line:
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
So the question is how to do this line in java?
Also, do you think there is any other thing I need to add, in order to make the java code work?
Here is the java code...
#Configuration
public class MailConfig {
#Bean(name = "mailExchangeService")
public ExchangeService buildExchangeService() throws URISyntaxException {
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);
service.setCredentials(new WebCredentials("email", "password", "domain"));
service.setUrl(new URI("https://mail.address.something:4567/ews/exchange.asmx"));
service.setWebProxy(new WebProxy("proxy", 1234));
return service;
}
}
#service
#RequiredArgsConstructor
public class MailService {
#Resource(name = "mailExchangeService")
private final ExchangeService service;
public EmailMessage sendMail(String subscription, String subject, String body, File csvFiles) {
EmailMessage message = null;
try{
message = new EmailMessage(this.service);
message.setItemClass("IPM.Note");
message.setSubject(subject);
message.setBody(new MessageBody(bodyType.HTML, body));
message.getAttachments().addFileAttachment(csvFiles.getAbsolutePath());
message.getToRecipients().add(subscription);
message.send();
}catch (Exception e) {
log.error("Error while sending mail", e);
}
return message;
}
}
I'm following JWT guide for quarkus here. I want to send custom response when UserGroup is not allowed to access an api.
This is the sample shown in the guide.
#GET()
#Path("roles-allowed")
#RolesAllowed({"Echoer", "Subscriber"})
#Produces(MediaType.TEXT_PLAIN)
public String helloRolesAllowed(#Context SecurityContext ctx) {
Principal caller = ctx.getUserPrincipal();
String name = caller == null ? "anonymous" : caller.getName();
boolean hasJWT = jwt != null;
String helloReply = String.format("hello + %s, isSecure: %s, authScheme: %s, hasJWT: %s", name, ctx.isSecure(), ctx.getAuthenticationScheme(), hasJWT);
return helloReply;
}
How do i know if the request is unauthorized so that i can send custom response.
Short answer: now it can not be done. (explanation in UPDATE section)
It looks like it is JEE application, so maybe here is your answer
Or try this.
Or add Provider:
#Provider
public class CustomReasonNotAuthorizedException implements ExceptionMapper<NotAuthorizedException> {
public Response toResponse(NotAuthorizedException bex) {
return Response.status(Response.Status.UNAUTHORIZED)
.entity("your text")
.build();
}
}
UPDATE
I checked source code and try it in debug and it looks that execution go through this code as below. So you can not change the message "Not authorized".
HttpAuthenticator authenticator = identity.getAttribute(HttpAuthenticator.class.getName());
RoutingContext context = ResteasyContext.getContextData(RoutingContext.class);
if (authenticator != null && context != null) {
authenticator.sendChallenge(context, null);
} else {
respond(requestContext, 401, "Not authorized");
}
I want to develop a SOAP client using CXF to connect to SharePoint. The authentication scheme is NTLM.
I am blocked on a scenario where the logged-in user of a machine (on which the SOAP client is being run) has access to SharePoint. The CXF soap client always uses the logged-in user. I want to specify some other user credentials (not the logged-in).
As CXF uses in-JDK HttpURLConnection; and what I have read about HttpURLConnection is, it bypasses the specified credentials when the logged-in user is NTLM authenticated.
Codes were tried on CXF version 2.7.11.
Solutions that I have tried out:
1) Setting Conduit authorization
String username = "user";
String password = "password";
JaxWsProxyfactoryBean factory1 = new JaxWsProxyfactoryBean();
factory1.setServiceClass(WebsSoap.class);
factory1.setAddress(url);
factory1.setUsername(username);
factory1.setPassword(password);
WebsSoap service = (WebsSoap) factory1.create();
Client client = ClientProxy.getClient(service);
HTTPconduit conduit = (HTTPconduit) client.getconduit();
conduit.getAuthorization().setAuthorizationType("NTLM");
conduit.getAuthorization().setUserName(username);
conduit.getAuthorization().setPassword(password);
HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
httpClientPolicy.setConnectionTimeout(36000);
httpClientPolicy.setAllowChunking(false);
conduit.setClient(httpClientPolicy);
service.getWeb(".");
Problem:
This does not work for the scenario specified above, as it always uses the logged-in credentials. And when I specify invalid credentials, it does not fail.
2) AsyncHTTPConduit
Another solution is to use AsyncHTTPConduit that uses HttpAsyncClient instead of HttpURLConnection. This is beacuse HTTP components do not bypass specified credentials and logged-in user can be ignored (I have successfully verified this with a test client using HttpClient).
Below is the code snippet::
Bus bus = BusFactory.getDefaultBus();
bus.setProperty( "use.async.http.conduit", "true" );
Client client = ClientProxy.getClient( service );
HTTPConduit http = (HTTPConduit)client.getConduit();
if ( http instanceof AsyncHTTPConduit ) {
AsyncHTTPConduit conduit = (AsyncHTTPConduit)http;
DefaultHttpAsyncClient defaultHttpAsyncClient;
try {
defaultHttpAsyncClient = conduit.getHttpAsyncClient();
}
catch ( IOException exception ) {
throw new RuntimeException( exception );
}
defaultHttpAsyncClient.getCredentialsProvider().setCredentials( AuthScope.ANY,
new NTCredentials( "username", "password", "", "domain" ) );
conduit.getClient().setAllowChunking( false );
conduit.getClient().setAutoRedirect( true );
}
Problem:
Above code throws error:
Authorization loop detected on conduit.
The above code snapshot shows the usage of DefaultHttpAsyncClient which is deprecated now and CloseableHttpAsyncClient is to be used instead. But CloseableHttpAsyncClient does not provide a way to specify credentials to an already existing CloseableHttpAsyncClient object. Not sure how to use CloseableHttpAsyncClient in this scenario.
3) Other solutions
The other solution that I tried out is to use sun.net.www.protocol.http.ntlm.NTLMAuthenticationCallback, to bypass logged-in user authentication, as mentioned here. Use this approach along with solution #1 mentioned above. This works as expected for valid/invalid credentials, and the code bypasses the logged-in credentials :). But when I specify invalid credentials, I do not get HTTP 401 error, instead I get
Could not send message, server reached max retries 20
I am trying to avoid this solution because it uses java’s internal package and there is no way to determine HTTP 401 error directly.
What can I do to arrive at a complete solution?
Try this interceptor. This will avoid automatic authentication.
public class DisableAutomaticNTLMAuthOutInterceptor extends AbstractPhaseInterceptor<Message>
{
private boolean isFieldsAvailable;
private Field tryTransparentNTLMProxyField;
private Field tryTransparentNTLMServerField;
public DisableAutomaticNTLMAuthOutInterceptor() {
super(Phase.PRE_STREAM);
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Void run() {
try {
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMServerField = HttpURLConnection.class.getDeclaredField("tryTransparentNTLMServer");
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMServerField.setAccessible(true);
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMProxyField = HttpURLConnection.class.getDeclaredField("tryTransparentNTLMProxy");
DisableAutomaticNTLMAuthOutInterceptor.this.tryTransparentNTLMProxyField.setAccessible(true);
DisableAutomaticNTLMAuthOutInterceptor.this.isFieldsAvailable = true;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
}
#Override
public void handleMessage(final Message message) throws Fault {
if (this.isFieldsAvailable)
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Void run() {
try {
Object httpConnection = message.get("http.connection");
if (httpConnection != null) {
DisableAutomaticNTLMAuthOutInterceptor.this.processHttpConnection(message.get("http.connection"));
}
} catch (Throwable t) {
t.printStackTrace();
}
return null;
}
});
}
private void processHttpConnection(Object httpConnection) throws IllegalArgumentException, IllegalAccessException {
if (HttpURLConnection.class.isAssignableFrom(httpConnection.getClass())) {
tryTransparentNTLMServerField.set(httpConnection, Boolean.FALSE);
tryTransparentNTLMProxyField.set(httpConnection, Boolean.FALSE);
} else {
Field tempField = null;
for (Field field : httpConnection.getClass().getDeclaredFields()) {
if (HttpURLConnection.class.isAssignableFrom(field.getType())) {
field.setAccessible(true);
tempField = field;
break;
}
}
if (tempField != null) {
processHttpConnection(tempField.get(httpConnection));
}
}
}
}