web-dev-qa-db-fra.com

Ne pas recevoir le jeton d'actualisation Google OAuth

Je souhaite obtenir le jeton d'accès de Google. L'API de Google indique que, pour obtenir le jeton d'accès, envoyez le code et d'autres paramètres à la page de génération de jetons. La réponse sera un objet JSON du type:

{
"access_token" : "ya29.AHES6ZTtm7SuokEB-RGtbBty9IIlNiP9-eNMMQKtXdMP3sfjL1Fc",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/HKSmLFXzqP0leUihZp2xUt3-5wkU7Gmu2Os_eBnzw74"
}

Cependant, je ne reçois pas le jeton d'actualisation. La réponse dans mon cas est:

{
 "access_token" : "ya29.sddsdsdsdsds_h9v_nF0IR7XcwDK8XFB2EbvtxmgvB-4oZ8oU",
"token_type" : "Bearer",
"expires_in" : 3600
}
227
Muhammad Usman

Le refresh_token est fourni uniquement lors de la première autorisation de l'utilisateur. Les autorisations ultérieures, telles que celles que vous effectuez lors du test d'une intégration OAuth2, ne renverront pas le refresh_token à nouveau. :)

  1. Accédez à la page affichant les applications avec accès à votre compte: https://myaccount.google.com/u/0/permissions .
  2. Dans le menu Applications tierces, choisissez votre application.
  3. Cliquez sur Supprimer l'accès, puis sur Ok pour confirmer.
  4. La prochaine demande OAuth2 que vous ferez renverra un refresh_token.

Vous pouvez également ajouter le paramètre de requête Prompt=consent à la redirection OAuth (voir la page Google OAuth 2.0 pour Web Server Applications ).

Cela invitera l'utilisateur à autoriser l'application à nouveau et renverra toujours un refresh_token.

578
Rich Sutton

Pour obtenir le jeton d'actualisation, vous devez ajouter à la fois approval_Prompt=force et access_type="offline" Si vous utilisez le client Java fourni par Google, il ressemblera à ceci:

GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
            HTTP_TRANSPORT, JSON_FACTORY, getClientSecrets(), scopes)
            .build();

AuthorizationCodeRequestUrl authorizationUrl =
            flow.newAuthorizationUrl().setRedirectUri(callBackUrl)
                    .setApprovalPrompt("force")
                    .setAccessType("offline");
55
Gal Morad

J'ai cherché une longue nuit et c'est ce qui se passe:

User-example.php modifié à partir de admin-sdk

$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$authUrl = $client->createAuthUrl();
echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>";

alors vous obtenez le code à l'URL de redirection .__ et l'authentification avec le code et le jeton d'actualisation

$client()->authenticate($_GET['code']);
echo $client()->getRefreshToken();

Vous devriez le stocker maintenant;)

Lorsque votre clé d'accès expire, faites simplement 

$client->refreshToken($theRefreshTokenYouHadStored);
27
Norbert

Cela m'a causé une certaine confusion et j'ai donc pensé partager ce que je suis venu apprendre à la dure:

Lorsque vous demandez l'accès à l'aide des paramètres access_type=offline et approval_Prompt=force, vous devez recevoir à la fois un jeton access et un jeton refresh. Le jeton access expire peu de temps après sa réception et vous devrez l'actualiser. 

Vous avez correctement demandé à obtenir un nouveau jeton access et reçu la réponse contenant votre nouveau jeton access. Le fait de ne pas recevoir de nouveau jeton refresh me troublait également. Cependant, c’est ce qu’il est censé être puisque vous pouvez utiliser le même jeton refresh encore et encore.

Je pense que certaines des autres réponses supposent que vous vouliez vous procurer un nouveau jeton refresh pour une raison quelconque et suggérons que vous ré-autorisiez l'utilisateur, mais en réalité, vous n'en avez pas besoin depuis le refresh Le jeton que vous avez fonctionnera jusqu'à sa révocation par l'utilisateur. 

14
jeteon

