实现远程oauth服务器,并在服务器上不保存令牌的情况下与客户端交换令牌



我正在开发一个小型Java应用程序来访问yt实时流的聊天。除了twitchyt没有irc服务器来访问聊天,所以我不得不使用yt API。基本上我只打算自己使用它,但我可能会把它提供给一些朋友,甚至可能公开。

我已经成功地获得了对API的访问权限,但前提是我的系统中存储了客户端机密和令牌。当我想公开它时,我必须设置一个身份验证服务器,然后将令牌传输到客户端。主要问题是实际创建令牌所需的方法com.google.api.client.auth.oauth2.AuthorizationCodeFlow.createAndStoreCredential(TokenResponse, String)。这将把令牌存储在代码运行的地方——所以在计划的环境中是在服务器上,而不是在客户端上。

我还发现了这一点:OAuth服务器,存储用户令牌——所以如果我正确理解这一点,每当服务使用谷歌OAuth时,它都会将访问令牌存储在自己的存储中,然后有人与客户端交互。所以,我有两个问题:

  1. 如果我想向公众开放一个项目,以便其他人使用,我该如何实现这样的身份验证服务
  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,这样攻击者就很难拦截令牌,但风险仍然存在。因此,我正在寻找一种方法来创建令牌,而不将其存储在服务器上(即使是短时间内),以防止可能的攻击向量。你们知道怎么做吗?

//更新因此,我对它进行了修改,看看如何在客户端上创建令牌——这仍然需要在身份验证代码流中使用我的客户端id和客户端机密(否则会出现一个异常,告诉你设置这些)。这最终出现在以下几行:

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");

好吧,据我所知,在不暴露API秘密的情况下,甚至不可能将Credential对象传输到远程客户端,而这个秘密必须保密。这意味着任何调用都必须通过服务器,服务器必须在几个会话/连接上以某种方式唯一地识别客户端,并且每个回复都必须转发回客户端。

所以,这让我想到了一个问题:我想做的事情有可能吗?*=用java编写一个程序,访问我的实时流的聊天-它将其令牌存储在本地,但请求外部服务器获取

我还尝试使用API密钥,但我想做的似乎没有足够的权限,即使是对我给定的用户名进行简单的通道id查找也失败了。。。所以我必须使用OAuth

//更新2好吧,这似乎无关紧要——我在5分钟内达到了每天10k的正常免费用户帐户的最大限制——我想我必须切换到twitch。。。

因此,我找到了一个解决方案:https://github.com/cryptearth/YouTubeLiveChat/commit/b1ce15400688b6907600b006463ce538132bd807归根结底,有两件事我直到现在都不明白:

  1. Credential.Builder只需将"null"作为客户端机密即可正常工作
  2. AuthorizationCodeFlow不需要DataStoreFactory

因此,当不为AuthorizationCodeFlow设置DataStoreFactory时,凭证会被创建,但不会存储在任何地方(在源中,有一个简单的if(null)来检查是否设置了DataStoreFactory)。客户端id也并不重要,因为没有其他线程访问新创建的凭据的风险。由于AccessToken的有效性有限(大约一个小时),我没有测试当客户端运行时间超过这个时间时会发生什么,但由于文档提示有内部检查,所以我想它会尝试刷新自己,但会失败。或者,如果刷新没有发生,下一个呼叫将失败,只需谷歌回复401-未经授权。因此,通过禁用自动刷新机制,我想我必须自己检查一下,并在正确的时间在服务器上进行刷新。为了刷新,我只将刷新令牌发送到服务器,然后用新的访问令牌和它的新生存期进行响应。因此,服务器不必存储任何内容,客户端只需存储刷新令牌-完成!重新实现自动刷新的东西可能会在有效性用完后滥用401回复引起的异常,这是可行的,但被认为是糟糕的代码风格,必须弄清楚如何编写它。

关于在5分钟内达到10公里的限制:我把暂停时间设置为10秒,在一次测试中给了我大约2小时30米的时间,这仍然不足以满足我自己的日常流媒体,如果我愿意分享,我必须进一步延长暂停时间。因此,如果10秒让我获得2h30m,我需要100秒的轮询超时才能获得大约24小时——一次使用(!)——即1m40s——乘以用户数量。我想我必须制作一个合适的项目页面,并以某种方式增加我的最大配额。。。但这是另一天的故事。

问题已解决-项目开发暂停。