Tidak menerima token penyegaran Google OAuth

270

Saya ingin mendapatkan token akses dari Google. Google API mengatakan bahwa untuk mendapatkan token akses, kirim kode dan parameter lainnya ke halaman penghasil token, dan responsnya akan menjadi Objek JSON seperti:

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

Namun, saya tidak menerima token penyegaran. Tanggapan dalam kasus saya adalah:

{
 "access_token" : "ya29.sddsdsdsdsds_h9v_nF0IR7XcwDK8XFB2EbvtxmgvB-4oZ8oU",
"token_type" : "Bearer",
"expires_in" : 3600
}
Muhammad Usman
sumber
Saya memiliki masalah serupa. Periksa jawaban saya di sini
Arithran

Jawaban:

675

Ini refresh_tokenhanya diberikan pada otorisasi pertama dari pengguna. Otorisasi berikutnya, seperti yang Anda buat saat menguji integrasi OAuth2, tidak akan mengembalikan refresh_tokenlagi. :)

  1. Buka halaman yang menunjukkan Aplikasi dengan akses ke akun Anda: https://myaccount.google.com/u/0/permissions .
  2. Di bawah menu aplikasi Pihak Ketiga, pilih aplikasi Anda.
  3. Klik Hapus akses dan kemudian klik Oke untuk mengonfirmasi
  4. Permintaan OAuth2 berikutnya yang Anda buat akan mengembalikan refresh_token(asalkan itu juga termasuk parameter kueri 'access_type = offline'.

Atau, Anda dapat menambahkan parameter kueri prompt=consent&access_type=offlineke pengalihan OAuth (lihat halaman OAuth 2.0 untuk Aplikasi Server Web ).

Ini akan meminta pengguna untuk mengotorisasi aplikasi lagi dan akan selalu mengembalikan a refresh_token.

Rich Sutton
sumber
20
Ini tidak berhasil untuk saya, tetapi menambahkan param "access_type = offline" tampaknya melakukan trik: developers.google.com/accounts/docs/OAuth2WebServer#offline
Jesse
87
Anda perlu access_type=offlinedalam semua kasus saat Anda menginginkannya refresh_token.
DanH
5
Tapi bagaimana cara menyegarkan token setelah kedaluwarsa dalam kasus ini?
vivek_jonam
5
@vivek_jonam Menyimpan token penyegaran dan tanggal kedaluwarsa. Saat kedaluwarsa Anda meminta token baru menggunakan token penyegaran. Lihat di sini: developers.google.com/accounts/docs/OAuth2WebServer#refresh
gelviis
4
Saya membuatnya bekerja dengan $client->setAccessType('offline'). The function setApprovalPrompt()sudah berlalu dalam force, secara default.
moey
57

Untuk mendapatkan token penyegaran, Anda harus menambahkan keduanya approval_prompt=forcedan access_type="offline" Jika Anda menggunakan klien java yang disediakan oleh Google, ia akan terlihat seperti ini:

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

AuthorizationCodeRequestUrl authorizationUrl =
            flow.newAuthorizationUrl().setRedirectUri(callBackUrl)
                    .setApprovalPrompt("force")
                    .setAccessType("offline");
Gal Morad
sumber
Dalam simpul: var authUrl = oauth2Client.generateAuthUrl ({access_type: 'offline', ruang lingkup: RUANG LINGKUP, approval_prompt: 'force'});
Joris Mans
2
Sangat keterlaluan bahwa Google belum membahas hal ini dalam dokumentasi mereka atau setidaknya tidak dalam dokumentasi php atau oath2 yang telah saya tatap selama 7 jam. Mengapa di dunia ini tidak ada teks tebal besar dalam dokumen mereka
Colin Rickels
Terima kasih! Documents di sini ( github.com/googlesamples/apps-script-oauth2 ) sangat menyesatkan tentang parameter ini. Ketika saya menambahkan persetujuan_prompt = memaksa saya akhirnya mendapat token penyegaran.
Alex Zhevzhik
28

Saya mencari malam yang panjang dan ini melakukan trik:

Pengguna-example.php yang dimodifikasi dari admin-sdk

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

kemudian Anda mendapatkan kode di url redirect dan otentikasi dengan kode dan mendapatkan token penyegaran

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

Anda harus menyimpannya sekarang;)

