sudden gmail smtp authentication failure when using app password - spring-boot

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.

Related

Spring and SimpleMailMessage: change setTo and setFrom

I am building a Spring app that allows the user to send an email to customer support.
I have noticed though that it doesn't matter what I put in the attributes "setTo" and "setFrom" of the SimpleMailMessage, I keep receiving the email from myself to myself.
Here is my code:
Controller Endpoint
#PostMapping("/send-email")
public ResponseEntity<Object> sendEmail(#RequestParam String userEmail) {
log.info("[START] send-email");
notificationService.sendNotification();
return new ResponseEntity<>(HttpStatus.OK);
}
Service
public void sendNotification(String emailUtente) {
SimpleMailMessage messaggio = new SimpleMailMessage();
messaggio.setTo(customerServiceAddress);
messaggio.setFrom(emailUtente);
messaggio.setSubject("Test Spring Email");
messaggio.setText("Tadaaaa! Email da Spring!");
try{
javaMailSender.send(messaggio);
} catch (Exception ex){
log.error("Error! " + ex.getMessage());
}
}
application.properties
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=theCustomerServiceAddress
spring.mail.password=thePassword
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
Where am I mistaking?
Also, I am not sure what should be put into the property spring.mail.username, I guessed the email address of whom is receiving the email?
Thank you for your experience and time.

Change the Bad credentials error response spring security oauth2

I have a AuthorizationServer which uses password grant_type using spring security. I am using this for mobile application, when a user enter username password to log in, the app calls the token endpoint and generate a token if he/she is an authenticated user. This is all handled by password grant_type itself. For a unsuccessful log in it returns below general error with 400 HTTP status code.
{
"error": "invalid_grant",
"error_description": "Bad credentials"
}
But for my scenario I need customize this error message. Is their a way to change this error message ?
Note that i tried the suggested duplicate question -
Customize authentication failure response in Spring Security using AuthenticationFailureHandler
but it uses the formLogin and it's not working with my implementation.
Thank you,
Rajith
I couldn't find an answer to this problem for many days. Finally, I got help from one of my colleagues. He asked me to follow this tutorial and it worked for me. Now I could transform the default spring framework response to my response template as follows.
{
"status": 400,
"message": "Invalid username or password",
"timestamp": "2020-06-19T10:58:29.973+00:00",
"payload": null
}
But still, we don't know, why authenticationFailure handler is not working. Hope this helps.
If you want to change only the message text in the response, than it will be enough to add the messages.properties file to the classpath of your application with the following content:
AbstractUserDetailsAuthenticationProvider.badCredentials=Invalid username or password
This will lead to the response below:
{
"error": "invalid_grant",
"error_description": "Invalid username or password"
}
Sabin answer is works, but i need to throw the exception using BadCredentialsException,
#Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
private final static Logger LOGGER = LogManager.getLogger(CustomAuthenticationProvider.class);
#Autowired
private UserService userService;
#Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException{
final String username = authentication.getName();
final String password = authentication.getCredentials().toString();
try {
/* CHECKING USER CREDENTIAL */
/* check account */
User userDetail = userService.findByUsername(username);
if (userDetail == null){
throw new Exception("User not found!");
}
/* check password */
String origPass = Utilities.getEncrypted(new String(Base64.decodeBase64(password)), username);
if(!userDetail.getPassword().equals(origPass)){
throw new Exception("Wrong username or password!");
}
/* check is active */
if(!userDetail.getIsActive()){
throw new Exception("User is not active!");
}
/* check allowance in web type */
if(Access.isWeb()){
if(!userDetail.getIsWeb())
throw new Exception("Web access prohibited!");
}
/* check allowance in mobile type */
if(Access.isMobile()){
if(!userDetail.getIsMobile())
throw new Exception("Mobile access prohibited!");
}
/* do some logs */
userService.login(userDetail);
return new UsernamePasswordAuthenticationToken(userDetail, "{noop}".concat(origPass), userDetail.getAuthorities());
} catch (Exception e) {
LOGGER.error("[OAUTH] Error : " + e.getLocalizedMessage());
throw new BadCredentialsException(e.getLocalizedMessage(), e);
}
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}

