Vários tokens de acesso Oauth2

13

Eu tenho uma API que usa oAuth2 e meus próprios aplicativos móveis que usam essa API como back-end. Como os usuários podem fazer logon por vários dispositivos (por exemplo, iPhone, iPad, tablet Android ou telefone Android) ao mesmo tempo, preciso da API para distinguir entre cada conexão. Eu gostaria de fazer isso por meio de tokens de acesso separados: cada cliente recebe um token de acesso separado.

O problema é que a implementação atual que usamos (spring-security-oauth2) gera uma chave exclusiva com base em client_id, nome de usuário e escopo. Então, basicamente, ao obter um token de acesso, todos os clientes obtêm o mesmo token de acesso para o mesmo usuário. Isso é feito usando DefaultAuthenticationKeyGenerator.

É seguro ignorar o gerador de chaves de autenticação e simplesmente criar um novo token de acesso em cada solicitação de um cliente?

lista de controle
fonte
2
você pode usar o escopo para diferenciar cada cliente? ou seja, forneça ao ios um escopo "ios", android um escopo "android", o tablet um escopo "tablet", etc. sempre gera um novo token.
Rob
No entanto, em geral, a implementação do Spring Security OAuth2 funcionou bem para mim (depois de concluir a configuração XML), mas gerenciar os objetos de token e autenticação era um problema constante.
Rob
2
A pesquisa no Google por "DefaultAuthenticationKeyGenerator" levou-me a um arquivo .java na biblioteca spring-security-oauth no GitHub. Essa classe implementa a AuthenticationKeyGeneratorinterface. Você poderia criar sua própria implementação e usá-la?
Greg Burghardt
O URL para o arquivo .java eu encontrei: github.com/spring-projects/spring-security-oauth/blob/master/...
Greg Burghardt
2
Estou de acordo com @ Rob você pode ir com devicetype no pedido como "Android", "iOS", "web" etc
Vikash rajpurohit

Respostas:

1

O Spring Cloud já fornece esse comportamento. Basta adicionar clientes diferentes. Como o iosAppClient, androidAppClient na sua classe 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")
                    ........

        }

No back-end, você pode obter o ID do cliente como o seguinte

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

e implemente um comportamento diferente com base no clientId.

Rocks360
fonte
0

Uma resposta é que cada plataforma de aplicativo é um cliente diferente, portanto, deve ter um ID de cliente diferente. Um para o aplicativo iOS, outro para o site, etc.

Quanto à diferenciação entre, digamos, um iPad e um iPhone, eu sugeriria não confiar no sistema OAuth para isso.

RibaldEddie
fonte
0

Eu me deparei com o mesmo problema ao desenvolver meu back-end com Spring Boot e OAuth2. O problema que encontrei foi que, se vários dispositivos compartilhavam os mesmos tokens, uma vez que um dispositivo atualizasse o token, o outro dispositivo ficaria sem noção e, resumindo, os dois dispositivos entrariam em um frenesi de atualização de token. Minha solução foi substituir o padrão AuthenticationKeyGeneratorpor uma implementação personalizada que substitui DefaultAuthenticationKeyGeneratore adiciona um novo parâmetro client_instance_idna mistura do gerador de chaves. Meus clientes móveis enviaram esse parâmetro, que deve ser único nas instalações de aplicativos (iOS ou Android). Este não é um requisito especial, pois a maioria dos aplicativos móveis já rastreia a instância do aplicativo de alguma 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 você injetaria de maneira semelhante:

final JdbcTokenStore tokenStore = new JdbcTokenStore(mDataSource);
tokenStore.setAuthenticationKeyGenerator(new EnhancedAuthenticationKeyGenerator());

A solicitação HTTP ficaria assim:

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}}

O benefício de usar essa abordagem é que, se o cliente não enviar uma client_instance_id , a chave padrão será gerada e, se uma instância for fornecida, a mesma chave será retornada sempre para a mesma instância. Além disso, a chave é independente da plataforma. A desvantagem seria que o resumo MD5 (usado internamente) é chamado duas vezes.

Cosmin Radu
fonte