Saat akseskey Anda habis, lakukan saja

$client->refreshToken($theRefreshTokenYouHadStored);
Norbert
sumber
Sempurna @Norbert, Ini persis apa yang saya butuhkan.
Emmanuel
Terima kasih! Jawaban tepat untuk pertanyaan saya @Norbert
teja-MVT
16

Ini telah menyebabkan saya kebingungan, jadi saya pikir saya akan membagikan apa yang saya pelajari dengan cara yang sulit:

Ketika Anda meminta akses menggunakan access_type=offlinedan approval_prompt=forceparameter Anda harus menerima token akses dan token penyegaran . The akses Token berakhir segera setelah Anda menerimanya dan Anda akan perlu untuk menyegarkan itu.

Anda dengan benar membuat permintaan untuk mendapatkan token akses baru dan menerima respons yang memiliki token akses baru Anda . Saya juga bingung dengan kenyataan bahwa saya tidak mendapatkan token penyegaran baru . Namun, beginilah seharusnya karena Anda dapat menggunakan token penyegaran yang sama berulang kali.

Saya pikir beberapa jawaban lain berasumsi bahwa Anda ingin mendapatkan token penyegaran baru untuk beberapa alasan dan menyarankan agar Anda mengotorisasi ulang pengguna, tetapi pada kenyataannya, Anda tidak perlu karena token penyegaran yang Anda miliki akan bekerja hingga dicabut oleh pengguna.

jeteon
sumber
1
Saya memiliki CMS tempat pengguna yang berbeda menggunakan akun google yang berbeda untuk terhubung ke api analytics. Namun, terkadang beberapa pengguna dapat terhubung menggunakan akun google perusahaan yang sama, tetapi masing-masing menginginkan akses ke akun Analytics yang berbeda. Hanya yang pertama yang menerima token penyegaran, sementara yang lainnya tidak dan karenanya harus terhubung kembali setiap jam. Apakah tidak ada cara untuk mendapatkan token refresh SAMA untuk otentikasi berikutnya, bukan hanya access_token yang kedaluwarsa dalam satu jam?
SsjCosty
1
API tampaknya menghasilkan token penyegaran tepat sekali. "Pembagian" token apa pun harus terjadi dalam kode Anda. Anda harus berhati-hati agar tidak secara tidak sengaja memberikan hak akses baru kepada pengguna. Cara sederhana untuk melakukan ini adalah memiliki aplikasi melacak token penyegaran dan akun terkait di penyimpanan mereka sendiri ('tabel' terpisah dalam SQLese). Kemudian, ketika Anda ingin mendapatkan token akses baru, Anda memeriksa dan menggunakan token yang mungkin umum ini dari sana. Diterapkan dengan cara tertentu, kode Anda tidak perlu tahu siapa yang benar-benar mendapatkan token.
jeteon
1
Saya tidak tahu bagaimana saya bisa mengidentifikasi token penyegaran mana yang harus saya kaitkan dengan token akses baru yang baru saja saya dapatkan. Ada beberapa pengguna yang melakukan login, dan satu-satunya kesamaan yang mereka miliki adalah mereka menggunakan akun Google (email) yang sama untuk terhubung ke API. Tetapi Google tidak mengirim kembali ID akun atau email, itu hanya mengirim kembali token. Jadi saya tidak tahu bagaimana mengaitkan 2 pengguna CMS yang berbeda ...
SsjCosty
Saya telah sepenuhnya menjelaskan masalah saya di sini: stackoverflow.com/questions/30217524/…
SsjCosty
Youtube oAuth2 refresh_token hanya ditampilkan saat digunakan.
Dmitry Polushkin
7

