invalid_grant mencoba mendapatkan token oAuth dari google

121

Saya terus mendapatkan invalid_grantkesalahan saat mencoba mendapatkan token oAuth dari Google untuk terhubung ke api kontak mereka. Semua informasi benar dan saya telah memeriksa tiga kali lipat ini sehingga agak bingung.

Adakah yang tahu apa yang mungkin menyebabkan masalah ini? Saya telah mencoba mengatur id klien yang berbeda untuk itu tetapi saya mendapatkan hasil yang sama, saya telah mencoba menghubungkan banyak cara berbeda termasuk mencoba otentikasi paksa, tetapi tetap hasilnya sama.

André Figueira
sumber
bagi saya masalahnya ada di halaman kredensial google ... saya membuat yang lain ... dan menyelesaikan masalah ....
costamatrix

Jawaban:

59

Saya mengalami masalah ini ketika saya tidak secara eksplisit meminta akses "offline" saat mengirim pengguna ke OAuth "Apakah Anda ingin memberi izin aplikasi ini untuk menyentuh barang-barang Anda?" halaman.

Pastikan Anda menentukan access_type = offline dalam permintaan Anda.

Detailnya ada di sini: https://developers.google.com/accounts/docs/OAuth2WebServer#offline

(Juga: Saya pikir Google menambahkan batasan ini pada akhir 2011. Jika Anda memiliki token lama sebelum itu, Anda harus mengarahkan pengguna Anda ke halaman izin untuk mengotorisasi penggunaan offline.)

bonkydog
sumber
9
@Adders Saya setuju. Aku mengatur access_typeuntuk offline, kesalahan ini masih terjadi.
slideshowp2
Ini seharusnya bukan jawaban yang diterima.
Kishan Solanki
Buka dokumentasi developer.google.com/android-publisher/authorization ini dan baca semuanya untuk diterapkan
Kishan Solanki
70

Saya mengalami masalah yang sama ini meskipun menentukan "offline" access_typedalam permintaan saya sesuai jawaban bonkydog. Singkat cerita, saya menemukan bahwa solusi yang dijelaskan di sini berhasil untuk saya:

https://groups.google.com/forum/#!topic/google-analytics-data-export-api/4uNaJtquxCs

Intinya, ketika Anda menambahkan Klien OAuth2 di konsol Google API Anda, Google akan memberi Anda "ID Klien" dan "alamat Email" (dengan asumsi Anda memilih "webapp" sebagai jenis klien Anda). Dan terlepas dari konvensi penamaan Google yang menyesatkan, mereka mengharapkan Anda mengirimkan "Alamat email" sebagai nilai client_idparameter saat Anda mengakses API OAuth2 mereka.

Ini berlaku saat memanggil kedua URL ini:

Perhatikan bahwa panggilan ke URL pertama akan berhasil jika Anda memanggilnya dengan "ID Klien", bukan "Alamat email" Anda. Namun menggunakan kode yang dikembalikan dari permintaan itu tidak akan berfungsi saat mencoba mendapatkan token pembawa dari URL kedua. Sebagai gantinya Anda akan mendapatkan pesan 'Error 400' dan "invalid_grant".

aroth
sumber
66
Benar-benar konyol: Terutama bagian di mana ia bekerja dengan client_id jika Anda mendapatkan token penyegaran awal. API Google dan dokumentasinya berantakan.
Traubenfuchs
7
Saya membenturkan kepala saya terhadap masalah ini selama berjam-jam. Saya tidak pernah menyangka bahwa 'client_id' bukan yang diharapkan untuk bidang 'client_id'. Kecuali kadang-kadang saat Anda mendapatkan refresh_token dan itu berfungsi. Cukup yakin kata-kata yang saya miliki untuk google saat ini tidak dapat diucapkan di SO.
Justin
6
Halo .. Saya tidak dapat menemukan alamat "email" yang kalian bicarakan. inilah yang saya miliki di konsol saya -> pbs.twimg.com/media/CVVcEBWUwAAIiCy.png:large
omarojo
4
Dimana alamat email itu? Saya mengalami masalah yang sama
Isma Haro
4
Jangan pernah percaya dokumentasi Google. Dokumentasi dan API paling tajam berasal dari Google, perusahaan paling berharga di dunia. Saya harus menghabiskan waktu berjam-jam untuk menggunakan Google API. Ada masalah demi masalah dan kemudian pustaka .Net mereka sendiri untuk API yang berbeda tidak dikompilasi bersama karena masalah ketergantungan yang berbeda dan semuanya. Kode sekarang berfungsi dengan baik untuk sebagian besar pengguna tetapi untuk beberapa pengguna saya masih mendapatkan invalid_grant, invalid_credentials, dll. Tanpa alasan tertentu.
Allen King
56

