Google OAuth-Aktualisierungstoken wird nicht empfangen

270

Ich möchte das Zugriffstoken von Google erhalten. Die Google-API gibt an, dass zum Abrufen des Zugriffstokens der Code und andere Parameter an die Token-Generierungsseite gesendet werden sollen. Die Antwort lautet wie folgt: JSON-Objekt:

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

Ich erhalte jedoch kein Aktualisierungstoken. Die Antwort in meinem Fall lautet:

{
 "access_token" : "ya29.sddsdsdsdsds_h9v_nF0IR7XcwDK8XFB2EbvtxmgvB-4oZ8oU",
"token_type" : "Bearer",
"expires_in" : 3600
}
Muhammad Usman
quelle
Ich hatte ein ähnliches Problem. Überprüfen Sie meine Antwort hier
Arithran

Antworten:

675

Die refresh_tokenwird nur bei der ersten Autorisierung durch den Benutzer bereitgestellt. Nachfolgende Berechtigungen, wie sie beispielsweise beim Testen einer OAuth2-Integration vorgenommen werden, geben die nicht refresh_tokenerneut zurück. :) :)

  1. Wechseln Sie zu der Seite mit Apps mit Zugriff auf Ihr Konto: https://myaccount.google.com/u/0/permissions .
  2. Wählen Sie im Menü Apps von Drittanbietern Ihre App aus.
  3. Klicken Sie auf Zugriff entfernen und dann zur Bestätigung auf OK
  4. Die nächste OAuth2-Anforderung, die Sie stellen, gibt eine zurück refresh_token(vorausgesetzt, sie enthält auch den Abfrageparameter 'access_type = offline'.

Alternativ können Sie die Abfrageparameter prompt=consent&access_type=offlinezur OAuth-Umleitung hinzufügen (siehe Googles Seite OAuth 2.0 für Webserveranwendungen ).

Dadurch wird der Benutzer aufgefordert, die Anwendung erneut zu autorisieren, und es wird immer a zurückgegeben refresh_token.

Rich Sutton
quelle
20
Dies funktionierte nicht für mich, aber das Hinzufügen des Parameters "access_type = offline" schien den Trick zu tun: developer.google.com/accounts/docs/OAuth2WebServer#offline
Jesse
87
Sie müssen access_type=offlinein allen Fällen, wenn Sie die wollen refresh_token.
DanH
5
Aber wie aktualisiere ich das Token in diesem Fall nach Ablauf?
vivek_jonam
5
@vivek_jonam Speichern Sie das Aktualisierungstoken und das Ablaufdatum. Wenn es abläuft, fordern Sie mithilfe des Aktualisierungstokens ein neues Token an. Siehe hier: developer.google.com/accounts/docs/OAuth2WebServer#refresh
gelviis
4
Ich habe es zum Laufen gebracht $client->setAccessType('offline'). Das function setApprovalPrompt()ist forcestandardmäßig bereits übergeben .
Moey
57

Um die Refresh - Token Sie beide hinzuzufügen haben approval_prompt=forceund access_type="offline" wenn Sie die Java - Client von Google bereitgestellt verwenden es wie folgt aussehen:

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

AuthorizationCodeRequestUrl authorizationUrl =
            flow.newAuthorizationUrl().setRedirectUri(callBackUrl)
                    .setApprovalPrompt("force")
                    .setAccessType("offline");
Gal Morad
quelle
Im Knoten: var authUrl = oauth2Client.generateAuthUrl ({Zugriffstyp: 'offline', Bereich: SCOPES, Genehmigungsaufforderung: 'force'});
Joris Mans
2
Es ist empörend, dass Google dies nicht in seiner Dokumentation oder zumindest nicht in der PHP- oder Oath2-Dokumentation angesprochen hat, auf die ich 7 Stunden lang gestarrt habe. Warum in aller Welt ist dies nicht in großen Fettdruck in ihren Dokumenten
Colin Rickels
Danke dir! Die Dokumente hier ( github.com/googlesamples/apps-script-oauth2 ) sind über diesen Parameter sehr irreführend. Als ich Approval_prompt = Force hinzugefügt habe, habe ich endlich ein Aktualisierungstoken erhalten.
Alex Zhevzhik
28

Ich habe eine lange Nacht gesucht und das macht den Trick:

Geänderte user-example.php von admin-sdk

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

Dann erhalten Sie den Code unter der Umleitungs-URL und die Authentifizierung mit dem Code sowie das Aktualisierungstoken

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

Du solltest es jetzt aufbewahren;)

