web-dev-qa-db-fra.com

Est-il possible d'utiliser OAuth 2.0 sans serveur de redirection?

J'essaie de créer un client Java local qui interagit avec l'API SurveyMonkey.

SurveyMonkey nécessite un jeton d'accès longue durée en utilisant OAuth 2.0, que je ne connais pas très bien.

Cela fait des heures que je recherche sur Google et je pense que la réponse est non, mais je veux juste être sûr:

Est-il possible pour moi d'écrire un simple Java qui interagit avec SurveyMonkey, sans configurer mon propre serveur de redirection dans un cloud?

J'ai l'impression que mon propre service en ligne est obligatoire pour pouvoir recevoir les jetons de porteur générés par OAuth 2.0. Est-il possible que je ne puisse pas demander à SurveyMonkey d'envoyer des jetons de porteur directement à mon client?

Et si je devais configurer mon propre servlet personnalisé quelque part et l'utiliser comme redirect_uri, le flux correct serait le suivant:

  1. Jeton de support de requête client Java de SurveyMonkey, avec redirect_uri étant ma propre URL de servlet personnalisée.
  2. SurveyMonkey envoie un jeton à mon URL de servlet personnalisée.
  3. Le client Java interroge l'URL du servlet personnalisé jusqu'à ce qu'un jeton soit disponible?

Est-ce correct?

22
Tovi7

Pas exactement, l'intérêt du flux OAuth est que l'utilisateur (le client pour lequel vous accédez aux données) doit vous donner la permission d'accéder à leurs données.

Voir instructions d'authentification . Vous devez envoyer l'utilisateur à la page d'autorisation OAuth:

https://api.surveymonkey.net/oauth/authorize?api_key<your_key>&client_id=<your_client_id>&response_type=code&redirect_uri=<your_redirect_uri>

Cela affichera une page à l'utilisateur lui indiquant à quelles parties de son compte vous demandez l'accès (par exemple, voir ses enquêtes, voir ses réponses, etc.). Une fois que l'utilisateur approuve cela en cliquant sur "Autoriser" sur cette page, SurveyMonkey ira automatiquement à ce que vous définissez comme votre URI de redirection (assurez-vous que celui de l'URL ci-dessus correspond à ce que vous avez défini dans les paramètres de votre application) avec le code .

Donc, si votre URL de redirection était https://example.com/surveymonkey/oauth, SurveyMonkey redirigera l'utilisateur vers cette URL avec un code:

https://example.com/surveymonkey/oauth?code=<auth_code>

Vous devez prendre ce code, puis l'échanger contre un jeton d'accès en effectuant une demande POST à https://api.surveymonkey.net/oauth/token?api_key=<your_api_key> avec les paramètres de publication suivants:

client_secret=<your_secret>
code=<auth_code_you_just_got>
redirect_uri=<same_redirect_uri_as_before>
grant_type=authorization_code

Cela retournera un jeton d'accès, vous pouvez ensuite utiliser ce jeton d'accès pour accéder aux données sur le compte de l'utilisateur. Vous ne donnez pas le jeton d'accès à l'utilisateur que vous devez utiliser pour accéder au compte de l'utilisateur. Pas besoin de sondage ou quoi que ce soit.

Si vous accédez simplement à votre propre compte, vous pouvez utiliser le jeton d'accès fourni dans la page des paramètres de votre application. Sinon, il n'y a aucun moyen d'obtenir un jeton d'accès pour un utilisateur sans configurer votre propre serveur de redirection (sauf si tous les utilisateurs sont dans le même groupe que vous, c'est-à-dire plusieurs utilisateurs sous le même compte; mais je n'entrerai pas dans le détail). SurveyMonkey a besoin d'un endroit pour vous envoyer le code une fois que l'utilisateur l'autorise, vous ne pouvez pas simplement en demander un.

16
General Kandalaft

Oui, il est possible d'utiliser OAuth2 sans URL de rappel. RFC6749 introduit plusieurs flux. Les types d'octroi implicite et code d'autorisation nécessitent un URI de redirection. Cependant, le type d'octroi Informations d'identification du mot de passe du propriétaire de la ressource ne le fait pas.

Depuis la RFC6749, d'autres spécifications ont été publiées qui ne nécessitent aucun URI de redirection:

  • RFC7522 : Profil SAML (Security Assertion Markup Language) 2.0 pour OAuth 2.0 Subventions d'authentification et d'autorisation client
  • RFC752 : Profil de jeton Web JSON (JWT) pour OAuth 2.0 Authentification client et subventions d'autorisation)

Il existe un autre projet IETF qui essaie d'introduire un autre type de subvention pour les appareils limités ( https://tools.ietf.org/html/draft-ietf-oauth-device-flow ) qui ne nécessite aucun rediriger l'URI.

Dans tous les cas, si les types de subventions ci-dessus ne correspondent pas à vos besoins, rien ne vous empêche de créer un type de subvention personnalisé .

8
Florent Morselli

Vous avez besoin d'implémenter quelque chose qui agira comme redirect_uri, qui n'a pas nécessairement besoin d'être hébergé ailleurs que votre client (comme vous le dites, dans quelques nuages).

Je ne suis pas très familier avec Java et Servelets, mais si je suppose correctement, ce serait quelque chose qui pourrait gérer http: // localhost: some_port . Dans ce cas , le flux que vous décrivez est correct.

J'ai implémenté le même flux avec succès en C #. Voici la classe qui implémente ce flux. J'espère que ça aide.

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"]);
    }
}
4
manos