реализовать удаленный oauth-сервер и обменять токен с клиентом без сохранения токена на сервере - PullRequest
0 голосов
/ 10 февраля 2020

// update - см. Ниже

Итак, в настоящее время я разрабатываю небольшое приложение Java для доступа к чату моих прямых трансляций на yt. Кроме twitch у yt нет сервера ir c для доступа к чату, поэтому я вынужден использовать yt api. По сути, я планирую использовать его только для себя, но, возможно, я предложу его для некоторых друзей или даже сделаю его общедоступным c. Я уже успешно получил доступ к API, но только если у меня есть секрет клиента и токен, хранящийся в моей системе. Когда я хочу открыть его publi c, я должен настроить сервер авторизации, а затем передать токен клиенту. Основной проблемой является метод com.google.api.client.auth.oauth2.AuthorizationCodeFlow.createAndStoreCredential (TokenResponse, String), который необходим для фактического создания токена. Это будет хранить токен там, где когда-либо выполняется код - так в плановой среде на сервере, а не на клиенте. Я также нашел это: OAuth-сервер, хранящий пользовательские токены - поэтому, если я правильно понимаю, когда служба использует google oauth, она сохраняет токен доступа в своем собственном хранилище, а затем кто-то взаимодействует с клиентом. Итак, у меня есть два вопроса: 1) Как реализовать такой сервис аутентификации, если я хочу открыть проект для публикации c, чтобы он мог использоваться другими? 2) Как я могу отправить токен клиенту? Или все коммуникации должны осуществляться через мой сервер? 2a) Если ответ на предыдущий вопрос положительный, как мне обеспечить доступ, чтобы каждый клиент мог получить доступ к своему собственному токену, только если он идентифицирован только простой строкой?

В настоящее время это код для получения токена. :

public class Main
{
    public final static void main(final String... args)
    {
        (new Main()).start();
    }
    private Main() {}
    private void start()
    {
        try
        {
            File DATA_STORE_DIR=new File(System.getProperty("user.home"), "yta");
            JsonFactory JSON_FACTORY=JacksonFactory.getDefaultInstance();
            List<String> SCOPES=Arrays.asList(YouTubeScopes.YOUTUBE_READONLY, YouTubeScopes.YOUTUBE, YouTubeScopes.YOUTUBE_FORCE_SSL, YouTubeScopes.YOUTUBE_UPLOAD);
            DataStoreFactory DATA_STORE_FACTORY=new FileDataStoreFactory(DATA_STORE_DIR);
            NetHttpTransport transport=GoogleNetHttpTransport.newTrustedTransport();
            GoogleClientSecrets clientSecrets=GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(new FileInputStream(new File(DATA_STORE_DIR, "client_secret.json"))));
            GoogleAuthorizationCodeFlow googleAuthorizationCodeFlow=new GoogleAuthorizationCodeFlow.Builder(transport, JSON_FACTORY, clientSecrets, SCOPES).setDataStoreFactory(DATA_STORE_FACTORY).build();
            String authURL=googleAuthorizationCodeFlow.newAuthorizationUrl().setRedirectUri("urn:ietf:wg:oauth:2.0:oob").build();
            Desktop.getDesktop().browse(new URI(authURL));
            String authToken=System.console().readLine();
            GoogleTokenResponse googleTokenResponse=googleAuthorizationCodeFlow.newTokenRequest(authToken).setRedirectUri("urn:ietf:wg:oauth:2.0:oob").execute();
            Credential credential=googleAuthorizationCodeFlow.createAndStoreCredential(googleTokenResponse, "userId");
        }
        catch(IOException|GeneralSecurityException|URISyntaxException ex)
        {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }

    }
}

, который заканчивается в файле, "StoredCredential" создается в каталоге DATA_STORE_DIR, который содержит токен аутентификации, идентифицированный как "userId". Чтобы передать токен клиенту, я могу просто вызвать getRefreshToken и getAccessToken, отправить их клиенту, сохранить их на клиенте, создать новый экземпляр Credential из этих двух строк и «активировать» его, обновив его. Чтобы избавиться от токена на стороне сервера, я мог бы просто вызвать delete в хранилище данных - и для повышения безопасности я мог бы использовать UUID для этого одного потока, так что злоумышленнику было бы трудно перехватить токен - но существует риск все еще там. Поэтому я ищу способ создания токена без его сохранения на сервере (даже на короткое время), чтобы предотвратить возможный вектор атаки. Кто-нибудь из вас знает, как это сделать?

