JavaMail with gmail SMTP - How to handle an unexisting email addresses - spring-boot

In my project I'm using a JavaMail implementation to send emails through GMAIL's SMTP server.
The general concept is ok, my custom validators checks whether the e-mail was correctly formulated etc...
Everything is fine, however I am not able to catch anything what should be considered, as unsent email. I went deeper into JavaMail docs, and they advised to catch SendMailException and MessageException, but based on the code, it handles only an incorrectly formulated e-mail addresses or an empty Strings. If the user will come up with an unexisting email, but well formulated, the API will not inform that anything happened.
Did anyone handle with the similar issue and knows how to go forward?
Thanks in advance for your help.
#Bean
public JavaMailSender javaMailSender(EmailProperties prop) {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost(prop.getHost());
mailSender.setPort(prop.getPort());
mailSender.setUsername(prop.getUsername());
mailSender.setPassword(prop.getPassword());
mailSender.setDefaultFileTypeMap(FileTypeMap.getDefaultFileTypeMap());
mailSender.setJavaMailProperties(getProperties(prop));
mailSender.setProtocol(prop.getProtocol());
mailSender.setDefaultEncoding("UTF-8");
return mailSender;
}
private Properties getProperties(EmailProperties prop) {
Properties props = new Properties();
props.put("mail.transport.protocol", prop.getProtocol());
props.put("mail.smtp.auth", prop.getAuth());
props.put("mail.smtp.starttls.enable", prop.getStarttls());
props.put("mail.debug", prop.getDebug());
return props;
}

Related

sudden gmail smtp authentication failure when using app password

