SurveyMonkey APIと対話するローカルJavaベースのクライアントを作成しようとしています。
SurveyMonkeyには、OAuth 2.0を使用した長寿命のアクセストークンが必要です。
私はこれを何時間もグーグルで調べてきましたが、答えはノーだと思いますが、ただ確認したいだけです:
SurveyMonkeyと対話する単純なJavaクライアント、クラウドに独自のリダイレクトサーバーを設定せずに?
OAuth 2.0。によって生成されたベアラートークンを受信できるようにするために、独自のオンラインサービスが必須であると感じています。
そして、自分のカスタムサーブレットをどこかに設定し、redirect_uriとして使用する場合、正しいフローは次のようになります。
これは正しいです?
正確ではありませんが、OAuth=フローの全体のポイントは、ユーザー(データに代わってデータにアクセスしているクライアント)がデータへのアクセス許可を与える必要があるということです。
認証手順 を参照してください。ユーザーをOAuth承認ページに送信する必要があります。
https://api.surveymonkey.net/oauth/authorize?api_key<your_key>&client_id=<your_client_id>&response_type=code&redirect_uri=<your_redirect_uri>
これにより、アクセスをリクエストしているアカウントの部分を示すページがユーザーに表示されます(例:アンケートの表示、回答の表示など)。ユーザーがそのページで[認証]をクリックしてそれを承認すると、SurveyMonkeyはコードでリダイレクトURIとして設定したものに自動的に移動します(上記のURLがアプリの設定で設定したものと一致することを確認してください) 。
したがって、リダイレクトURLがhttps://example.com/surveymonkey/oauth
、SurveyMonkeyはコードを使用してユーザーをそのURLにリダイレクトします。
https://example.com/surveymonkey/oauth?code=<auth_code>
そのコードを取得してから、POST request to https://api.surveymonkey.net/oauth/token?api_key=<your_api_key>
次の投稿パラメーターを使用:
client_secret=<your_secret>
code=<auth_code_you_just_got>
redirect_uri=<same_redirect_uri_as_before>
grant_type=authorization_code
これによりアクセストークンが返され、そのアクセストークンを使用してユーザーのアカウントのデータにアクセスできます。ユーザーのアカウントにアクセスするために使用するユーザーにアクセストークンを与えないでください。ポーリングなどは必要ありません。
自分のアカウントにアクセスするだけの場合は、アプリの設定ページにあるアクセストークンを使用できます。そうしないと、独自のリダイレクトサーバーを設定せずにユーザーのアクセストークンを取得する方法はありません(すべてのユーザーがあなたと同じグループ、つまり、同じアカウントの複数のユーザーである場合を除きますが、私はそれに入りません)。 SurveyMonkeyには、ユーザーが承認したコードを送信する場所が必要です。コードを要求することはできません。
はい、コールバックURLなしでOAuth2を使用できます。 RFC6749 は、いくつかのフローを導入します。 Implicit および Authorization Code 付与タイプにはリダイレクトURIが必要です。ただし、 リソース所有者のパスワード資格情報 付与タイプはありません。
RFC6749以降、リダイレクトURIを必要としない他の仕様が発行されています。
制限されたデバイスに別の付与タイプを導入しようとする別のIETFドラフトがあります( https://tools.ietf.org/html/draft-ietf-oauth-device-flow )リダイレクトURI。
いずれにせよ、上記の付与タイプがニーズに合わない場合、 カスタム付与タイプ の作成を妨げるものは何もありません。
doは、redirect_uriとして機能するものを実装する必要があります。これは必ずしもクライアント以外の場所でホストする必要はありません(あなたが言うように、いくつかのクラウド)。
私はJavaおよびServeletsにはあまり馴染みがありませんが、正しく仮定すれば http:// localhost:some_port を処理できるものになります。その場合、説明するフローは正しいです。
同じフローをC#で正常に実装しました。そのフローを実装するクラスを次に示します。役に立てば幸いです。
class OAuth2Negotiator
{
private HttpListener _listener = null;
private string _accessToken = null;
private string _errorResult = null;
private string _apiKey = null;
private string _clientSecret = null;
private string _redirectUri = null;
public OAuth2Negotiator(string apiKey, string address, string clientSecret)
{
_apiKey = apiKey;
_redirectUri = address.TrimEnd('/');
_clientSecret = clientSecret;
_listener = new HttpListener();
_listener.Prefixes.Add(address + "/");
_listener.AuthenticationSchemes = AuthenticationSchemes.Anonymous;
}
public string GetToken()
{
var url = string.Format(@"https://api.surveymonkey.net/oauth/authorize?redirect_uri={0}&client_id=sm_sunsoftdemo&response_type=code&api_key=svtx8maxmjmqavpavdd5sg5p",
HttpUtility.UrlEncode(@"http://localhost:60403"));
System.Diagnostics.Process.Start(url);
_listener.Start();
AsyncContext.Run(() => ListenLoop(_listener));
_listener.Stop();
if (!string.IsNullOrEmpty(_errorResult))
throw new Exception(_errorResult);
return _accessToken;
}
private async void ListenLoop(HttpListener listener)
{
while (true)
{
var context = await listener.GetContextAsync();
var query = context.Request.QueryString;
if (context.Request.Url.ToString().EndsWith("favicon.ico"))
{
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
context.Response.Close();
}
else if (query != null && query.Count > 0)
{
if (!string.IsNullOrEmpty(query["code"]))
{
_accessToken = await SendCodeAsync(query["code"]);
break;
}
else if (!string.IsNullOrEmpty(query["error"]))
{
_errorResult = string.Format("{0}: {1}", query["error"], query["error_description"]);
break;
}
}
}
}
private async Task<string> SendCodeAsync(string code)
{
var GrantType = "authorization_code";
//client_secret, code, redirect_uri and grant_type. The grant type must be set to “authorization_code”
var client = new HttpClient();
client.BaseAddress = new Uri("https://api.surveymonkey.net");
var request = new HttpRequestMessage(HttpMethod.Post, string.Format("/oauth/token?api_key={0}", _apiKey));
var formData = new List<KeyValuePair<string, string>>();
formData.Add(new KeyValuePair<string, string>("client_secret", _clientSecret));
formData.Add(new KeyValuePair<string, string>("code", code));
formData.Add(new KeyValuePair<string, string>("redirect_uri", _redirectUri));
formData.Add(new KeyValuePair<string, string>("grant_type", GrantType));
formData.Add(new KeyValuePair<string, string>("client_id", "sm_sunsoftdemo"));
request.Content = new FormUrlEncodedContent(formData);
var response = await client.SendAsync(request);
if (!response.IsSuccessStatusCode)
{
_errorResult = string.Format("Status {0}: {1}", response.StatusCode.ToString(), response.ReasonPhrase.ToString());
return null;
}
var data = await response.Content.ReadAsStringAsync();
if (data == null)
return null;
Dictionary<string, string> tokenInfo = JsonConvert.DeserializeObject<Dictionary<string, string>>(data);
return(tokenInfo["access_token"]);
}
}