Meskipun ini adalah pertanyaan lama, sepertinya banyak yang masih mengalaminya - kami menghabiskan waktu berhari-hari untuk mencari tahu sendiri.

Dalam spesifikasi OAuth2, "invalid_grant" adalah semacam penampung-semua untuk semua kesalahan yang terkait dengan token yang tidak valid / kedaluwarsa / dicabut (pemberian autentikasi atau token penyegaran).

Bagi kami, masalahnya ada dua:

  1. Pengguna telah secara aktif mencabut akses ke aplikasi kami.
    Masuk akal, tetapi dapatkan ini: 12 jam setelah pencabutan, Google berhenti mengirim pesan kesalahan dalam tanggapan mereka: “error_description” : “Token has been revoked.”
    Ini agak menyesatkan karena Anda akan menganggap bahwa pesan kesalahan ada setiap saat yang sebenarnya tidak kasus. Anda dapat memeriksa apakah aplikasi Anda masih memiliki akses di halaman izin aplikasi .

  2. Pengguna telah menyetel ulang / memulihkan sandi Google mereka
    Pada bulan Desember 2015, Google mengubah perilaku default mereka sehingga penyetelan ulang sandi untuk pengguna non-Google Apps akan secara otomatis mencabut semua token penyegaran aplikasi pengguna. Saat pencabutan, pesan kesalahan mengikuti aturan yang sama seperti kasus sebelumnya, jadi Anda hanya akan mendapatkan "error_description" dalam 12 jam pertama. Sepertinya tidak ada cara untuk mengetahui apakah pengguna secara manual mencabut akses (sengaja) atau itu terjadi karena penyetelan ulang kata sandi (efek samping).

Selain itu, ada segudang penyebab potensial lain yang dapat memicu kesalahan:

  1. Jam / waktu server tidak sinkron
  2. Tidak diizinkan untuk akses offline
  3. Dibatasi oleh Google
  4. Menggunakan token penyegaran yang kedaluwarsa
  5. Pengguna sudah tidak aktif selama 6 bulan
  6. Gunakan email pekerja layanan, bukan ID klien
  7. Terlalu banyak token akses dalam waktu singkat
  8. SDK klien mungkin sudah usang
  9. Token penyegaran salah / tidak lengkap

Saya telah menulis artikel pendek yang merangkum setiap item dengan beberapa panduan debugging untuk membantu menemukan pelakunya. Semoga membantu.

laander
sumber
1
Skenario lain adalah jika Anda mencoba mendapatkan token beberapa kali dari kode autentikasi yang sama.
knownasilya
Itulah masalah saya, hanya mencabut aplikasi secara tidak sengaja. Kemudian harus menjalankan ulang refreshToken.php melalui terminal untuk menghasilkan kode otorisasi lain dan kemudian mengganti refreshToken di mana saja untuk clientID ini.
Robert Sinclair
7

Saya mengalami masalah yang sama. Bagi saya, saya memperbaikinya dengan menggunakan Alamat Email (string yang diakhiri dengan ... @ developer.gserviceaccount.com) daripada ID Klien untuk nilai parameter client_id. Penamaan yang ditetapkan oleh Google membingungkan di sini.