La réponse de Rich Sutton a finalement fonctionné pour moi, après avoir réalisé que l'ajout de access_type=offline est effectué sur la demande du client front end pour un code d'autorisation, pas la requête dorsale qui échange ce code pour un access_token. J'ai ajouté un commentaire à sa réponse et ce lien sur Google pour plus d'informations sur les jetons rafraîchissants.

P.S. Si vous utilisez Satellizer, , voici comment ajouter cette option à $ authProvider.google dans AngularJS .

7
Zack Morris

Si vous définissez cela, le jeton d'actualisation sera envoyé à chaque fois:

$client->setApprovalPrompt('force');

un exemple est donné ci-dessous (php): 

$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("email");
$client->addScope("profile"); 
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
2
apadana

1. Comment obtenir 'refresh_token'?

Solution: L'option access_type = 'offline' doit être utilisée lors de la génération de authURL. source: tilisation de OAuth 2.0 pour les applications serveur Web

2. Mais même avec 'type_accès = hors ligne', je ne reçois pas le 'refresh_token'?

Solution: Veuillez noter que vous ne l'obtiendrez qu'à la première demande. Par conséquent, si vous le stockez quelque part et qu'il existe une disposition pour le remplacer dans votre Code lors de l’obtention d’un nouveau code d’accès après l’expiration de la précédente, assurez-vous de ne pas écraser cette valeur.

De Google Auth Doc: (cette valeur = type_accès)

Cette valeur indique au serveur d'autorisation Google de renvoyer un jeton d'actualisation et un jeton d'accès la première fois que votre application échange un code d'autorisation pour les jetons.

Si vous avez à nouveau besoin de 'refresh_token', vous devez supprimer l'accès à votre application en suivant les étapes décrites dans réponse de Rich Sutton .

2
Mazahir Haroon

Pour obtenir le refresh_token, vous devez inclure access_type=offline dans l'URL de la requête OAuth. Lorsqu'un utilisateur s'authentifie pour la première fois, vous récupérez un refresh_token non nul ainsi qu'un access_token qui expire. 

Si vous vous trouvez dans une situation où un utilisateur peut ré-authentifier un compte pour lequel vous avez déjà un jeton d'authentification (comme le mentionne @SsjCosty ci-dessus), vous devez récupérer les informations de Google sur le compte pour lequel le jeton est destiné. Pour ce faire, ajoutez profile à vos portées. En utilisant la gemme OAuth2 Ruby, votre demande finale pourrait ressembler à ceci:

client = OAuth2::Client.new(
  ENV["GOOGLE_CLIENT_ID"],
  ENV["GOOGLE_CLIENT_SECRET"],
  authorize_url: "https://accounts.google.com/o/oauth2/auth",
  token_url: "https://accounts.google.com/o/oauth2/token"
)

# Configure authorization url
client.authorize_url(
  scope: "https://www.googleapis.com/auth/analytics.readonly profile",
  redirect_uri: callback_url,
  access_type: "offline",
  Prompt: "select_account"
)

Notez que l'étendue comporte deux entrées délimitées par des espaces, l'une pour l'accès en lecture seule à Google Analytics et l'autre pour la variable profile, qui est une norme OpenID Connect.

