I am trying to access the Google Calendar API using VBScript and Oauth offline access. I managed to get a refresh token and an access token, and now I want to get to the step where I actually use the API, using the following code:
sUrl="https://www.googleapis.com/calendar/v3/users/me/calendarList"
sRequest = "access_token="&accesstoken
response=HTTPPost (sUrl, sRequest)
Function HTTPPost(sUrl, sRequest)
set oHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oHTTP.open "POST", sUrl,false
oHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oHTTP.setRequestHeader "Content-Length", Len(sRequest)
oHTTP.send sRequest
HTTPPost = oHTTP.responseText
End Function
The response I get is :
{
"error": {
"errors": [
{
"domain": "global",
"reason": "authError",
"message": "Invalid Credentials",
"locationType": "header",
"location": "Authorization"
}
],
"code": 401,
"message": "Invalid Credentials"
}
}
This is even though the access token is valid - If I change sURL to https://www.googleapis.com/oauth2/v1/tokeninfo and run the script I get the following response showing a valid token:
{
"issued_to": "...",
"audience": "...,
"scope": "https://www.googleapis.com/auth/calendar",
"expires_in": 2568,
"access_type": "offline"
}
And also if I paste the URL https://www.googleapis.com/calendar/v3/users/me/calendarList?access_token=... into a browser I get a valid response from Google listing the user's calendars
401: Invalid Credentials
Invalid authorization header. The access token you're using is either
expired or invalid.
Access tokens are only good for 1 hour. After the hour is up the access token has expired and you need to use the refresh token to request a new one.
The raw code to get a new acesss token looks something like this. the key is grant_type=refresh_token this tells the authentication server you are requesting a new token.
https://accounts.google.com/o/oauth2/token
client_id={ClientId}.apps.googleusercontent.com&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
Your code uses HTTP POST but use an HTTP GET request is required as specified here: https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list. Also, you can provide the access token in the access_token parameter (and not in the oauth_token parameter as suggested in the other answer), but another option is to provide it in an Authorization header as in:
Authorization: Bearer <token>
As an example, a token that returns from:
curl -v https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=<token>`
the following introspection result:
{
"issued_to": "<...>.apps.googleusercontent.com",
"audience": "<...>.apps.googleusercontent.com",
"user_id": "<...>",
"scope": "<> https://www.googleapis.com/auth/calendar",
"expires_in": 3527,
"email": "<...>",
"verified_email": true,
"access_type": "offline"
}
can be used in against the Calendar API URL:
curl -v -H "Authorization: Bearer <>" https://www.googleapis.com/calendar/v3/users/me/calendarList
so that it gives me a list of JSON calendar data:
{
"kind": "calendar#calendarList",
"etag": "\"1420511621463000\"",
"nextSyncToken": "00001420511621463000",
"items": [
{
"kind": "calendar#calendarListEntry",
"etag": "\"1418127331044000\"",
...
I managed to figure this out - the calendarList API requires sending an empty body, therefore I change the code to:
set oHTTP = CreateObject("MSXML2.ServerXMLHTTP")
oHTTP.open "GET", sUrl&srequest,false
oHTTP.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
oHTTP.send
getcal = oHTTP.responseText
Related
API is working absolutely fine in graphql playground.
But when I tested the same API with the same data in postman it gave me an error.
{
"errors": [
{
"message": "Context creation failed: Permission denied",
"statusCode": 500
}
]
}
I was passing the access token in the header
like:
{"authorization":"Bearer accesstoken"}
In terms of postman data is going in this format
authorization: Bearer accesstoken
The issue is with the white space in the Bearer and access token
Is there any way we can pass a value with whitespaces in header?
I have tried using %20 and _.
I did request:
{
"grant_type": "client_credentials",
"client_id": 8,
"client_secret": "XXNKXXqJjfzG8XXSvXX1Q4pxxnkXmp8tT8TXXKXX",
"redirect_uri": "",
"scope": "*"
}
Then received response below:
{
"token_type": "Bearer",
"expires_in": 31536000,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGcXXXJSUzI1NiIsImp0aSI6ImEzYjJhZmU5OTYzMTE5MDAyZDAwZmEzNzU0ZGY3ZTRkMTgwYzhlYWRmNGQyOGU5MTI4YjAyYjJmYWQxZjY1NjUzMzAyZjNlZTI4MTgxMDFhIn0.eyJhdWQiOiI4IiwianRpIjoiYTNiMmFmZTk5NjMxMTkwMDJkMXXXYTM3XXXkZjdlNGQxODBjOGVhZGY0ZDI4ZTkxMjhiMDJiMmZhZDFmNjU2NTMzMDJmM2VlMjgxODEwMWEiLCJpYXQiOjE1NDAwMTQxNTUsIm5iZiI6MTU0MDAxNDE1NSwiZXhwIjoxNTcxNTUwMTU1LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.sNSYywfBf27yAojqZclpjliysbQARlYFktzanTMecXXXIai5DgJY0sKhGpHktP5cqirYdemoFKy2nOxzZ8g29gCQQ63zmxe3vpbDz1GAdrjCDWoUlwSXXXHx4VIsdSIzVdi9XyvPKaLKMdoL6nFeWgpgXKGIvHKdiHjKgQbY_08Qa6JMN5Up27qmIOQoXJNAf1nuXvBMabUU_Js7VNspwPfdC8nMZ5zhK1A_c32_lDRtHqkhDfqqBXdUB-inx-zixhn2ODC4b4tkdj7XXXXlVKFxHxKM3aVOMFlmKhypSDwIUB0dPsN8iHcLzkl1yjzRQcOvQEj5BXWLkLCPdkiX2YJuFiWGUm_nxiYoIRV3ptJDeBI5OJI870JTOwBfJePrHTbXmhbjNSQSflLtiOV34wbPQZWH3KMKcsGVYvXXX3rcO5cbZWeeJLGPPYYO-_AWDmdAm-Qsb6Tw1sPxEZRw0dw3zBHnLVrEK9GXXXN2U5wE9Ka3id8ecOJSXSD39X1PyZUB9dJTidmbiWYWgskSTsqLuWfzXXXtlXkb1iOO37kT_Y5zr71Wp1RJ1Fp38yIyHI6fR9hKqeNALSqhv2ALmcSMQsFGTtPG98lGulu-vRJJhgMJ3C3fSTljN7o9BM7Jz-h0ymxC8sSMSNsXakK1qu40vD40zRJMB09sBPjIAVo"
}
which i suppose to use it in authorization header to consume my API, as follow:
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGcXXXJSUzI1NiIsImp0aSI6ImEzYjJhZmU5OTYzMTE5MDAyZDAwZmEzNzU0ZGY3ZTRkMTgwYzhlYWRmNGQyOGU5MTI4YjAyYjJmYWQxZjY1NjUzMzAyZjNlZTI4MTgxMDFhIn0.eyJhdWQiOiI4IiwianRpIjoiYTNiMmFmZTk5NjMxMTkwMDJkMXXXYTM3XXXkZjdlNGQxODBjOGVhZGY0ZDI4ZTkxMjhiMDJiMmZhZDFmNjU2NTMzMDJmM2VlMjgxODEwMWEiLCJpYXQiOjE1NDAwMTQxNTUsIm5iZiI6MTU0MDAxNDE1NSwiZXhwIjoxNTcxNTUwMTU1LCJzdWIiOiIiLCJzY29wZXMiOltdfQ.sNSYywfBf27yAojqZclpjliysbQARlYFktzanTMecXXXIai5DgJY0sKhGpHktP5cqirYdemoFKy2nOxzZ8g29gCQQ63zmxe3vpbDz1GAdrjCDWoUlwSXXXHx4VIsdSIzVdi9XyvPKaLKMdoL6nFeWgpgXKGIvHKdiHjKgQbY_08Qa6JMN5Up27qmIOQoXJNAf1nuXvBMabUU_Js7VNspwPfdC8nMZ5zhK1A_c32_lDRtHqkhDfqqBXdUB-inx-zixhn2ODC4b4tkdj7XXXXlVKFxHxKM3aVOMFlmKhypSDwIUB0dPsN8iHcLzkl1yjzRQcOvQEj5BXWLkLCPdkiX2YJuFiWGUm_nxiYoIRV3ptJDeBI5OJI870JTOwBfJePrHTbXmhbjNSQSflLtiOV34wbPQZWH3KMKcsGVYvXXX3rcO5cbZWeeJLGPPYYO-_AWDmdAm-Qsb6Tw1sPxEZRw0dw3zBHnLVrEK9GXXXN2U5wE9Ka3id8ecOJSXSD39X1PyZUB9dJTidmbiWYWgskSTsqLuWfzXXXtlXkb1iOO37kT_Y5zr71Wp1RJ1Fp38yIyHI6fR9hKqeNALSqhv2ALmcSMQsFGTtPG98lGulu-vRJJhgMJ3C3fSTljN7o9BM7Jz-h0ymxC8sSMSNsXakK1qu40vD40zRJMB09sBPjIAVo
Accept:application/json
Content-Type:application/json
I'm able to catch the bearer token with: $token = $request->bearerToken(); in my controller
But I have difficulties to validate it, anyone know how to validate that token?
what i'm trying to achieve is quite simple, if the Bearer token valid then continue... if not then throw 403.
**I'm not using user authentication (no user) so I can't put middleware Auth::api in route (unless you tell me this is possible to validate token only without user).*
Try this and let me know if any error occurs.
Add the following in App/Http/Kernel.php file in protected $routeMiddleware array:
'client.credentials' => \Laravel\Passport\Http\Middleware\CheckClientCredentials::class,
Now in your controller add the following in the beginning:
public function __construct()
{
$this->middleware('client.credentials')->only(['']);
}
add your function names in the array to check if the request has an Authorization header with a token for the function.You do not need to do anything else.
You can check after, using postman with an incorrect token an error will return from the api as the result.
Also as a tip , do not put your "client_secret" on client side , it should not get in the wrong hands.
Use the following to generate a token for the user:
$user = User::where('email',$email)->where('password',$password)->firstOrFail();
$token = $user->createToken('Token')->accessToken;
POST request works fine for Lambda function and API. However, when I hit it through Postman then it gives 502 internal Gateway error.
When I print request in Clouwatch logs for Postman POST request, I get the following:
{
"resource": "/{proxy+}",
"path": "/entitlement",
"httpMethod": "POST",
"body": "ew0KICAgICJsYXN0UmVuZXdhbCI6IDE1MjcxNzY2Njg3OTUsDQogICAgInNvbGRUbyI6ICIwMDAxNjUyNDUzIiwNCiAgICAic3RhcnREYXRlIjogMTUyNzE3NjY2ODc5NSwNCiAgICAiZXhwaXJhdGlvbkRhdGUiOiAxNTI5NzY4NjY4Nzk1LA0KICAgICJhY3RpdmF0aW9uQ29kZSI6ICIxMTExMTExMTExMTExMTExIiwNCiAgICAicXVhbnRpdHkiOiAwLA0KICAgICJ2ZXJzaW9uIjogIjIxOSIsDQogICAgInRlcm0iOiAibW9udGhseSIsDQogICAgImFjdGl2ZSI6IHRydWUsDQogICAgIndlYmtleSI6ICJuZWhhLmNoaW5jaG9yZUBzaWVtZW5zLmNvbSIsDQogICAgInByb2R1Y3RTa3UiOiAiU0U5MTAtSVREIiwNCiAgICAiY3VzdG9tZXJJZCI6IG51bGwsDQogICAgImZpcnN0TmFtZSI6ICJUSElSRCIsDQogICAgImxhc3ROYW1lIjogIlRFU1QiLA0KICAgICJjb21wYW55TmFtZSI6ICJUaGlyZHRlc3QiLA0KICAgICJjYW5jZWxsYXRpb25EYXRlIjogbnVsbCwNCiAgICAiZW1haWwiOiAibmVoYS5jaGluY2hvcmVAc2llbWVucy5jb20iDQp9",
"isBase64Encoded": true
}
When I print request in logs for API Gateway, I get the following:
{
"resource": "/{proxy+}",
"path": "/entitlement",
"httpMethod": "POST",
"body": "{\"lastRenewal\":1532500221761,\"soldTo\":\"0001652453\",\"startDate\":1532500221761,\"expirationDate\":1535178621761,\"activationCode\":\"0449835557734402\",\"quantity\":0,\"version\":\"219\",\"term\":\"monthly\",\"active\":true,\"customerId\":null,\"firstName\":\"THIRD\",\"lastName\":\"TEST\",\"companyName\":\"Thirdtest\",\"cancellationDate\":null,\"email\":\"abc#xyz.com\"}",
"isBase64Encoded": false
}
GET request works fine. Face issue only for POST request.
In 'API-Gateway' console, expand API and select 'Settings' tab.
There is a field for 'Binary Media type', which was set to "*/*', due to which 'isBase64Encoded' is set to true in POST request.
However, my application was posting only json data in request body, so there is no need for 'binary support'. So we removed this field and it worked fine.
If you are posting binary data(images/files), in that case set 'Binary Media type' to respective file support type.
I'm reporting states for devices using http post with a jwt generated using service account. Below is the payload for jwt
{
"iss": "<service-account-email>",
"scope": "https://www.googleapis.com/auth/homegraph",
"aud": "https://accounts.google.com/o/oauth2/token",
"iat": <current-time>,
"exp": <current-time-plus-one-hour>
}
after this I sign the jwt using the private key for my service account using the python library google.auth.crypt.RSASigner.from_service_account_file(path)
and generate the jwt token. I am further using this token to obtain the access token from https://accounts.google.com/o/oauth/token, which is also successful.
After obtaining the access token I am making a post request to
https://homegraph.googleapis.com/v1/devices:reportStateAndNotification?key=api_key
with headers
{"Authorization": "Bearer <token>", "X-GFE-SSL": "yes", "Content-Type": "application/json"}
and json data
{ "requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf", "agent_user_id": "1234", "payload": { "devices": { "states": { "1458765": { "on": true }, "4578964": { "on": true, "isLocked": true } } } } }
But this gives me
{'error': {'code': 403, 'message': 'The request is missing a valid API key.', 'status': 'PERMISSION_DENIED'}}
I followed the steps from
https://developers.google.com/actions/smarthome/report-state
is there anything i'm doing wrong? or am I missing any steps?
UPDATE:
I added the api key to the uri now it gives me another error in response
{'error': {'code': 403, 'message': 'The caller does not have permission', 'status': 'PERMISSION_DENIED'}}
how do I resolve this issue?
In order to report the state to the Home Graph you have to:
Create a Service Account for the token creation from your SmartHomeApp project:
Go to APIs & Services => Click on Credentials
Click on Create credentials => Click on Service account key
Fill with your data creating a new New service account => Select the role Service Accounts > Service Account Token Creator
Download the JSON file with your keys and certificate
Get a valid signed JWT token:
credentials = service_account.Credentials.from_service_account_file(service_account_file, scopes="https://www.googleapis.com/auth/homegraph")
now = int(time.time())
expires = now + 3600 # One hour in seconds
payload = {
'iat': now,
'exp': expires,
'aud': "https://accounts.google.com/o/oauth2/token",
'scope': SCOPE,
'iss': credentials.service_account_email
}
signed_jwt = google.auth.jwt.encode(credentials.signer, payload)
Obtain a valid Access token:
headers = {"Authorization": "Bearer {}".format(signed_jwt.decode("utf-8")), "Content-Type": "application/x-www-form-urlencoded"}
data = {"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": signed_jwt}
access_token = requests.post("https://accounts.google.com/o/oauth2/token", data=data, headers=headers).get("access_token")
Send the reported states:
headers = {"Authorization": "Bearer {}".format(access_token), "X-GFE-SSL": "yes"}
data = {"requestId": request_id, "agent_user_id": agent_user_id, "payload": {"devices": {"states": states}}}
requests.post("https://homegraph.googleapis.com/v1/devices:reportStateAndNotification", data=json.dumps(data), headers=headers)
NOTE: in order to work these snippets require to import google.auth.jwt, from google.oauth2 import service_accountand import requests from google-auth, google-auth-httplib2 and requests packages.
I am using the google sheets API with PHP and followed the quick start guide that can be found over here https://developers.google.com/sheets/quickstart/php
When I do authorize properly and store the follow json file in a speicfied path
{
"access_token": "xxxxxxx",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "xxxxxx",
"created": 1472731452
}
After this expires the following gets triggered in my code
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken(
$client->getRefreshToken()
);
$this->filesystem
->put(
self::CREDENTIALS,
json_encode($client->getAccessToken())
);
}
Now my issue is when that code gets triggered it will update my file to something like the following.
{
"access_token": "xxxxxxx",
"token_type": "Bearer",
"expires_in": 3600,
"created": 1472731452
}
As you can see there is no refresh token anymore. When this token expires I start getting the following error
[LogicException]
refresh token must be passed in or set as part of setAccessToken
Which is perfactly understandable because I don't have the refresh token there anymore.
My question is why is the refresh token getting removed? I am call the same methods as the one in the quick start guide https://developers.google.com/sheets/quickstart/php
I am talking specificaly about this part in the guide
// Refresh the token if it's expired.
if ($client->isAccessTokenExpired()) {
$client->fetchAccessTokenWithRefreshToken($client->getRefreshToken());
file_put_contents($credentialsPath, json_encode($client->getAccessToken()));
}
Your refresh token expired because the lifespan set in your code was 3600 seconds only/1 hour.
{
"access_token": "xxxxxxx",
"token_type": "Bearer",
"expires_in": 3600, //refresh_token good for 1 hour
"refresh_token": "xxxxxx",
"created": 1472731452
}
Using a refresh token
A refresh token is obtained in offline scenarios during the first authorization code exchange. In these cases, your application may obtain a new access token by sending a refresh token to the Google OAuth 2.0 Authorization server.
To obtain a new access token this way, your application sends an HTTPS POST request to https://www.googleapis.com/oauth2/v4/token. The request must include the following parameters:
Such a request will look similar to the following:
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
client_id=8819981768.apps.googleusercontent.com&
client_secret={client_secret}&
refresh_token=1/6BMfW9j53gdGImsiyUH5kU5RsR4zwI9lUVX-tqf8JXQ&
grant_type=refresh_token
Check this SO thread for additional reference.
Hope this helps!