Jawaban Rich Sutton akhirnya berhasil untuk saya, setelah saya menyadari bahwa menambahkan access_type=offlinedilakukan atas permintaan klien ujung depan untuk kode otorisasi, bukan permintaan ujung belakang yang menukar kode itu dengan access_token. Saya telah menambahkan komentar untuk jawabannya dan tautan ini di Google untuk info lebih lanjut tentang token yang menyegarkan.

PS Jika Anda menggunakan Satellizer, berikut adalah cara menambahkan opsi itu ke $ authProvider.google di AngularJS .

Zack Morris
sumber
Detail yang sangat kecil namun penting. Menyelamatkan saya! Terima kasih :)
Dexter
@ZackMorris Jadi .. maksud Anda mengatakan bahwa saya tidak bisa mendapatkan refresh_token dari backend menggunakan token akses?
Nevermore
@Nevermore Anda tidak bisa mendapatkan refresh_token dari access_token itu sendiri. Jika Anda ingin server Anda menangani penyegaran, maka Anda harus menyimpan refresh_token di basis data Anda pertama kali. Juga jika Anda melakukan aliran OAuth klien di ujung depan, maka pengguna harus mengirim refresh_token mereka ke ujung belakang jika mereka ingin server me-refresh untuk mereka.
Zack Morris
4

Untuk mendapatkan refresh_tokenAnda perlu memasukkan access_type=offlinedalam URL permintaan OAuth. Saat pengguna mengautentikasi untuk pertama kali, Anda akan mendapatkan non-nil refresh_tokendan access_tokenyang kedaluwarsa.

Jika Anda memiliki situasi di mana pengguna mungkin mengautentikasi ulang akun yang sudah memiliki token otentikasi untuk Anda (seperti @SsjCosty menyebutkan di atas), Anda perlu mendapatkan kembali informasi dari Google di mana akun itu digunakan untuk tujuan tersebut. Untuk melakukan itu, tambahkan profileke lingkup Anda. Menggunakan permata OAuth2 Ruby, permintaan terakhir Anda mungkin terlihat seperti ini:

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

Perhatikan bahwa ruang lingkup memiliki dua entri terbatas-ruang, satu untuk akses hanya-baca ke Google Analytics, dan lainnya hanya profile, yang merupakan standar OpenID Connect.

Ini akan menghasilkan Google menyediakan atribut tambahan yang disebut id_tokendalam get_tokenrespons. Untuk mendapatkan informasi dari id_token, periksa halaman ini di Google docs. Ada beberapa perpustakaan yang disediakan Google yang akan memvalidasi dan "mendekode" ini untuk Anda (saya menggunakan permata google-id-token Ruby ). Setelah Anda diuraikan, subparameternya adalah ID akun Google yang unik.

Patut dicatat, jika Anda mengubah cakupan, Anda akan mendapatkan kembali token penyegaran lagi untuk pengguna yang telah diautentikasi dengan cakupan asli. Ini berguna jika, katakanlah, Anda sudah memiliki banyak pengguna dan tidak ingin membuat mereka semua menghapus aplikasi di Google.

Oh, dan satu catatan terakhir: Anda tidak perlu prompt=select_account , tetapi berguna jika Anda memiliki situasi di mana pengguna Anda mungkin ingin mengautentikasi dengan lebih dari satu akun Google (yaitu, Anda tidak menggunakan ini untuk masuk / otentikasi) .

intinya
sumber
Saya pikir bagian tentang mengidentifikasi pengguna tanpa menyimpan informasi pribadi adalah kuncinya. Terima kasih telah menunjukkannya, saya tidak melihat referensi di google docs tentang itu.
Danielo515
3

1. Bagaimana cara mendapatkan 'refresh_token'?

Solusi: opsi access_type = 'offline' harus digunakan ketika membuat authURL. sumber: Menggunakan OAuth 2.0 untuk Aplikasi Server Web