Ainsi, Google fournira un attribut supplémentaire appelé id_token dans la réponse get_token. Pour obtenir des informations sur id_token, consultez cette page dans les documents Google. Il existe une poignée de bibliothèques fournies par Google qui valideront et «décoderont» ceci pour vous (j'ai utilisé le joyau Ruby google-id-token ). Une fois que vous l'avez analysé, le paramètre sub est effectivement l'identifiant de compte Google unique.

Il est à noter que si vous modifiez la portée, vous récupérerez un jeton d'actualisation à nouveau pour les utilisateurs déjà authentifiés avec la portée d'origine. Ceci est utile si, par exemple, vous avez déjà un groupe d'utilisateurs et que vous ne voulez pas les rendre tous non-autorisés à l'application dans Google.

Oh, et une dernière remarque: vous n'avez pas besoinPrompt=select_account, mais c'est utile si vous souhaitez que vos utilisateurs puissent s'authentifier avec plusieurs comptes Google pour la connexion/authentification).

1
coreyward

Pour moi, j'essayais CalendarSampleServlet fourni par Google. Après 1 heure, la clé access_key expire et une redirection vers une page 401 est effectuée. J'ai essayé toutes les options ci-dessus mais elles n'ont pas fonctionné. Enfin, après avoir vérifié le code source pour 'AbstractAuthorizationCodeServlet', je pouvais voir que la redirection serait désactivée si des informations d'identification étaient présentes, mais idéalement, elle aurait dû rechercher refresh token!=null. J'ai ajouté le code ci-dessous à CalendarSampleServlet et cela a fonctionné par la suite. Grand soulagement après tant d'heures de frustration. Dieu merci.

if (credential.getRefreshToken() == null) {
    AuthorizationCodeRequestUrl authorizationUrl = authFlow.newAuthorizationUrl();
    authorizationUrl.setRedirectUri(getRedirectUri(req));
    onAuthorization(req, resp, authorizationUrl);
    credential = null;
}
1
Anoop Isaac
    #!/usr/bin/env Perl

    use strict;
    use warnings;
    use 5.010_000;
    use utf8;
    binmode STDOUT, ":encoding(utf8)";

    use Text::CSV_XS;
    use FindBin;
    use lib $FindBin::Bin . '/../lib';
    use Net::Google::Spreadsheets::V4;

    use Net::Google::DataAPI::Auth::OAuth2;

    use lib 'lib';
    use Term::Prompt;
    use Net::Google::DataAPI::Auth::OAuth2;
    use Net::Google::Spreadsheets;
    use Data::Printer ;


    my $oauth2 = Net::Google::DataAPI::Auth::OAuth2->new(
         client_id => $ENV{CLIENT_ID},
         client_secret => $ENV{CLIENT_SECRET},
         scope => ['https://www.googleapis.com/auth/spreadsheets'],
    );
    my $url = $oauth2->authorize_url();
    # system("open '$url'");
    print "go to the following url with your browser \n" ;
    print "$url\n" ;
    my $code = Prompt('x', 'paste code: ', '', '');
    my $objToken = $oauth2->get_access_token($code);

    my $refresh_token = $objToken->refresh_token() ;

    print "my refresh token is : \n" ;
    # debug p($refresh_token ) ;
    p ( $objToken ) ;


    my $gs = Net::Google::Spreadsheets::V4->new(
            client_id      => $ENV{CLIENT_ID}
         , client_secret  => $ENV{CLIENT_SECRET}
         , refresh_token  => $refresh_token
         , spreadsheet_id => '1hGNULaWpYwtnMDDPPkZT73zLGDUgv5blwJtK7hAiVIU'
    );

    my($content, $res);

    my $title = 'My foobar sheet';

    my $sheet = $gs->get_sheet(title => $title);

    # create a sheet if does not exit
    unless ($sheet) {
         ($content, $res) = $gs->request(
              POST => ':batchUpdate',
              {
                    requests => [
                         {
                              addSheet => {
                                    properties => {
                                         title => $title,
                                         index => 0,
                                    },
                              },
                         },
                    ],
              },
         );

         $sheet = $content->{replies}[0]{addSheet};
    }

    my $sheet_prop = $sheet->{properties};

    # clear all cells
    $gs->clear_sheet(sheet_id => $sheet_prop->{sheetId});

    # import data
    my @requests = ();
    my $idx = 0;

    my @rows = (
         [qw(name age favorite)], # header
         [qw(tarou 31 curry)],
         [qw(jirou 18 gyoza)],
         [qw(saburou 27 ramen)],
    );

    for my $row (@rows) {
         Push @requests, {
              pasteData => {
                    coordinate => {
                         sheetId     => $sheet_prop->{sheetId},
                         rowIndex    => $idx++,
                         columnIndex => 0,
                    },
                    data => $gs->to_csv(@$row),
                    type => 'PASTE_NORMAL',
                    delimiter => ',',
              },
         };
    }

    # format a header row
    Push @requests, {
         repeatCell => {
              range => {
                    sheetId       => $sheet_prop->{sheetId},
                    startRowIndex => 0,
                    endRowIndex   => 1,
              },
              cell => {
                    userEnteredFormat => {
                         backgroundColor => {
                              red   => 0.0,
                              green => 0.0,
                              blue  => 0.0,
                         },
                         horizontalAlignment => 'CENTER',
                         textFormat => {
                              foregroundColor => {
                                    red   => 1.0,
                                    green => 1.0,
                                    blue  => 1.0
                              },
                              bold => \1,
                         },
                    },
              },
              fields => 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment)',
         },
    };

    ($content, $res) = $gs->request(
         POST => ':batchUpdate',
         {
              requests => \@requests,
         },
    );

    exit;

    #Google Sheets API, v4

    # Scopes
    # https://www.googleapis.com/auth/drive   View and manage the files in your Google D# # i# rive
    # https://www.googleapis.com/auth/drive.file View and manage Google Drive files and folders that you have opened or created with this app
    # https://www.googleapis.com/auth/drive.readonly   View the files in your Google Drive
    # https://www.googleapis.com/auth/spreadsheets  View and manage your spreadsheets in Google Drive
    # https://www.googleapis.com/auth/spreadsheets.readonly  View your Google Spreadsheets