So I am trying to set up a spring boot app to send out automated emails. I have created a gmail account specifically for this purpose. I followed a tutorial which directed me to set up two factor authentication for my gmail account, and set up an app password to login. I did this and everything worked fine, I sent a test email from the app and it worked. HOWEVER without making any code changes I tried the proccess again a few hours later and I got the following message:
Exception in thread "main" org.springframework.mail.MailAuthenticationException: Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 534-5.7.9 Please log in with your web browser and then try again. Learn more at
534 5.7.9 https://support.google.com/mail/?p=WebLoginRequired v8-20020a05683018c800b005cb39fc3e15sm11417144ote.13 - gsmtp
Here is my application.properties file:
spring.mail.protocol=smtp
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=
spring.mail.password=
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
mail.smtp.debug=true
and here is my mail service (although this error occurs at app startup so I dont think its the cause)
#Service("mailService")
public class MailServiceImpl implements MailService {
#Autowired
private JavaMailSender mailSender;
public void sendEmail(Mail mail) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
try {
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage, true);
mimeMessageHelper.setSubject(mail.getMailSubject());
mimeMessageHelper.setFrom(new InternetAddress(mail.getMailFrom(), "tyler"));
mimeMessageHelper.setTo(mail.getMailTo());
mimeMessageHelper.setText(mail.getMailContent());
mailSender.send(mimeMessageHelper.getMimeMessage());
} catch (MessagingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
I have tried following the link for advice but it recommends I use an app password (which I already am) or to enable "less secure apps" (which I cant as a 2FA user). Any help would be greatly appreciated.

Create TCP client to send and receive in spring boot application

I am supposed to interact with a legacy system where I have to setup a TCP client using spring-integration in java/kotlin to send a message to a TCP system and receive its response, parse this response and send it to other client via REST. I went through many documentations and blogs which tells how to do via xml. Not able to find corresponding annotations for everything. Any code snippet will be very helpful.
#Service
class MyService{
#Autowired
MyGateway gateway;
public String callTCPClient(String msg){
return gateway.exchange(msg);
}
}
interface MyGateway{
String exchange (String msg)
}
As is shown in that answer, you can do whatever you want after the response is received...
#Bean
public IntegrationFlow client() {
return IntegrationFlows.from(MyGateway.class)
.handle(Tcp.outboundGateway(
Tcp.netClient("localhost", 1234)
.serializer(codec()) // default is CRLF
.deserializer(codec()))) // default is CRLF
.transform(Transformers.objectToString()) // byte[] -> String
.get();
}
In this case, we simply transform the byte array to a String, but you can perform whatever operations you want on it, e.g. JSON to Object. You can add as many steps as you want - add .handle(...) elements to call arbitrary methods. Read the Spring Integration Reference Manual.

Invalid JWToken: kid is a required JOSE Header

I am trying to implement an Oauth2 Authorization Server with SpringBoot using this guide as a reference.
My keystore has a single key. I have successfully managed to create a JWToken (I can check it at jwt.io).
I have also a test Resource Server. When I try to access any endpoint I receive the following message:
{
"error": "invalid_token",
"error_description": "Invalid JWT/JWS: kid is a required JOSE Header"
}
The token really does not have a kid header but I can not figure out how to add it. I can only add data to its payload, using a TokenEnchancer. It also seems that I am not the first one with this issue.
Is there any way to add this header or, at least, ignore it at the resource server?
I've been working on an article that might help you out here:
https://www.baeldung.com/spring-security-oauth2-jws-jwk
So, to configure a Spring Security OAuth Authorization Server to add a JWT kid header, you can follow the steps of section 4.9:
create a new class extending the JwtAccessTokenConverter
In the constructor:
configure the parent class using the same approach you've been using
obtain a Signer object using the signing key you're using
override the encode method. The implementation will be the same as the parent one, with the only difference that you’ll also pass the custom headers when creating the String token
public class JwtCustomHeadersAccessTokenConverter extends JwtAccessTokenConverter {
private JsonParser objectMapper = JsonParserFactory.create();
final RsaSigner signer;
public JwtCustomHeadersAccessTokenConverter(KeyPair keyPair) {
super();
super.setKeyPair(keyPair);
this.signer = new RsaSigner((RSAPrivateKey) keyPair.getPrivate());
}
#Override
protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
String content;
try {
content = this.objectMapper.formatMap(getAccessTokenConverter().convertAccessToken(accessToken, authentication));
} catch (Exception ex) {
throw new IllegalStateException("Cannot convert access token to JSON", ex);
}
Map<String, String> customHeaders = Collections.singletonMap("kid", "my_kid");
String token = JwtHelper.encode(content, this.signer, this.customHeaders)
.getEncoded();
return token;
}
}
Then, of course, create a bean using this converter:
#Bean
public JwtAccessTokenConverter accessTokenConverter(KeyPair keyPair) {
return new JwtCustomHeadersAccessTokenConverter(keyPair);
}
Here I used a KeyPair instance to obtain the signing key and configure the converter (based on the example of the article), but you might adapt that to your configuration.
In the article I also explain the relevant endpoints provided by the Spring Security OAuth Authentication Server.
Also, regarding #Ortomala Lokni's comment, I wouldn't expect Spring Security OAuth to add any new features at this point. As an alternative, you probably can wait to have a look at Spring Security's Authorization Server features, planned to be released in 5.3.0
I managed to solve it by changing the parameter used to identify the URL where the clients will retrieve the pubkey.
On application.properties, instead of:
security.oauth2.resource.jwk.key-set-uri=http://{auth_server}/.well-known/jwks.json
I used:
security.oauth2.resource.jwt.key-uri=http://{auth_server}/oauth/token_key
If I understood correctly, the key-set-uri config points to an endpoint that presents a set of keys and there is the need for a kid. On the other side key-uri config points to an endpoint with a single key.

Spring Boot Mail send email using acces token

I have simple mail sending functionality in project which configured in one bean.
#Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl javaMailSender = new JavaMailSenderImpl();
Properties properties = new Properties();
properties.setProperty("mail.smtp.auth", "false");
properties.setProperty("mail.smtp.socketFactory.port", "465");
properties.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
properties.setProperty("smtp.socketFactory.fallback", "false");
properties.setProperty("mail.smtp.starttls.enable", "true");
properties.setProperty("mail.smtp.starttls.required", "true");
javaMailSender.setHost("smtp.gmail.com");
javaMailSender.setProtocol("smtp");
javaMailSender.setUsername("username");
javaMailSender.setPassword("password");
javaMailSender.setJavaMailProperties(properties);
return javaMailSender;
}
and it works great.
Now I want to add functionality for sending emails via accessToken/refreshToken of specific email.
How to do it? What should I extend in my bean or add another bean for sending with token? I couldn't find some example which is full explained. As I understand I should add setFrom() and in setPassword() put accessToken
The use of OAUTH2 with JavaMail is explained on the JavaMail project page.
Also, you should fix these common mistakes in your code.

