fixed oauth2 authorization for apps of version 1.3.0 and less

This commit is contained in:
dashevchenko 2024-11-25 18:48:01 +02:00
parent 0c200d963e
commit c8d627371b
6 changed files with 43 additions and 16 deletions

View File

@ -38,6 +38,7 @@ import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriComponentsBuilder;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.OAuth2ClientId; import org.thingsboard.server.common.data.id.OAuth2ClientId;
import org.thingsboard.server.common.data.oauth2.PlatformType;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.oauth2.OAuth2ClientService;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -85,8 +86,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
String registrationId = this.resolveRegistrationId(request); String registrationId = this.resolveRegistrationId(request);
String redirectUriAction = getAction(request, "login"); String redirectUriAction = getAction(request, "login");
String appPackage = getAppPackage(request); String appPackage = getAppPackage(request);
String platform = getPlatform(request);
String appToken = getAppToken(request); String appToken = getAppToken(request);
return resolve(request, registrationId, redirectUriAction, appPackage, appToken); return resolve(request, registrationId, redirectUriAction, appPackage, platform, appToken);
} }
@Override @Override
@ -96,8 +98,9 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
} }
String redirectUriAction = getAction(request, "authorize"); String redirectUriAction = getAction(request, "authorize");
String appPackage = getAppPackage(request); String appPackage = getAppPackage(request);
String platform = getPlatform(request);
String appToken = getAppToken(request); String appToken = getAppToken(request);
return resolve(request, registrationId, redirectUriAction, appPackage, appToken); return resolve(request, registrationId, redirectUriAction, appPackage, platform, appToken);
} }
private String getAction(HttpServletRequest request, String defaultAction) { private String getAction(HttpServletRequest request, String defaultAction) {
@ -112,11 +115,15 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
return request.getParameter("pkg"); return request.getParameter("pkg");
} }
private String getPlatform(HttpServletRequest request) {
return request.getParameter("platform");
}
private String getAppToken(HttpServletRequest request) { private String getAppToken(HttpServletRequest request) {
return request.getParameter("appToken"); return request.getParameter("appToken");
} }
private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String oauth2ClientId, String redirectUriAction, String appPackage, String appToken) { private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String oauth2ClientId, String redirectUriAction, String appPackage, String platform, String appToken) {
if (oauth2ClientId == null) { if (oauth2ClientId == null) {
return null; return null;
} }
@ -132,11 +139,18 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
if (StringUtils.isEmpty(appToken)) { if (StringUtils.isEmpty(appToken)) {
throw new IllegalArgumentException("Invalid application token."); throw new IllegalArgumentException("Invalid application token.");
} else { } else {
String appSecret = this.oAuth2ClientService.findAppSecret(new OAuth2ClientId(UUID.fromString(oauth2ClientId)), appPackage); String callbackUrlScheme;
if (StringUtils.isEmpty(appSecret)) { if (platform != null) {
throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package."); callbackUrlScheme = validateMobileAppToken(oauth2ClientId, appPackage, PlatformType.valueOf(platform), appToken);
} else {
// for backward compatibility with mobile apps of version 1.3.0 and less try to validate token with android and then ios app secret
try {
callbackUrlScheme = validateMobileAppToken(oauth2ClientId, appPackage, PlatformType.ANDROID, appToken);
} catch (IllegalArgumentException e) {
log.warn("Failed attempt to validate android application token", e);
callbackUrlScheme = validateMobileAppToken(oauth2ClientId, appPackage, PlatformType.IOS, appToken);
}
} }
String callbackUrlScheme = this.oAuth2AppTokenFactory.validateTokenAndGetCallbackUrlScheme(appPackage, appToken, appSecret);
attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme); attributes.put(TbOAuth2ParameterNames.CALLBACK_URL_SCHEME, callbackUrlScheme);
} }
} }
@ -174,6 +188,14 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
.build(); .build();
} }
private String validateMobileAppToken(String oauth2ClientId, String appPackage, PlatformType platformType, String appToken) {
String appSecret = this.oAuth2ClientService.findAppSecret(new OAuth2ClientId(UUID.fromString(oauth2ClientId)), appPackage, platformType);
if (StringUtils.isEmpty(appSecret)) {
throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package.");
}
return this.oAuth2AppTokenFactory.validateTokenAndGetCallbackUrlScheme(appPackage, appToken, appSecret);
}
private String resolveRegistrationId(HttpServletRequest request) { private String resolveRegistrationId(HttpServletRequest request) {
if (this.authorizationRequestMatcher.matches(request)) { if (this.authorizationRequestMatcher.matches(request)) {
return this.authorizationRequestMatcher return this.authorizationRequestMatcher

View File

@ -40,7 +40,7 @@ public interface OAuth2ClientService extends EntityDaoService {
OAuth2Client findOAuth2ClientById(TenantId tenantId, OAuth2ClientId providerId); OAuth2Client findOAuth2ClientById(TenantId tenantId, OAuth2ClientId providerId);
String findAppSecret(OAuth2ClientId oAuth2ClientId, String pkgName); String findAppSecret(OAuth2ClientId oAuth2ClientId, String pkgName, PlatformType platformType);
void deleteOAuth2ClientById(TenantId tenantId, OAuth2ClientId oAuth2ClientId); void deleteOAuth2ClientById(TenantId tenantId, OAuth2ClientId oAuth2ClientId);

View File

@ -38,7 +38,7 @@ public interface OAuth2ClientDao extends Dao<OAuth2Client> {
List<OAuth2Client> findByMobileAppBundleId(UUID mobileAppBundleId); List<OAuth2Client> findByMobileAppBundleId(UUID mobileAppBundleId);
String findAppSecret(UUID id, String pkgName); String findAppSecret(UUID id, String pkgName, PlatformType platformType);
void deleteByTenantId(UUID tenantId); void deleteByTenantId(UUID tenantId);

View File

@ -34,6 +34,7 @@ import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent; import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.DataValidator;
import org.thingsboard.server.dao.service.Validator;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
@ -45,6 +46,8 @@ import java.util.stream.Collectors;
@Service("OAuth2ClientService") @Service("OAuth2ClientService")
public class OAuth2ClientServiceImpl extends AbstractEntityService implements OAuth2ClientService { public class OAuth2ClientServiceImpl extends AbstractEntityService implements OAuth2ClientService {
private static final String PLATFORM_TYPE_IS_REQUIRED = "Platform type is required if package name is specified";
@Autowired @Autowired
private OAuth2ClientDao oauth2ClientDao; private OAuth2ClientDao oauth2ClientDao;
@Autowired @Autowired
@ -90,9 +93,10 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA
} }
@Override @Override
public String findAppSecret(OAuth2ClientId oAuth2ClientId, String pkgName) { public String findAppSecret(OAuth2ClientId oAuth2ClientId, String pkgName, PlatformType platformType) {
log.trace("Executing findAppSecret oAuth2ClientId = [{}] pkgName = [{}]", oAuth2ClientId, pkgName); log.trace("Executing findAppSecret oAuth2ClientId = [{}] pkgName = [{}], platform [{}]", oAuth2ClientId, pkgName, platformType);
return oauth2ClientDao.findAppSecret(oAuth2ClientId.getId(), pkgName); Validator.checkNotNull(platformType, PLATFORM_TYPE_IS_REQUIRED);
return oauth2ClientDao.findAppSecret(oAuth2ClientId.getId(), pkgName, platformType);
} }
@Override @Override

View File

@ -80,8 +80,8 @@ public class JpaOAuth2ClientDao extends JpaAbstractDao<OAuth2ClientEntity, OAuth
} }
@Override @Override
public String findAppSecret(UUID id, String pkgName) { public String findAppSecret(UUID id, String pkgName, PlatformType platformType) {
return repository.findAppSecret(id, pkgName); return repository.findAppSecret(id, pkgName, platformType != null ? platformType.name() : null);
} }
@Override @Override

View File

@ -72,9 +72,10 @@ public interface OAuth2ClientRepository extends JpaRepository<OAuth2ClientEntity
"LEFT JOIN MobileAppBundleOauth2ClientEntity bc ON bc.mobileAppBundleId = b.id " + "LEFT JOIN MobileAppBundleOauth2ClientEntity bc ON bc.mobileAppBundleId = b.id " +
"LEFT JOIN OAuth2ClientEntity c ON bc.oauth2ClientId = c.id " + "LEFT JOIN OAuth2ClientEntity c ON bc.oauth2ClientId = c.id " +
"WHERE c.id = :clientId " + "WHERE c.id = :clientId " +
"AND a.pkgName = :pkgName") "AND a.pkgName = :pkgName and a.platformType = :platformType")
String findAppSecret(@Param("clientId") UUID id, String findAppSecret(@Param("clientId") UUID id,
@Param("pkgName") String pkgName); @Param("pkgName") String pkgName,
@Param("platformType") String platformType);
@Transactional @Transactional
@Modifying @Modifying