Wenn Ihr Zugriffsschlüssel abgelaufen ist, tun Sie es einfach

$client->refreshToken($theRefreshTokenYouHadStored);
Norbert
quelle
Perfekt @Norbert, das war genau das, was ich brauchte.
Emmanuel
Vielen Dank! Genaue Antwort auf meine Frage @Norbert
varun teja-MVT
16

Dies hat mich verwirrt und ich dachte, ich würde teilen, was ich auf die harte Tour gelernt habe:

Wenn Sie den Zugriff mit den Parametern access_type=offlineund anfordern approval_prompt=force, sollten Sie sowohl ein Zugriffstoken als auch ein Aktualisierungstoken erhalten . Das Zugriffstoken läuft bald nach Erhalt ab und Sie müssen es aktualisieren.

Sie haben die Anforderung zum Abrufen eines neuen Zugriffstokens korrekt gestellt und die Antwort mit Ihrem neuen Zugriffstoken erhalten . Ich war auch verwirrt darüber, dass ich kein neues Aktualisierungstoken erhalten habe . Dies ist jedoch so gemeint, da Sie immer wieder dasselbe Aktualisierungstoken verwenden können.

Ich denke, einige der anderen Antworten gehen davon aus, dass Sie sich aus irgendeinem Grund ein neues Aktualisierungstoken zulegen wollten, und schlugen vor, dass Sie den Benutzer erneut autorisieren, aber tatsächlich müssen Sie dies nicht tun, da das Aktualisierungstoken, das Sie haben, erst funktioniert vom Benutzer widerrufen.

Jeteon
quelle
1
Ich habe ein CMS, in dem verschiedene Benutzer unterschiedliche Google-Konten verwenden, um eine Verbindung zur Analytics-API herzustellen. Manchmal können jedoch mehrere Benutzer eine Verbindung mit demselben Google-Unternehmenskonto herstellen, die jedoch jeweils Zugriff auf ein anderes Analytics-Konto wünschen. Nur der erste erhält das Aktualisierungstoken, während alle anderen dies nicht tun und daher jede Stunde eine neue Verbindung herstellen müssen. Gibt es nicht eine Möglichkeit, das gleiche Aktualisierungstoken für nachfolgende Authentifizierungen zu erhalten, anstatt nur das access_token, das innerhalb einer Stunde abläuft?
SsjCosty
1
Die API scheint das Aktualisierungstoken genau einmal zu erzeugen . Jede "Freigabe" des Tokens müsste in Ihrem Code erfolgen. Sie müssen jedoch darauf achten, dass Benutzer nicht versehentlich neue Zugriffsrechte erhalten. Eine einfache Möglichkeit, dies zu tun, besteht darin, dass die Anwendung die Aktualisierungstoken und die zugehörigen Konten in ihrem eigenen Speicher verfolgt (separate 'Tabelle' in SQLese). Wenn Sie dann ein neues Zugriffstoken erhalten möchten, suchen Sie nach diesem möglicherweise gemeinsamen Token und verwenden es von dort aus. Auf eine bestimmte Weise implementiert, muss Ihr Code nicht wissen, wer das Token tatsächlich erhalten hat.
Jeteon
1
Ich weiß nicht, wie ich feststellen kann, welches Aktualisierungstoken ich einem neuen Zugriffstoken zuordnen soll, das ich gerade erhalten habe. Es gibt verschiedene Benutzer, die sich anmelden. Das einzige, was sie gemeinsam haben, ist, dass sie dasselbe Google-Konto (E-Mail) verwenden, um eine Verbindung zur API herzustellen. Google sendet jedoch keine ID des Kontos oder der E-Mail zurück, sondern nur ein Token. Ich weiß also nicht, wie ich die 2 verschiedenen CMS-Benutzer
zuordnen soll
Ich habe mein Problem hier vollständig erklärt: stackoverflow.com/questions/30217524/…
SsjCosty
Youtube oAuth2 refresh_token wird nur angezeigt, wenn Gewalt angewendet wird.
Dmitry Polushkin
7

Die Antwort von Rich Sutton funktionierte schließlich für mich, nachdem mir klar wurde, dass das Hinzufügen access_type=offlineauf der Anfrage des Front-End- Clients nach einem Autorisierungscode erfolgt, nicht auf der Back-End-Anfrage, die diesen Code gegen ein access_token austauscht. Ich habe seiner Antwort und diesem Link bei Google einen Kommentar hinzugefügt, um weitere Informationen zum Aktualisieren von Token zu erhalten.