Tony Vu
sumber
6
Ini adalah jawaban yang sama yang diberikan oleh @aroth lebih dari setahun sebelumnya
Bryan Ash
3

Masalah saya adalah saya menggunakan URL ini:

https://accounts.google.com/o/oauth2/token

Kapan saya seharusnya menggunakan URL ini:

https://www.googleapis.com/oauth2/v4/token

Ini sedang menguji akun layanan yang menginginkan akses offline ke mesin Penyimpanan .

Brad Lee
sumber
2

Saya mendapat pesan kesalahan yang sama 'invalid_grant' dan itu karena authResult ['code'] yang dikirim dari javascript sisi klien tidak diterima dengan benar di server.

Cobalah untuk mengeluarkannya kembali dari server untuk melihat apakah itu benar dan bukan string kosong.

Mihai Crăiță
sumber
1

jika Anda menggunakan perpustakaan juru tulis, cukup atur mode offline, seperti yang disarankan bonkydog di sini adalah kodenya:

OAuthService service = new ServiceBuilder().provider(Google2Api.class).apiKey(clientId).apiSecret(apiSecret)
                .callback(callbackUrl).scope(SCOPE).offline(true)
                .build();

https://github.com/codolutions/scribe-java/

Oleksii Kyslytsyn
sumber
1

Menggunakan clientId Android (tidak ada client_secret) saya mendapatkan respon kesalahan berikut:

{
 "error": "invalid_grant",
 "error_description": "Missing code verifier."
}

Saya tidak dapat menemukan dokumentasi apa pun untuk bidang 'code_verifier' tetapi saya menemukan jika Anda menyetelnya ke nilai yang sama di otorisasi dan permintaan token, itu akan menghapus kesalahan ini. Saya tidak yakin apa nilai yang dimaksudkan atau apakah itu harus aman. Ini memiliki beberapa panjang minimum (16? Karakter) tetapi saya menemukan pengaturan nulljuga berfungsi.

Saya menggunakan AppAuth untuk permintaan otorisasi di klien Android saya yang memiliki setCodeVerifier()fungsi.

AuthorizationRequest authRequest = new AuthorizationRequest.Builder(
                                    serviceConfiguration,
                                    provider.getClientId(),
                                    ResponseTypeValues.CODE,
                                    provider.getRedirectUri()
                            )
                            .setScope(provider.getScope())
                            .setCodeVerifier(null)
                            .build();

Berikut adalah contoh permintaan token di node:

request.post(
  'https://www.googleapis.com/oauth2/v4/token',
  { form: {
    'code': '4/xxxxxxxxxxxxxxxxxxxx',
    'code_verifier': null,
    'client_id': 'xxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com',
    'client_secret': null,
    'redirect_uri': 'com.domain.app:/oauth2redirect',
    'grant_type': 'authorization_code'
  } },
  function (error, response, body) {
    if (!error && response.statusCode == 200) {
      console.log('Success!');
    } else {
      console.log(response.statusCode + ' ' + error);
    }

    console.log(body);
  }
);

Saya menguji dan ini bekerja dengan https://www.googleapis.com/oauth2/v4/tokendan https://accounts.google.com/o/oauth2/token.

Jika Anda menggunakan GoogleAuthorizationCodeTokenRequestgantinya:

final GoogleAuthorizationCodeTokenRequest req = new GoogleAuthorizationCodeTokenRequest(
                    TRANSPORT,
                    JSON_FACTORY,
                    getClientId(),
                    getClientSecret(),
                    code,
                    redirectUrl
);
req.set("code_verifier", null);          
GoogleTokenResponse response = req.execute();
Justin Fiedler
sumber
1

Ini adalah jawaban yang konyol, tetapi masalah bagi saya adalah saya gagal menyadari bahwa saya telah mengeluarkan token oAuth aktif untuk pengguna Google saya yang gagal saya simpan. Solusi dalam kasus ini adalah pergi ke konsol api dan mengatur ulang rahasia klien.

