Me encontré con el mismo problema al desarrollar mi backend con Spring Boot y OAuth2. El problema que encontré fue que, si varios dispositivos compartían los mismos tokens, una vez que un dispositivo actualizaba el token, el otro dispositivo no tendría ni idea y, para resumir, ambos dispositivos entraron en un frenesí de actualización de tokens. Mi solución fue reemplazar el valor predeterminado AuthenticationKeyGenerator
con una implementación personalizada que anula DefaultAuthenticationKeyGenerator
y agrega un nuevo parámetro client_instance_id
en la mezcla del generador de claves. Mis clientes móviles enviarían este parámetro, que debe ser único en todas las instalaciones de aplicaciones (iOS o Android). Este no es un requisito especial, ya que la mayoría de las aplicaciones móviles ya rastrean la instancia de la aplicación de alguna forma.
public class EnhancedAuthenticationKeyGenerator extends DefaultAuthenticationKeyGenerator {
public static final String PARAM_CLIENT_INSTANCE_ID = "client_instance_id";
private static final String KEY_SUPER_KEY = "super_key";
private static final String KEY_CLIENT_INSTANCE_ID = PARAM_CLIENT_INSTANCE_ID;
@Override
public String extractKey(final OAuth2Authentication authentication) {
final String superKey = super.extractKey(authentication);
final OAuth2Request authorizationRequest = authentication.getOAuth2Request();
final Map<String, String> requestParameters = authorizationRequest.getRequestParameters();
final String clientInstanceId = requestParameters != null ? requestParameters.get(PARAM_CLIENT_INSTANCE_ID) : null;
if (clientInstanceId == null || clientInstanceId.length() == 0) {
return superKey;
}
final Map<String, String> values = new LinkedHashMap<>(2);
values.put(KEY_SUPER_KEY, superKey);
values.put(KEY_CLIENT_INSTANCE_ID, clientInstanceId);
return generateKey(values);
}
}
que luego inyectarías de manera similar:
final JdbcTokenStore tokenStore = new JdbcTokenStore(mDataSource);
tokenStore.setAuthenticationKeyGenerator(new EnhancedAuthenticationKeyGenerator());
La solicitud HTTP se vería así
POST /oauth/token HTTP/1.1
Host: {{host}}
Authorization: Basic {{auth_client_basic}}
Content-Type: application/x-www-form-urlencoded
grant_type=password&username={{username}}&password={{password}}&client_instance_id={{instance_id}}
El beneficio de usar este enfoque es que, si el cliente no envía un client_instance_id
, se generará la clave predeterminada, y si se proporciona una instancia, se devuelve la misma clave cada vez para la misma instancia. Además, la clave es la plataforma independiente. La desventaja sería que el resumen MD5 (utilizado internamente) se llama dos veces.