Múltiples tokens de acceso Oauth2

13

Tengo una API que usa oAuth2 y mis propias aplicaciones móviles que usan esta API como back-end. Dado que los usuarios pueden iniciar sesión a través de múltiples dispositivos (es decir, iPhone, iPad, tableta Android o teléfono Android) al mismo tiempo, necesito la API para distinguir entre cada conexión. Me gustaría hacer esto a través de tokens de acceso separados: cada cliente obtiene un token de acceso separado.

El problema es que la implementación actual que utilizamos (spring-security-oauth2) genera una clave única basada en client_id, nombre de usuario y alcance. Básicamente, al obtener un token de acceso, todos los clientes obtienen el mismo token de acceso para el mismo usuario. Esto se hace usando DefaultAuthenticationKeyGenerator.

¿Es seguro ignorar el generador de claves de autenticación y simplemente crear un nuevo token de acceso en cada solicitud de un cliente?

Lista de Verificación
fuente
2
¿Puedes usar el alcance para diferenciar a cada cliente? es decir, dar a iOS un alcance "ios", Android un alcance "android", la tableta un alcance "tableta", etc. generó un token nuevo cada vez.
Rob
Sin embargo, en general, la implementación de Spring Security OAuth2 funcionó bien para mí (una vez que pasé por la configuración XML), pero administrar el token y los objetos de autenticación fue un problema continuo.
Rob
2
Buscar en Google "DefaultAuthenticationKeyGenerator" me llevó a un archivo .java en la biblioteca spring-security-oauth en GitHub. Esa clase implementa la AuthenticationKeyGeneratorinterfaz. ¿Podría crear su propia implementación y usarla en su lugar?
Greg Burghardt
La URL del archivo .java que encontré: github.com/spring-projects/spring-security-oauth/blob/master/…
Greg Burghardt
2
Estoy de acuerdo con @Rob, puede solicitar el tipo de dispositivo como "android", "ios", "web", etc.
Vikash Rajpurohit

Respuestas:

1

Spring Cloud ya proporciona este comportamiento. Simplemente agregue diferentes clientes. Al igual que iosAppClient, androidAppClient en su clase AuthorizationServerConfiguration.

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
                clients.inMemory().withClient("androidAppclient")
                    .secret("clientsecret")
                    .autoApprove(true)
                    .accessTokenValiditySeconds(120)
                    .authorizedGrantTypes("password")
                    .resourceIds("accountservice")
                    .scopes("read", "write")
                    .and()
                    .withClient("iosappclient")
                    ........

        }

En el backend puede obtener el ID de cliente como el siguiente

clientId = ((OAuth2Authentication) authentication).getOAuth2Request().getClientId();

e implementar diferentes comportamientos basados ​​en el clientId.

Rocas360
fuente
0

Una respuesta es que cada plataforma de aplicación es un cliente diferente, por lo que debe tener una identificación de cliente diferente. Uno para la aplicación iOS, otro para el sitio web, etc.

En cuanto a diferenciar entre un iPad y un iPhone, sugiero no depender del sistema OAuth para esto.

RibaldEddie
fuente
0

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 AuthenticationKeyGeneratorcon una implementación personalizada que anula DefaultAuthenticationKeyGeneratory agrega un nuevo parámetro client_instance_iden 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.

Cosmin Radu
fuente