Ada banyak jawaban lain tentang SO untuk efek ini misalnya Atur Ulang Rahasia Klien OAuth2 - Apakah klien perlu memberikan kembali akses?

paul
sumber
1

Anda mungkin harus menghapus respons OAuth yang basi / tidak valid.

Kredit: sampel google oauth2 node.js berhenti berfungsi invalid_grant

Catatan : Tanggapan OAuth juga akan menjadi tidak valid jika sandi yang digunakan dalam otorisasi awal telah diubah.

Jika dalam lingkungan bash, Anda dapat menggunakan yang berikut ini untuk menghapus respons basi:

rm /Users/<username>/.credentials/<authorization.json>

Lindauson
sumber
1

Ada dua alasan utama untuk kesalahan invalid_grant yang harus Anda perhatikan sebelum permintaan POST untuk Refresh Token dan Access Token.

  1. Header permintaan harus berisi "content-type: application / x-www-form-urlencoded"
  2. Muatan permintaan Anda harus berupa Data Formulir yang dikodekan url, jangan kirim sebagai objek json.

RFC 6749 OAuth 2.0 mendefinisikan invalid_grant sebagai: Pemberian otorisasi yang diberikan (misalnya, kode otorisasi, kredensial pemilik sumber daya) atau token penyegaran tidak valid, kedaluwarsa, dicabut, tidak cocok dengan URI pengalihan yang digunakan dalam permintaan otorisasi, atau diberikan ke klien lain .

Saya menemukan artikel bagus lainnya, di sini Anda akan menemukan banyak alasan lain untuk kesalahan ini.

https://blog.timekit.io/google-oauth-invalid-grant-nightmare-and-how-to-fix-it-9f4efaf1da35

Hisham Javed
sumber
Apakah Anda bermaksud memposting dua jawaban yang hampir identik? Anda mungkin ingin menghapus yang ini karena yang lain memiliki baris tambahan.
Blastfurnace
1

Jika Anda mengujinya di tukang pos / insomnia dan hanya mencoba membuatnya berfungsi, petunjuk: kode auth server (parameter kode) hanya baik sekali. Artinya jika Anda memasukkan salah satu parameter lain dalam permintaan dan mendapatkan kembali 400, Anda harus menggunakan kode autentikasi server baru atau Anda hanya akan mendapatkan 400 lagi.

Jason Sultana
sumber
0

Setelah mempertimbangkan dan mencoba semua cara lain di sini, berikut ini cara saya menyelesaikan masalah di nodejs dengan googleapismodul yang berhubungan dengan requestmodul, yang saya gunakan untuk mengambil token alih-alih getToken()metode yang disediakan :

const request = require('request');

//SETUP GOOGLE AUTH
var google = require('googleapis');
const oAuthConfigs = rootRequire('config/oAuthConfig')
const googleOAuthConfigs = oAuthConfigs.google

//for google OAuth: https://github.com/google/google-api-nodejs-client
var OAuth2 = google.auth.OAuth2;
var googleOAuth2Client = new OAuth2(
    process.env.GOOGLE_OAUTH_CLIENT_ID || googleOAuthConfigs.clientId, 
    process.env.GOOGLE_OAUTH_CLIENT_SECRET || googleOAuthConfigs.clientSecret, 
    process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl);

/* generate a url that asks permissions for Google+ and Google Calendar scopes
https://developers.google.com/identity/protocols/googlescopes#monitoringv3*/
var googleOAuth2ClientScopes = [
    'https://www.googleapis.com/auth/plus.me',
    'https://www.googleapis.com/auth/userinfo.email'
];

var googleOAuth2ClientRedirectURL = process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl; 

var googleOAuth2ClientAuthUrl = googleOAuth2Client.generateAuthUrl({
  access_type: 'offline', // 'online' (default) or 'offline' (gets refresh_token)
  scope: googleOAuth2ClientScopes // If you only need one scope you can pass it as string
});

