I’m having trouble implementing Google OAuth2 authentication for sign-up and sign-in in my Jmix project, coming from a Spring Boot background where it was easier to implement. Despite trying the OpenID Connect add-on, I haven’t been able to get it working. My goal is to allow users to register and log in to my application using their Google accounts, and I’m looking for guidance on how to achieve this in Jmix.
Can you use Keycloak?
If so you can implement social login on Keycloak side (which should be easier) and then make your Jmix application use Keycloak as IDP via OIDC add-on.
@Component
public class OAuth2UserPersistence {
@Autowired
private DataManager dataManager;
@Authenticated
public User loadUserByGoogleId(String googleId) {
return dataManager.load(User.class)
.query("select u from User u where u.googleId = :googleId")
.parameter("googleId", googleId)
.optional()
.orElseGet(() -> {
User user = dataManager.create(User.class);
user.setGoogleId(googleId);
return user;
});
}
@Authenticated
public User saveUser(User user) {
return dataManager.save(user);
}
}
Create/modify your security config to map external user to Jmix user
@EnableWebSecurity
@Configuration
public class OAuth2SecurityConfiguration extends FlowuiVaadinWebSecurity {
private static final Logger log = LoggerFactory.getLogger(OAuth2SecurityConfiguration.class);
private final RoleGrantedAuthorityUtils authorityUtils;
private final OAuth2UserPersistence oidcUserPersistence;
public OAuth2SecurityConfiguration(RoleGrantedAuthorityUtils authorityUtils,
OAuth2UserPersistence oidcUserPersistence) {
this.authorityUtils = authorityUtils;
this.oidcUserPersistence = oidcUserPersistence;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.oauth2Login(configurer ->
configurer
.loginPage(getLoginPath())
.userInfoEndpoint(userInfoEndpointConfig ->
userInfoEndpointConfig
.oidcUserService(oidcUserService()))
.successHandler(this::onAuthenticationSuccess)
);
}
private void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException {
// redirect to the main screen after successful authentication using auth provider
new DefaultRedirectStrategy().sendRedirect(request, response, "/");
}
/**
* Service responsible for loading OIDC users (Google uses OIDC protocol)
*/
private OAuth2UserService<OidcUserRequest, OidcUser> oidcUserService() {
OidcUserService delegate = new OidcUserService();
return (userRequest) -> {
// Delegate to the default implementation for loading a user
OidcUser oidcUser = delegate.loadUser(userRequest);
//find or create user with given Google id
String googleId = oidcUser.getSubject();
User jmixUser = oidcUserPersistence.loadUserByGoogleId(googleId);
jmixUser.setUsername(googleId);
jmixUser.setGoogleId(googleId);
jmixUser.setEmail(oidcUser.getEmail());
User savedJmixUser = oidcUserPersistence.saveUser(jmixUser);
savedJmixUser.setAuthorities(getDefaultGrantedAuthorities());
return savedJmixUser;
};
}
/**
* Builds granted authority list that grants access to the FullAccess role
*/
private Collection<GrantedAuthority> getDefaultGrantedAuthorities() {
return List.of(authorityUtils.createResourceRoleGrantedAuthority(FullAccessRole.CODE));
}
}
UI
Add button to your login screen and in click\action handler execute UI.getCurrent().getPage().setLocation("/oauth2/authorization/google");