PS Wenn Sie Satellizer verwenden, können Sie diese Option wie folgt zu $ ​​authProvider.google in AngularJS hinzufügen .

Zack Morris
quelle
Sehr kleine Details, aber wichtig. Rettete mich ! Danke :)
Dexter
@ZackMorris Also ... willst du damit sagen, dass ich kein refresh_token vom Backend mit dem Zugriffstoken erhalten kann?
Nie mehr
@Nevermore Sie können kein refresh_token vom access_token selbst erhalten. Wenn Ihr Server Aktualisierungen verarbeiten soll, müssen Sie das refresh_token beim ersten Mal in Ihrer Datenbank speichern. Wenn Sie einen Client-OAuth-Flow im Front-End ausführen, müssen Benutzer ihr refresh_token an das Back-End senden, wenn der Server für sie aktualisiert werden soll.
Zack Morris
4

Um das zu erhalten refresh_token, müssen Sie es access_type=offlinein die OAuth-Anforderungs-URL aufnehmen. Wenn sich ein Benutzer zum ersten Mal authentifiziert, erhalten Sie eine Nicht-Null refresh_tokensowie eine access_tokenabgelaufene zurück.

Wenn ein Benutzer ein Konto erneut authentifiziert, für das Sie bereits ein Authentifizierungstoken haben (wie oben bei @SsjCosty erwähnt), müssen Sie Informationen von Google zurückerhalten, für welches Konto das Token bestimmt ist. Fügen Sie dazu hinzuprofile dazu Ihre Bereiche hinzu. Wenn Sie den OAuth2 Ruby-Edelstein verwenden, sieht Ihre endgültige Anfrage möglicherweise folgendermaßen aus:

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

Beachten Sie, dass der Bereich zwei durch Leerzeichen getrennte Einträge enthält, einen für den schreibgeschützten Zugriff auf Google Analytics und profileeinen für OpenID Connect.

Dies führt dazu, dass Google ein zusätzliches Attribut bereitstellt, das id_tokenin der get_tokenAntwort aufgerufen wird . Um Informationen aus dem id_token zu erhalten, lesen Sie diese Seite in den Google-Dokumenten. Es gibt eine Handvoll von Google bereitgestellter Bibliotheken, die dies für Sie validieren und "dekodieren" (ich habe das Ruby -Juwel "google-id-token" verwendet ). Sobald Sie es analysiert haben, ist der subParameter effektiv die eindeutige Google-Konto-ID.

Bemerkenswert, wenn Sie sich ändern den Bereich , erhalten Sie erneut ein Aktualisierungstoken für Benutzer, die sich bereits beim ursprünglichen Bereich authentifiziert haben. Dies ist nützlich, wenn Sie beispielsweise bereits eine Reihe von Nutzern haben und nicht möchten, dass alle Benutzer die App in Google deaktivieren.

Oh, und eine letzte Anmerkung: Sie müssen nicht brauchen prompt=select_account , aber es ist nützlich , wenn Sie eine Situation, wo Ihre Benutzer möchten vielleicht mit mehr als einem Google - Konto authentifizieren (dh Sie verwenden diese nicht für die Anmeldung in / Authentifizierung) .

Coreyward
quelle
Ich denke, der Teil über die Identifizierung von Benutzern ohne Speicherung persönlicher Informationen ist der Schlüssel. Vielen Dank für den Hinweis, ich habe in Google Docs keinen Hinweis darauf gesehen.
Danielo515
3

1. Wie bekomme ich 'refresh_token'?

Lösung: Die Option access_type = 'offline' sollte beim Generieren von authURL verwendet werden. Quelle: Verwenden von OAuth 2.0 für Webserveranwendungen

2. Aber selbst mit 'access_type = offline' erhalte ich nicht das 'refresh_token'?

Lösung: Bitte beachten Sie, dass Sie es nur bei der ersten Anforderung erhalten. Wenn Sie es also irgendwo speichern und es vorgesehen ist, dies in Ihrem Code zu überschreiben, wenn Sie nach Ablauf des vorherigen Ablaufs ein neues access_token erhalten, stellen Sie sicher, dass Sie diesen Wert nicht überschreiben.

Aus Google Auth Doc: (dieser Wert = access_type)

Dieser Wert weist den Google-Autorisierungsserver an, ein Aktualisierungstoken und ein Zugriffstoken zurückzugeben, wenn Ihre Anwendung zum ersten Mal einen Autorisierungscode gegen Token austauscht.