0
Yordan Georgiev

Ma solution était un peu bizarre. J'ai essayé toutes les solutions trouvées sur Internet et rien. De manière surprenante, cela a fonctionné: supprimez le credentials.json, actualisez, appliquez à nouveau votre application dans votre compte. Le nouveau fichier credentials.json aura le jeton d'actualisation. Sauvegardez ce fichier quelque part. Continuez ensuite à utiliser votre application jusqu'à ce que l'erreur de jeton d'actualisation se reproduise. Supprimez le fichier crendetials.json qui ne contient maintenant plus qu'un message d'erreur (ce qui s'est produit dans mon cas), puis collez votre ancien fichier d'informations d'identification dans le dossier, c'est fait! Cela fait 1 semaine que je l’ai fait et je n’ai plus de problèmes. 

0
Guilherme Canoa

maintenant, google avait refusé ces paramètres dans ma demande (access_type, Prompt) ... :( et il n’existe aucun bouton "Révoquer l’accès". Je suis frustré de récupérer mon refresh_token lol

UPDATE: J'ai trouvé la réponse ici: D vous pouvez récupérer le jeton d'actualisation par une requête https://developers.google.com/identity/protocols/OAuth2WebServer

curl -H "Type de contenu: application/x-www-form-urlencoded"\ https://accounts.google.com/o/oauth2/revoke?token= {token}

Le jeton peut être un jeton d'accès ou un jeton d'actualisation. Si le jeton est un jeton d'accès et qu'il possède un jeton d'actualisation correspondant, le jeton d'actualisation sera également révoqué.

Si la révocation est traitée avec succès, le code d'état de la réponse est 200. Pour les conditions d'erreur, un code d'état 400 est renvoyé avec un code d'erreur.

0
Tran Tien Duc

Utiliser offline access et Prompt: consent a bien fonctionné pour moi:

   auth2 = gapi.auth2.init({
                    client_id: '{cliend_id}' 
   });

   auth2.grantOfflineAccess({Prompt:'consent'}).then(signInCallback); 
0

Afin d'obtenir chaque fois une nouvelle valeur refresh_token sur l'authentification, le type d'informations d'identification OAuth 2.0 créées dans le tableau de bord doit être "Autre". De plus, comme mentionné ci-dessus, l'option access_type = 'offline' doit être utilisée lors de la génération de authURL.

Lors de l'utilisation d'informations d'identification avec le type "Application Web", aucune combinaison de variables Invite/approbation_Prompt ne fonctionnera. Vous obtiendrez toujours le refresh_token uniquement à la première demande.

0
Martin Kask