Server: Ubuntu Server 20.04 LTS on AWS EC2
PHP 7.4.3
Chrome version 101.0.4951.67
Using the client library for PHP (Installed with Composer version 2.3.5)
Should I do something with the API key in addition to OAuth authentication?
When I run the YouTube Data API listSubscriptions() method, I got the following error:
When excute Subscriptions list method of YouTube Data API,
this error happened. I'm not sure why the access was forbidden:
Fatal error: Uncaught Google\Service\Exception: { "error": { "code": 403, "message": "The request is missing a valid API key.", "errors": [ { "message": "The request is missing a valid API key.", "domain": "global", "reason": "forbidden" } ], "status": "PERMISSION_DENIED" } } in /home/ubuntu/vendor/google/apiclient/src/Http/REST.php:128 Stack trace: #0 /home/ubuntu/vendor/google/apiclient/src/Http/REST.php(103): Google\Http\REST::decodeHttpResponse() #1 [internal function]: Google\Http\REST::doExecute() #2 /home/ubuntu/vendor/google/apiclient/src/Task/Runner.php(182): call_user_func_array() #3 /home/ubuntu/vendor/google/apiclient/src/Http/REST.php(66): Google\Task\Runner->run() #4 /home/ubuntu/vendor/google/apiclient/src/Client.php(922): Google\Http\REST::execute() #5 /home/ubuntu/vendor/google/apiclient/src/Service/Resource.php(238): Google\Client->execute() #6 /home/ubuntu/vendor/google/apiclient-services/src/YouTube/Resource/Subscriptions.php(125): Google\Ser in /home/ubuntu/vendor/google/apiclient/src/Http/REST.php on line 128
Here is the PHP with the error:
userAuthorization.php
<?php
set_include_path('/home/ubuntu');
require_once 'vendor/autoload.php';
$CLIENT_ID = "xxxxxxxx";
$OAUTH2_CLIENT_SECRET = 'YYYYYY';
$id_token = $_POST['credential'];
$client = new Google_Client();
$client->setClientId($CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$payload = $client->verifyIdToken($id_token);
if ($payload) {
$userid = $payload['sub'];
echo "Token is Valid";
$client->setScopes('https://www.googleapis.com/auth/youtube.force-ssl');
$youtube = new Google_Service_YouTube($client);
$params['mine'] = 'true';
$subsclistsResponse = $youtube->subscriptions->listSubscriptions('snippet', $params);
} else {
echo "Token is Invalid";
}
I used client library for PHP. (Installed its package with Composer version 2.3.5)
I read API Guide's Subscriptions list method section,
and learned the error means "The requester is not allowed to access the requested subscriptions."(403 forbidden error)
(Of course I mean my subscription.)
What I have tried:
Until I get an error,
・I'd got an OAuth client ID from Google API Console.
Also enabled Google+ API & YouTube Data API v3.
And I added my redirect & origin URIs.
・Call requestAccessToken () and allow access in the user consent dialog
・Click the Sign in with Google button
・Then, the error occurs. Error message is "The request is missing a valid API key."
Below are the HTML of the "Login with Google" page and
a "Please permit our Access" button & a getToken () script that triggers a user consent prompt.
That's all, thanks.
<!DOCTYPE html>
<html>
<head>
<script src="https://accounts.google.com/gsi/client" onload="initClient()" async defer></script>
</head>
<body>
<script>
var client;
var access_token;
function initClient() {
<!-- Initialize a new token client with client ID of App. -->
client = google.accounts.oauth2.initTokenClient({
client_id: 'xxxxxxxx',
<!-- Configure OAuth2.0 scope which the user accesses. -->
scope: 'https://www.googleapis.com/auth/youtube.readonly \
https://www.googleapis.com/auth/youtube.force-ssl',
<!-- Google returns TokenResponse where access-token & list of access permitted by user was stored. -->
callback: (tokenResponse) => {
if (tokenResponse && tokenResponse.access_token) {
<!-- After requestAccessToken() called callback function and access-token, use hasGrantedAllScopes() to comfirm that user accepted requested scopes -->
if (google.accounts.oauth2.hasGrantedAllScopes(tokenResponse,
'https://www.googleapis.com/auth/youtube.readonly',
'https://www.googleapis.com/auth/youtube.force-ssl')){
}
<!-- return access-token -->
access_token = tokenResponse.access_token;
}
},
});
}
<!-- get access-token -->
function getToken() {
client.requestAccessToken();
}
</script>
<!-- Configure & display Sign in with Google button (If clicked, jump to URL spicified with "data-login_uri" ) -->
<div id="g_id_onload"
data-client_id="xxxxxxxx"
data-context="signin"
data-ux_mode="redirect"
data-login_uri="https://xxxxxx/gcp/userAuthorization.php"
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-type="standard"
data-shape="rectangular"
data-theme="outline"
data-text="$ {button.text}"
data-size="large"
data-logo_alignment="left">
</div>
<!-- With user's click trigger getting token -->
<button onclick="getToken();">Please permit our Access</button><br><br>
</body>
</html>
First, I guessed something was wrong with "scope" when the error happened.
So I implemented "User's Consent dialog",
and called requestAccessToken() of client library.
"User's Consent dialog" is successfully triggered when HTML button is clicked. In the dialog displayed some scopes like figure2 in this article: How user authorization works | Google Identity Services JavaScript SDK | Google Developers[^]
(I did this following
Migrate to Google Identity Services | Google Identity Services JavaScript SDK | Google Developers[^] )
In addition, before recieving 403 forbidden error, I'd already coded some scripts:
I implemented rendering "Sign in with Google" by following google's document (Migrating from Google Sign-In | Sign In With Google | Google Developers[^][^])
note
・At "HTML and JavaScript" section in "Migrating from Google Sign-In" document, I coded as a redirectmode; And it's successfully redirected to PHP login-endpoint page.
・At PHP, it seemed I successfully get a valid ID token,
because verifyIdToken() returned payload.
(I followed this article:Verify the Google ID token on your server side | Sign In With Google | Google Developers[^])
userAuthorization.php
<?php
set_include_path('/home/ubuntu');
require_once 'vendor/autoload.php';
$CLIENT_ID = "xxxxxxxx";
$OAUTH2_CLIENT_SECRET = 'YYYYYY';
$id_token = $_POST['credential'];
$client = new Google_Client();
$client->setClientId($CLIENT_ID);
$client->setClientSecret($OAUTH2_CLIENT_SECRET);
$payload = $client->verifyIdToken($id_token);
if ($payload) {
$userid = $payload['sub'];
echo "Token is Valid";
$client->setScopes('https://www.googleapis.com/auth/youtube.force-ssl');
$youtube = new Google_Service_YouTube($client);
$params['mine'] = 'true';
$subsclistsResponse = $youtube->subscriptions->listSubscriptions('snippet', $params);
} else {
echo "Token is Invalid";
}