Sending Error message in Spring websockets

I am trying to send error messages in Spring websockets with STOMP over SockJS.
I am basically trying to achieve which is being done here.
This is my Exception Handler
#MessageExceptionHandler
#SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(Exception message) throws ApplicationError {
return new ApplicationError("test");
}
And I am subscribing to
stompClient.subscribe('/user/queue/error', stompErrorCallback, {token: accessToken});
User in my case is not authenticated, but from here
While user destinations generally imply an authenticated user, it
isn’t required strictly. A WebSocket session that is not associated
with an authenticated user can subscribe to a user destination. In
such cases the #SendToUser annotation will behave exactly the same as
with broadcast=false, i.e. targeting only the session that sent the
message being handled.
All this works fine when I am throwing this error from myHandler which is my Websocket Handler defined in websocket config.
I have a ClientInboundChannelInterceptor which extends ChannelInterceptorAdapter which intercepts all the messages in preSend.
In case of any exception in this interceptor, I want to throw it back to the user session which sent this message,
public class ClientInboundChannelInterceptor extends ChannelInterceptorAdapter {
#Autowired
#Lazy(value = true)
#Qualifier("brokerMessagingTemplate")
private SimpMessagingTemplate simpMessagingTemplate;
#Override
public Message<?> preSend(Message message, MessageChannel channel) throws IllegalArgumentException{
if(some thing goes wrong)
throw new RuntimeException();
}
#MessageExceptionHandler
#SendToUser(value = "/queue/error",broadcast = false)
public ApplicationError handleException(RuntimeException message) throws ApplicationError {
return new ApplicationError("test");
}
}
#MessageExceptionHandler does not catch this exception. So I tried sending it to the user directly using simpMessagingTemplate.
I basically want to do :
simpMessagingTemplate.convertAndSendToUser(SOMETHING,"/queue/error",e);
SOMETHING should be the correct username but user is not authenticated in my case, so I can't use headerAccessor.getUser().getName()
I have even tried with
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getHeader("","/queue/error",e, Collections.singletonMap(SimpMessageHeaderAccessor.SESSION_ID_HEADER, headerAccessor.getSessionId()));
but this is not working.
I have even tried headerAccessor.getSessionId() in the place of username, but that does not seem to work.
What is the correct way to do this?
What should I use as username in convertAndSendToUser?
My initial intuition was correct, sessionId is used as the username in case of unauthenticated user situations, but the problem was with headers.
After few hours of debugging through #SendToUser and simpMessagingTemplate.convertAndSendToUser(), I realised that if we use #SendToUser headers will be set automatically and we have to explicitly define the headers if we are using simpMessagingTemplate.convertAndSendToUser().
#SendToUser was setting two headers,
simpMessageType:SimpMessageType.MESSAGE,simpSessionId:sessionId
So I have tried adding the headers,
String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId);
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headerMap);
It did not work, I have tried giving the headers as MessageHeaders
String sessionId = headerAccessor.getSessionId();
Map<String,Object> headerMap = new HashMap<>();
headerMap.put("simpMessageType", SimpMessageType.MESSAGE);
headerMap.put("simpSessionId",sessionId);
MessageHeaders headers = new MessageHeaders(headerMap);
simpMessagingTemplate.convertAndSendToUser(headerAccessor.getSessionId(),"/queue/error",e,headers);
didn't work either.
After some more debugging I found out the correct way to set the headers, and probably this is the only way to create these headers(from SendToMethodReturnValueHandler.java).
private MessageHeaders createHeaders(String sessionId) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}
So finally,
String sessionId = headerAccessor.getSessionId();
template.convertAndSendToUser(sessionId,"/queue/error","tesssssts",createHeaders(sessionId));
did the trick.
You can use convertAndSendToUser() only if that user is subscribed to the destination:
super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
Where user can be just sessionId - headerAccessor.getSessionId()
The #MessageExceptionHandler does its work only withing #MessageMapping or #SubscribeMapping.
See SendToMethodReturnValueHandler source code for more info.

Resources