// update Итак, я возился с ним, чтобы посмотреть, как создать токен на клиенте - для которого по-прежнему требуются мой идентификатор клиента и секрет клиента, используемые в поток кода аутентификации (в противном случае вы получите исключение, сообщающее вам об этом). Это закончилось следующими строками:

credential=(new Credential.Builder(BearerToken.authorizationHeaderAccessMethod()))
    .setTransport(transport)
    .setJsonFactory(JSON_FACTORY)
    .setTokenServerEncodedUrl("https://oauth2.googleapis.com/token")
    .setClientAuthentication(new ClientParametersAuthentication("client-id", "client-secret"))
    .build()
.setAccessToken("access-token")
.setRefreshToken("refresh-token");

Ну, насколько я могу судить, невозможно даже передать объект Credential удаленному клиенту без раскрытия секрета API, который необходимо сохранить. секрет. Это будет означать, что любой вызов должен проходить через сервер, который должен каким-то образом однозначно идентифицировать клиента по нескольким сеансам / соединениям, и что каждый ответ должен быть перенаправлен обратно клиенту.

Итак - это приводит меня к вопросу: возможно ли то, что я хочу сделать *? * = Напишите программу в java, которая имеет доступ к чату моего живого потока - который хранит свой токен локально, но запрашивает внешний сервер для его получения

Я также пытался использовать ключ API - но, похоже, для что я хочу сделать, это не имеет достаточных разрешений - даже простой поиск идентификатора канала для моего заданного имени пользователя не удается ... поэтому я должен использовать OAuth

// update 2 Хорошо - кажется, не имеет значения - Я установил максимальный лимит обычной бесплатной учетной записи пользователя в размере 10 КБ в день в течение 5 минут - думаю, мне нужно переключиться на подергивание ...

1 Ответ

0 голосов
/ 16 февраля 2020

Итак, я нашел решение: https://github.com/cryptearth/YouTubeLiveChat/commit/b1ce15400688b6907600b006463ce538132bd807 Это сводится к двум вещам, которые я до сих пор не понимал:

  1. Credential.Builder отлично работает только с укажите "null" в качестве секрета клиента.
  2. AuthorizationCodeFlow не нуждается в DataStoreFactory.

Таким образом, когда не устанавливается DataStoreFactory для AuthorizationCodeFlow, создается учетная запись, но просто нигде не хранится (в источнике есть простой if (null), чтобы проверить, был ли установлен DataStoreFactory). Этот идентификатор клиента также не имеет значения, поскольку нет риска, что другой поток сможет получить доступ к вновь созданным учетным данным. Так как AccessTokens имеет ограниченный срок действия (около часа), я не проверял, что происходит, когда клиент работает дольше, чем этот, но, как подсказывает do c, есть внутренние проверки, поэтому я думаю, что он попытается обновить sh сам - который потерпит неудачу. Или, если refre sh не произошло, следующий вызов не удастся просто с 401 - Несанкционированный ответ от Google. Таким образом, отключив механизм авторефрама sh, я полагаю, мне нужно как-то самому проверить его и позволить рефреру sh произойти на сервере в нужное время. Для refre sh я просто отправил на сервер токен refre sh, который затем ответил бы новым токеном доступа и новым временем его существования. Итак, серверу не нужно ничего хранить, а клиенту только токен refre sh - DONE! Реализовать функцию auto-refre sh можно было бы сделать грязно, если использовать исключение, вызванное ответом 401, после истечения срока действия - будет работать, но считается плохим стилем кода - придется выяснить, как написать это несколько не плохо.

О достижении предела 10k за 5 минут: я установил тайм-аут на 10se c - дал мне около 2х30 м на тесте - все еще недостаточно для моих собственных ежедневных потоков - и если я поделюсь им, я пришлось удлинить тайм-аут еще дальше. Итак, если 10se c принесет мне 2h30m, мне понадобится время ожидания опроса 100se c, чтобы получить около 24 часов - для одного использования (!) - это 1m40s - умноженное на количество пользователей. Думаю, мне нужно было бы создать правильную страницу проекта и каким-то образом увеличить максимальную квоту ... но это история для другого дня.

Вопрос решен - проект dev приостановлен.

...