2. Tetapi bahkan dengan 'access_type = offline', saya tidak mendapatkan 'refresh_token'?

Solusi: Harap dicatat bahwa Anda akan mendapatkannya hanya pada permintaan pertama, jadi jika Anda menyimpannya di suatu tempat dan ada ketentuan untuk menimpa ini dalam kode Anda ketika mendapatkan access_token baru setelah kedaluwarsa sebelumnya, maka pastikan untuk tidak menimpa nilai ini.

Dari Google Auth Doc: (nilai ini = access_type)

Nilai ini menginstruksikan server otorisasi Google untuk mengembalikan token penyegaran dan token akses saat pertama kali aplikasi Anda menukar kode otorisasi dengan token.

Jika Anda perlu 'refresh_token' lagi, maka Anda perlu menghapus akses untuk aplikasi Anda dengan mengikuti langkah-langkah yang ditulis dalam jawaban Rich Sutton .

Mazahir Haroon
sumber
2

Pengaturan ini akan menyebabkan token penyegaran dikirim setiap kali:

$client->setApprovalPrompt('force');

contoh diberikan di bawah ini (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
sumber
1

Bagi saya, saya mencoba CalendarSampleServletmenyediakan oleh Google. Setelah 1 jam, access_key habis dan ada pengalihan ke halaman 401. Saya mencoba semua opsi di atas tetapi tidak berhasil. Akhirnya setelah memeriksa kode sumber untuk 'AbstractAuthorizationCodeServlet' , saya bisa melihat bahwa pengalihan akan dinonaktifkan jika ada kredensial, tetapi idealnya harus diperiksa refresh token!=null. Saya menambahkan kode di bawah ini CalendarSampleServletdan berhasil setelah itu. Sangat lega setelah frustrasi selama berjam-jam. Terima kasih Tuhan.

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

sekarang google telah menolak parameter-parameter dalam permintaan saya (access_type, prompt) ... :( dan tidak ada tombol "Cabut Akses" sama sekali. Saya frustasi karena mendapatkan kembali lol refresh_token saya

UPDATE: Saya menemukan jawabannya di sini: D Anda dapat memperoleh kembali token penyegaran dengan permintaan https://developers.google.com/identity/protocols/OAuth2WebServer

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

Token dapat berupa token akses atau token penyegaran. Jika token adalah token akses dan memiliki token refresh yang sesuai, token refresh juga akan dicabut.

Jika pencabutan berhasil diproses, maka kode status dari respons adalah 200. Untuk kondisi kesalahan, kode status 400 dikembalikan bersama dengan kode kesalahan.

Tran Tien Duc
sumber
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
sumber
0

Menggunakan akses offline dan prompt: persetujuan bekerja dengan baik untuk saya:

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

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

Solusi saya agak aneh..saya mencoba setiap solusi yang saya temukan di internet dan tidak ada. Anehnya ini berhasil: hapus kredensial.json, segarkan, vinculate aplikasi Anda di akun lagi. File credentials.json baru akan memiliki token penyegaran. Cadangkan file ini di suatu tempat. Kemudian tetap gunakan aplikasi Anda sampai kesalahan token penyegaran kembali. Hapus file crendetials.json yang sekarang hanya dengan pesan kesalahan (ini terjadi dalam kasus saya), lalu tempelkan file kredensial lama Anda di folder, selesai! Sudah 1 minggu sejak saya melakukan ini dan tidak punya masalah lagi.

Guilherme Canoa
sumber
0

Untuk mendapatkan refresh_token baru setiap kali pada otentikasi, tipe kredensial OAuth 2.0 yang dibuat di dasbor harus "Lainnya". Juga seperti yang disebutkan di atas opsi access_type = 'offline' harus digunakan ketika membuat authURL.

Saat menggunakan kredensial dengan jenis "Aplikasi Web" tidak ada kombinasi variabel prompt / approval_prompt akan bekerja - Anda masih akan mendapatkan refresh_token hanya pada permintaan pertama.

Martin Kask
sumber