Password Policy errors not being thrown with LDAP Spring Security

I have am fairly new to Spring Security with LDAP and I am trying to authenticate with a user who has a password expired on the LDAP server(FreeIPA).
I cannot seem to trigger any password expired exception etc. The Password Policy Response Controls always return null....
Here is my code, perhaps I am doing something incorrectly. How do I handle password policy errors? They don't fire at all currently.
<bean id="freeIpaContextSource" class="org.springframework.security.ldap.ppolicy.PasswordPolicyAwareContextSource">
<constructor-arg value="${logon.freeipa.zone.ldap.connection.url}"/>
<property name="base" value="${logon.freeipa.zone.user.dn.base}"/>
</bean>
<bean id="freeIpaLdapTemplate" class="org.springframework.security.ldap.SpringSecurityLdapTemplate">
<constructor-arg name="contextSource" ref="freeIpaContextSource"/>
</bean>
I have a custom LdapAuthenticator below which uses a ldaptemplate to authenticate users.
#Override
public DirContextOperations authenticate(Authentication authentication) {
checkForIllegalStateDuringAuthentication(authentication);
logger.info(String.format("*** Beginning to authenticate against LDAP zone %s ***", authorizationZone.getName()));
zoneAuthenticationService.saveRequestDataInSession((UsernamePasswordAuthenticationToken) authentication, authorizationZone.getName());
CollectingAuthenticationErrorCallback errorCallback = new CollectingAuthenticationErrorCallback();
boolean isAuthenticated = false;
String userName = authentication.getName();
String password = authentication.getCredentials().toString();
String filterLookup = buildLDAPFilterLookupString(userName);
if (StringUtils.isNotBlank(password)) {
logger.info(String.format("*** Attempting authentication for user %s ***", userName));
try {
isAuthenticated = ldapTemplate.authenticate(StringUtils.EMPTY, filterLookup, password, errorCallback);
} catch (Exception exception) {
errorCallback.execute(exception);
}
}
if (!isAuthenticated) {
if (errorCallback.getError() == null) {
errorCallback.execute(new AuthenticationException(null));
}
//Any LDAP exception caught are stored inside the errorCallBack for use later to display an appropriate error.
logger.error(String.format("*** Authentication for user %s has failed. Exception has occurred while system performed LDAP authentication. ***", userName), errorCallback.getError());
throw new LdapAuthenticationException(errorCallback.getError().getMessage(), errorCallback.getError());
}
logger.info(String.format("*** Authentication for user %s has succeeded ***", userName));
return new DirContextAdapter(buildAuthenticatedDN(userName));
}
No matter what I do I cannot get any password policy errors to return. From my understanding you need to set a request control with PasswordPolicyControl when attempting to authenticate, but I never receive any response controls from the server. I have tried implementing something like below, but no luck on anything.
LdapContext context = (LdapContext)ldapTemplate.getContextSource().getContext(buildAuthenticatedDN(userName).toString(), password);
Control[] rctls = new Control[]{new PasswordPolicyControl(false)};
context.reconnect(rctls);
PasswordPolicyResponseControl ctrl = PasswordPolicyControlExtractor.extractControl(context);
//ctrl is always null
if (ctrl.isExpired()) {
throw new
PasswordPolicyException(ctrl.getErrorStatus());
}
What am I doing wrong? I am struggling greatly with this and any help would very much be appreciated.
If your client really sends the correct response control you might hit this issue (open since 7 years):
#1539 [RFE] Add code to check password expiration on ldap bind
IIRC FreeIPA enforces password expiry only during Kerberos pre-authc (kinit).

Spring Boot Security OAuth - handle InternalAuthenticationServiceException