//AFTER SETUP, THE FOLLOWING IS FOR OBTAINING TOKENS FROM THE AUTHCODE


        const ci = process.env.GOOGLE_OAUTH_CLIENT_ID || googleOAuthConfigs.clientId
        const cs = process.env.GOOGLE_OAUTH_CLIENT_SECRET || googleOAuthConfigs.clientSecret
        const ru = process.env.GOOGLE_OAUTH_CLIENT_REDIRECT_URL || googleOAuthConfigs.callbackUrl
        var oauth2Client = new OAuth2(ci, cs, ru);

        var hostUrl = "https://www.googleapis.com";
        hostUrl += '/oauth2/v4/token?code=' + authCode + '&client_id=' + ci + '&client_secret=' + cs + '&redirect_uri=' + ru + '&grant_type=authorization_code',
        request.post({url: hostUrl}, function optionalCallback(err, httpResponse, data) {
            // Now tokens contains an access_token and an optional refresh_token. Save them.
            if(!err) {
                //SUCCESS! We got the tokens
                const tokens = JSON.parse(data)
                oauth2Client.setCredentials(tokens);

                //AUTHENTICATED PROCEED AS DESIRED.
                googlePlus.people.get({ userId: 'me', auth: oauth2Client }, function(err, response) {
                // handle err and response
                    if(!err) {
                        res.status(200).json(response);
                    } else {
                        console.error("/google/exchange 1", err.message);
                        handleError(res, err.message, "Failed to retrieve google person");
                    }
                });
            } else {
                console.log("/google/exchange 2", err.message);
                handleError(res, err.message, "Failed to get access tokens", err.code);
            }
        });

Saya hanya menggunakan requestuntuk membuat permintaan api melalui HTTP seperti yang dijelaskan di sini: https://developers.google.com/identity/protocols/OAuth2WebServer#offline

POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
redirect_uri=https://oauth2.example.com/code&
grant_type=authorization_code
lwdthe1
sumber
0

Coba ubah url Anda untuk permintaan menjadi

https://www.googleapis.com/oauth2/v4/token
Sergiy Voytovych
sumber
0

Untuk orang-orang yang akan datang ... Saya membaca banyak artikel dan blog tetapi beruntung dengan solusi di bawah ini ...

GoogleTokenResponse tokenResponse =
      new GoogleAuthorizationCodeTokenRequest(
          new NetHttpTransport(),
          JacksonFactory.getDefaultInstance(),
          "https://www.googleapis.com/oauth2/v4/token",
          clientId,
          clientSecret,
          authCode,
          "") //Redirect Url
     .setScopes(scopes)
     .setGrantType("authorization_code")
     .execute();

Blog ini menggambarkan kasus berbeda di mana kesalahan "invalid_grant" muncul.

Nikmati!!!

Kanishk Gupta
sumber
0

bagi saya, saya harus memastikan bahwa redirect_uriitu sama persis dengan yang ada di konsol pengembang Authorised redirect URIs, yang memperbaikinya untuk saya, saya dapat men-debug dan tahu apa sebenarnya masalahnya setelah beralih dari https://accounts.google.com/o/oauth2/tokenkehttps://www.googleapis.com/oauth2/v4/token

Saya mendapat kesalahan yang tepat:

{"error": "redirect_uri_mismatch",  "error_description": "Bad Request"}
Waqleh
sumber
0

Saya mengalami masalah ini setelah mengaktifkan API layanan baru di konsol Google dan mencoba menggunakan kredensial yang dibuat sebelumnya.

Untuk memperbaiki masalah ini, saya harus kembali ke halaman kredensial, mengklik nama kredensial, dan mengklik "Simpan" lagi . Setelah itu, saya dapat mengautentikasi dengan baik.

kimphamg
sumber
0

Dalam kasus saya, masalahnya ada di kode saya. Secara keliru saya mencoba memulai klien 2 kali dengan token yang sama. Jika tidak ada jawaban di atas yang membantu memastikan Anda tidak membuat 2 contoh klien.

Kode saya sebelum perbaikan:

def gc_service
      oauth_client = Signet::OAuth2::Client.new(client_options)
      oauth_client.code = params[:code]
      response = oauth_client.fetch_access_token!
      session[:authorization] = response
      oauth_client.update!(session[:authorization])

      gc_service = Google::Apis::CalendarV3::CalendarService.new
      gc_service.authorization = oauth_client

      gc_service
    end
primary_calendar_id = gc_service.list_calendar_lists.items.select(&:primary).first.id

gc_service.insert_acl(primary_calendar_id, acl_rule_object, send_notifications: false)

segera setelah saya mengubahnya menjadi (gunakan hanya satu contoh):

@gc_service = gc_service
primary_calendar_id = @gc_service.list_calendar_lists.items.select(&:primary).first.id

@gc_service.insert_acl(primary_calendar_id, acl_rule_object, send_notifications: false)

itu memperbaiki masalah saya dengan jenis hibah.

Kate Kasinskaya
sumber
0

Bagi saya masalahnya adalah saya memiliki banyak klien dalam proyek saya dan saya cukup yakin ini baik-baik saja, tetapi saya menghapus semua klien untuk proyek itu dan membuat yang baru dan semua mulai bekerja untuk saya (Punya ide ini dari bantuan plugin WP_SMTP forum dukungan) Saya tidak dapat menemukan tautan itu untuk referensi

Subrata Fouzdar
sumber
0

Jika Anda membersihkan masukan pengguna (Misalnya, $_GET["code"]di php) Pastikan Anda tidak mengganti sesuatu di kode secara tidak sengaja.

Regex yang saya gunakan sekarang /[^A-Za-z0-9\/-]/

nathanfranke
sumber
0

Lihat https://dev.to/risafj/beginner-s-guide-to-oauth-understanding-access-tokens-and-authorization-codes-2988 ini

Pertama, Anda memerlukan access_token:

$code = $_GET['code'];

$clientid = "xxxxxxx.apps.googleusercontent.com";
$clientsecret = "xxxxxxxxxxxxxxxxxxxxx";

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/oauth2/v4/token");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".urlencode($clientid)."&client_secret=".urlencode($clientsecret)."&code=".urlencode($code)."&grant_type=authorization_code&redirect_uri=". urlencode("https://yourdomain.com"));
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close ($ch);

$server_output = json_decode($server_output);
$access_token = $server_output->access_token;
$refresh_token = $server_output->refresh_token;
$expires_in = $server_output->expires_in;

Amankan Access Token dan Refresh Token dan expire_in, di Database. Token Akses kedaluwarsa setelah $ expires_in detik. Maka Anda perlu mengambil Token Access baru (dan mengamankannya di Database) dengan Permintaan berikut:

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://www.googleapis.com/oauth2/v4/token");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, "client_id=".urlencode($clientid)."&client_secret=".urlencode($clientsecret)."&refresh_token=".urlencode($refresh_token)."&grant_type=refresh_token");
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));

curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$server_output = curl_exec($ch);
curl_close ($ch);

$server_output = json_decode($server_output);
$access_token = $server_output->access_token;
$expires_in = $server_output->expires_in;

Ingatlah untuk menambahkan Domain redirect_uri ke Domain Anda di Konsol Google: https://console.cloud.google.com/apis/credentials di Tab "OAuth 2.0-Client-IDs". Di sana Anda juga menemukan ID Klien dan Rahasia Klien Anda.

Konstantin XFlash Stratigenas
sumber
0

Ada waktu tunggu yang tidak terdokumentasi antara saat Anda pertama kali mengalihkan pengguna ke halaman otentikasi Google (dan mendapatkan kembali kode), dan saat Anda mengambil kode yang dikembalikan dan mempostingnya ke url token. Ini berfungsi dengan baik untuk saya dengan client_id yang sebenarnya disediakan oleh google sebagai lawan dari "alamat email tidak berdokumen". Saya hanya perlu memulai prosesnya lagi.

sparkyspider
sumber