Wenn Sie erneut 'refresh_token' benötigen, müssen Sie den Zugriff für Ihre App entfernen, indem Sie die in Rich Suttons Antwort beschriebenen Schritte ausführen .

Mazahir Haroon
quelle
2

Wenn Sie dies einstellen, wird das Aktualisierungstoken jedes Mal gesendet:

$client->setApprovalPrompt('force');

Ein Beispiel ist unten angegeben (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');
Apadana
quelle
1

Für mich habe ich versucht, CalendarSampleServletvon Google zur Verfügung gestellt. Nach 1 Stunde läuft der access_key ab und es wird auf eine 401-Seite umgeleitet. Ich habe alle oben genannten Optionen ausprobiert, aber sie haben nicht funktioniert. Schließlich konnte ich beim Überprüfen des Quellcodes auf 'AbstractAuthorizationCodeServlet' feststellen , dass die Umleitung deaktiviert ist, wenn Anmeldeinformationen vorhanden sind, aber im Idealfall sollte dies überprüft werden refresh token!=null. Ich habe den folgenden Code hinzugefügt CalendarSampleServletund es hat danach funktioniert. Große Erleichterung nach so vielen Stunden der Frustration. Danke Gott.

if (credential.getRefreshToken() == null) {
    AuthorizationCodeRequestUrl authorizationUrl = authFlow.newAuthorizationUrl();
    authorizationUrl.setRedirectUri(getRedirectUri(req));
    onAuthorization(req, resp, authorizationUrl);
    credential = null;
}
Anoop Isaac
quelle
0

Jetzt hatte Google diese Parameter in meiner Anfrage abgelehnt (access_type, prompt) ... :( und es gibt überhaupt keine Schaltfläche "Zugriff widerrufen". Ich bin frustriert, weil ich mein refresh_token lol zurückbekommen habe

UPDATE: Ich habe die Antwort hier gefunden: D Sie können das Aktualisierungstoken durch eine Anfrage https://developers.google.com/identity/protocols/OAuth2WebServer zurückerhalten

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

Das Token kann ein Zugriffstoken oder ein Aktualisierungstoken sein. Wenn das Token ein Zugriffstoken ist und über ein entsprechendes Aktualisierungstoken verfügt, wird das Aktualisierungstoken ebenfalls widerrufen.

Wenn der Widerruf erfolgreich verarbeitet wurde, lautet der Statuscode der Antwort 200. Bei Fehlerbedingungen wird ein Statuscode 400 zusammen mit einem Fehlercode zurückgegeben.

Tran Tien Duc
quelle
0
    #!/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
Yordan Georgiev
quelle
0

Mit den Offline - Zugriff und prompt: Zustimmung gut für mich gearbeitet:

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

   auth2.grantOfflineAccess({prompt:'consent'}).then(signInCallback); 
Carlos Eduardo Ki Lee
quelle
0

Meine Lösung war ein bisschen komisch. Ich habe jede Lösung ausprobiert, die ich im Internet gefunden habe, und nichts. Überraschenderweise hat dies funktioniert: Löschen Sie die Anmeldeinformationen.json, aktualisieren Sie, vinculieren Sie Ihre App erneut in Ihrem Konto. Die neue Datei credentials.json enthält das Aktualisierungstoken. Sichern Sie diese Datei irgendwo. Verwenden Sie dann Ihre App so lange, bis der Fehler beim Aktualisieren des Tokens erneut auftritt. Löschen Sie die Datei crendetials.json, die jetzt nur noch eine Fehlermeldung enthält (dies ist in meinem Fall passiert), und fügen Sie dann Ihre alte Anmeldeinformationsdatei in den Ordner ein. Fertig! Es ist 1 Woche her, seit ich das getan habe und hatte keine Probleme mehr.

Guilherme Canoa
quelle
0

Um bei der Authentifizierung jedes Mal ein neues refresh_token zu erhalten, sollte der im Dashboard erstellte Typ der OAuth 2.0-Anmeldeinformationen "Andere" sein. Wie oben erwähnt, sollte beim Generieren der authURL auch die Option access_type = 'offline' verwendet werden.

Wenn Sie Anmeldeinformationen vom Typ "Webanwendung" verwenden, funktioniert keine Kombination von Eingabeaufforderungs- / Genehmigungsaufforderungsvariablen. Sie erhalten das refresh_token nur bei der ersten Anforderung.

Martin Kask
quelle