My Spring Boot OAuth REST application returns "401 Unauthorized" status when the database connection failure(Spring Security throws InternalAuthenticationServiceException ).
It's strange, and I need to change status to "500 Internal server error" that client can provide some adequate description, like "service is not available".
If I use WebResponseExceptionTranslator then I can catch response, but if I change HTTP status, it works only when the database active. If the database is shutdown, then I get "401 Unauthorized" again.
How can I solve this problem most gracefully?
Depends on which level the exception is thrown, you might want to add exception handler to your login controller:
#ExceptionHandler(InternalAuthenticationServiceException.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
// convert exception to 500, add logging and
}
Learn more about exception handling here:
https://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
I fix this by adding "try catch" around jdbcTemplate request in my custom UserDetailService.
protected List<UserDetails> loadUsersByUsername(String username) {
try {
userDetailsList = this.getJdbcTemplate().query( USERS_BY_USERNAME, new String[]{username},
new RowMapper() {
public UserDetails mapRow( ResultSet rs, int rowNum ) throws SQLException {
String username = rs.getString( 1 );
/* etc. map user fields */
return new SecurityUser( username, /* other params... */ );
}
});
} catch (CannotGetJdbcConnectionException e){
logger.error( "UserDetailService SQL error: " + e.getMessage(), e );
}
return userDetailsList;
}
And then I check InternalAuthenticationServiceException
by WebResponseExceptionTranslator and change response status.
It seems that when I catch CannotGetJdbcConnectionException then something ruins in chain. It works, but I will leave my question open, maybe someone can offer a more clear solution.

Spring Security + Auth LDAP : BindRequest and UnbindRequest?

After days of Google researching, Reading The F* Spring Security Manual and testing, I'm becoming desperate ...
The context : I'm implementing a Micro-Services architecture with Eureka etc...
I implemented an Auth Service which works very well with a MySQL authentication database. But now, I want to join my company LDAP through an OpenLDAP who works adequately.
So, I'm trying to join the LDAP with my spring security authentication.
The code of my configure() method (I replaced my company and domain name, the account {0} is "test"):
auth.ldapAuthentication()
.contextSource()
.url("ldap://myldap/ou=users,dc=mydomain,dc=mycompany")
.and()
.userDnPatterns("cn={0}");
I also tried different ways to write this and all the time, I get Bad Credentials or LDAP 32 error. With userDnPattern, usersearchbase method, passwordcompare, passwordencoder and others. I also tried to put DC in the root() method and the OU in the group...() method, no change (I think in fact that Spring Security sort these parameters smartly before sending LDAP Requests). To be honest, I tried 357654 differents ways to write the configure() method ...
The problem is that : When I put the same config, credentials, domains ... in an LDAP explorer software, it works correctly.
So I monitored LDAP networks exchanges with Wireshark and I saw this :
Wireshark screen
As we can see, there's 8 requests exchanged. The first 5 are OK. It find my account "test" correctly. But there's three over requests (with the unbindRequest which going back).
The problem is that Spring give me the result of the last request and say me the account doesn't exist or the credentials don't work, etc...
Have you got a clue for this ? Do you know how Spring Security works to question LDAP ? How can I do to contact my LDAP adequately with the framework ?
Thank you for reading.
Help me Stack Overflow, you're my only hope ...
I finally found the problem and got a solution.
My enterprise LDAP is an LDAP above an AD.
And this LDAP+AD needs Bind Authentication and doesn't authorize anonymous bind then authentication.
In Spring Security, there's an object which can do this : BindAuthenticator
This is how I try to use it (and it works).
#Override
protected void configure(AuthenticationManagerBuilder authManagerBuilder) throws Exception {
authManagerBuilder.authenticationProvider(ldapAuthenticationProvider()).userDetailsService(userDetailsService());
}
#Bean
public LdapAuthenticationProvider ldapAuthenticationProvider() throws Exception {
LdapAuthenticationProvider lAP = new LdapAuthenticationProvider(ldapAuthenticator(), ldapAuthoritiesPopulator());
return lAP;
}
#Bean
public LdapContextSource ldapContextSource() throws Exception {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource([URL of the LDAP]);
return contextSource;
}
#Bean
public LdapAuthenticator ldapAuthenticator() throws Exception {
BindAuthenticator authenticator = new BindAuthenticator(ldapContextSource());
authenticator.setUserDnPatterns(new String[] {"CN={0},"+[MY ENTERPRISE LDAP FILTER]});
return authenticator;
}
Hope this sample code will help some people ...
Thank you !

Resources