How can I a Google Api restful endpoint using service key? - rest

I'm using postman to memic a restful api call and trying to access google sheets API end point. When I try to access my endpoint it returns:
{
"error": {
"code": 403,
"message": "The request is missing a valid API key.",
"status": "PERMISSION_DENIED"
}
}
which is fair enough as I did not use my API key. I created a service account and got a json file, but I plan to access using a rest endpoint so need to pass token in header but I'm not sure how.
I looked at the json file and wasn't sure what to extract in order to pass it for my rest call.
Has anyone been able to do this successfully?

Before calling Google Services from Postman, you would need to re-create the flow for getting an access token form service account credentials :
build and encode the JWT payload from the data from credentials files (to populate aud, iss, sub, iat and exp)
request an access token using that JWT
make the request to the API using this access token
You can find a complete guide for this flow is located here: https://developers.google.com/identity/protocols/oauth2/service-account#authorizingrequests
Here is an example in python. You will need to install pycrypto and pyjwt to run this script :
import requests
import json
import jwt
import time
#for RS256 you may need this
#from jwt.contrib.algorithms.pycrypto import RSAAlgorithm
#jwt.register_algorithm('RS256', RSAAlgorithm(RSAAlgorithm.SHA256))
token_url = "https://oauth2.googleapis.com/token"
credentials_file_path = "./google.json"
#build and sign JWT
def build_jwt(config):
iat = int(time.time())
exp = iat + 3600
payload = {
'iss': config["client_email"],
'sub': config["client_email"],
'aud': token_url,
'iat': iat,
'exp': exp,
'scope': 'https://www.googleapis.com/auth/spreadsheets'
}
jwt_headers = {
'kid': config["private_key_id"],
"alg": 'RS256',
"typ": 'JWT'
}
signed_jwt = jwt.encode(
payload,
config["private_key"],
headers = jwt_headers,
algorithm = 'RS256'
)
return signed_jwt
with open(credentials_file_path) as conf_file:
config = json.load(conf_file)
# 1) build and sign JWT
signed_jwt = build_jwt(config)
# 2) get access token
r = requests.post(token_url, data= {
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": signed_jwt.decode("utf-8")
})
token = r.json()
print(f'token will expire in {token["expires_in"]} seconds')
at = token["access_token"]
print(at)
Note the value of the scope: https://www.googleapis.com/auth/spreadsheets
Probably, you can do all the above flow using Google API library depending on what
programming language you prefer
The script above will print the access token :
ya29.AHES67zeEn-RDg9CA5gGKMLKuG4uVB7W4O4WjNr-NBfY6Dtad4vbIZ
Then you can use it in Postman in Authorization header as Bearer {TOKEN}.
Or using curl :
curl "https://sheets.googleapis.com/v4/spreadsheets/$SPREADSHEET_ID" \
-H "Authorization: Bearer $ACCESS_TOKEN"
Note: you can find an example of using service account keys to call Google translate API here

Related

connect to REST endpoint using OAuth2

I am trying to explore different options to connect to a REST endpoint using Azure Data Factory. I have the below python code which does what I am looking for but not sure if Azure Data Factory offers something out of the box to connect to the api or a way to call a custom code.
Code:
import sys
import requests
from requests_oauthlib import OAuth2Session
from oauthlib.oauth2 import BackendApplicationClient
import json
import logging
import time
logging.captureWarnings(True)
api_url = "https://webapi.com/api/v1/data"
client_id = 'client'
client_secret = 'secret'
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)
token = oauth.fetch_token(token_url='https://webapi.com/connect/accesstoken', client_id=client_id, client_secret=client_secret)
client = OAuth2Session(client_id, token=token)
response = client.get(api_url)
data = response.json()
When I look at the REST linked service I don't see many authentication options
Could you please point to me on what activities to use to make OAuth2 working in Azure Data Factory
You would have to use a WebActivity to call using POST method and get the authentication token before getting data from API.
Here is an example.
First create an Web Activity.
Select your URL that would do the authentication and get the token.
Set Method to POST.
Create header > Name: Content-Type Value: application/x-www-form-urlencoded
Configure request body for HTTP request.
..
Format: grant_type=refresh_token&client_id={client_id}&client_secret=t0_0CxxxxxxxxOKyT8gWva3GPU0JxYhsQ-S1XfAIYaEYrpB&refresh_token={refresh_token}
Example: grant_type=refresh_token&client_id=HsdO3t5xxxxxxxxx0VBsbGYb&client_secret=t0_0CqU8oA5snIOKyT8gWxxxxxxxxxYhsQ-S1XfAIYaEYrpB&refresh_token={refresh_token
I have shown above for example, please replace with respective id and secret when you try.
As an output from this WebActivity, you would receive a JSON string. From which you can extract the access_token to further use in any request header from further activities (REST linked service) in the pipeline depending on your need.
You can get the access_token like below. I have assigned it to a variable for simplicity.
#activity('GetOauth2 token').output.access_token
Here is an example from official MS doc for Oauth authentication implementation for copying data.

Generate access token using JWT

I've been given access to an okta token endpoint. I would like to use this service to request a token. I was given a url, client id, client secret, scope and grant type. I can use postman to make a POST call to the url (/v1/token) and pass the above info (client id, client secret, scope and grant type) and I get an access token back.
I can easily make this call in java with RestTemplate or equivalent, but I would like to use an API that would manage the token for me.
I've found JJWT. All the examples I see out there show me how to create a JWT using JJWT. What I would like to do is to get my access token, but I'm not sure how to do that. I mean i get that JJWT is an API to create JWT, but then how can I use the JWT to get my access token?
Any help/clarification/direction is much appreciated.
We using JWT with the node.js, to create new Token jwt.sign(data, key) takes at least to an argument, the fist must be some credential like userId, email..., the second will be key to verify later. to verify the token is it valid we use jwt.verify(), the first argument is token (where the jwt.sing() give you) and the second is the key (where you provide when creating);
example:
Creating JWT token:
var jwt = require('jsonwebtoken');
cosnt token = jwt.sign({ email: 'test#test.com', userId: '993333' }, 'secretkey');
verifying Token:
try {
const decodedToken = jwt.verify(token, 'secretkey');
}
catch(err) {
throw new Error(err)
}
// once verified
conosole.log(decodedToken)
I found this post how to create and verify token using java, thanks!

Gsuite Directory API 403 Error on API call or Grant Type Error on JWT Generation

Using Python and creating my own JWT using HTTP/Rest methodology, I simply can't get delegation to work.
On one hand, google JWT troubleshoot documentation says that ISS needs to be the same as the SUB (the service account).
However, on the server to server oauth2 documentation, it says that to impersonate an account, the sub needs to be the account I am attempting to impersonate in the claim.
Needless to say, despite enabling domain-wide delegation, adding the correct scopes, etc, I get nothing back but 403 when attempting to access the user domain utilizing the requests library in python with the following example:
> requests.get("https://www.googleapis.com/admin/directory/v1/users/useremail#/
> google.org",headers={'Authorization':f' Bearer {oauth2tokenhere}'})
Here is an example of my claim:
> claim = { "iss": 'serviceaccountemail',
> 'sub': 'impersonatedaccountemail',
> 'scope': 'https://www.googleapis.com/auth/admin.directory.user.readonly',
> 'exp': ((datetime.datetime.today() + datetime.timedelta(minutes=60)).timestamp()),
> 'iat': ((datetime.datetime.today()).timestamp()),
> 'aud': "https://oauth2.googleapis.com/token"
> }
The above claim will generate a generalized grant error (cute, but not helpful).
If I change the claim and ensure that the sub and the iss are the same, the oauth2token generates, but I get a 403 error when attempting to hit the API.
Here is the server to server oauth2 documentation stating the sub should be the
account the service account is attempting to impersonate.
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
Here is the troubleshooting article outlining the ISS/Sub being the same (although cloud article is the closest relevant topic I could find)
https://cloud.google.com/endpoints/docs/openapi/troubleshoot-jwt
EDIT:
I am utilizing the service account information from the downloaded .json file that is downloaded when creating the service account file.
import json as j
import datetime
import jwt
import requests
#creates the claim, 'secret' (from the private key), and the kid, from the service account file, and returns these values in a tuple.
#the tuple can then be used to make dependable positional argument entries to the parameters of the createJWT function.
def create_claim_from_json(self,objpath,scope=["https://www.googleapis.com/auth/admin.directory.user.readonly" "https://www.googleapis.com/auth/admin.directory.user"]):
with open(f'{objpath}','r') as jobj:
data = j.load(jobj)
claim = {
"iss": str(data['client_id']),
"sub": str(data['client_id']),
"scope": str(scope),
"exp": ((datetime.datetime.today() + datetime.timedelta(minutes=59)).timestamp()),
"iat": ((datetime.datetime.today()).timestamp()),
"aud": "https://oauth2.googleapis.com/token"
}
private_key = data['private_key']
kid = {"kid": f"{data['private_key_id']}"}
return claim, private_key, kid
#assembles the JWT using the claim, secret (Private key from the Service account file), the kid value, and the documented RS256 alg.
#returns the completed JWT object back to be used to send to the oauth2 endpoint
#the JWT will be used in the function call retrieve_oauth2token.
def createJWT(self, claim, secret, kid, alg='RS256'):
encoded_jwt = (jwt.encode(claim, secret, alg, kid)).decode('UTF-8')
return encoded_jwt
#Using the JWT created in memory, sends the JWT to the googleapi oauth2 uri and returns a token
def retrieve_oauth2token(self, jwt):
oauth2 = requests.post(f'https://oauth2.googleapis.com/token?grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant- type%3Ajwt-bearer&assertion={jwt}')
oauth2=oauth2.json()
return oauth2 #['access_token'], oauth2['token_type']
The documentation has a clear overview, did you follow the steps as described in the addendum? I am missing some parts of your code. But you did not mention using a service account (json) key. And the documentation also show that you have to use the (delegated) service account as both iss and sub. Furthermore, you need to use a kid. This is how it is done:
payload = {
'iss': '123456-compute#developer.gserviceaccount.com',
'sub': '123456-compute#developer.gserviceaccount.com',
'aud': 'https://firestore.googleapis.com/',
'iat': time.time(),
'exp': iat + 3600
}
additional_headers = {'kid': PRIVATE_KEY_ID_FROM_JSON}
signed_jwt = jwt.encode(payload, PRIVATE_KEY_FROM_JSON, headers=additional_headers, algorithm='RS256')
url = "URL OF THE API TO CALL"
header = {'Authorization': f'Bearer {signed_jwt}'}
resp = requests.get(url, headers=header)
Note: you can find PRIVATE_KEY_FROM_JSON in the private_key_id field of your service account JSON credentials file.

Not able to set generated access_token in request object

I am authenticating user with the help of JWT token and #nestjs/passport library. I am able to successfully generate and return the access token but stuck at how to set it in the req object. So that I can validate other api routes until user logs out
Below code shows how I generated the access_token and returned to the calling point
let payload = { username: user.email, sub: user.id }
const accessToken = this.jwtService.sign(payload);
return {
expires_in: 3600,
access_token: accessToken,
status: 200
}
I am getting the access_token correctly. I verified generated access_token using https://jwt.io/ debugger and results were correct.
Can someone help me in setting up the req object with access_token?
There is multiple way you could send JWT token in your requests. If you have followed this tutorial: https://docs.nestjs.com/techniques/authentication, you are using passport-jwt library for JWT. One way is through header if you don't set auth schema it will be this one:
Authorization: JWT <-HERE YOU SHOULD INSERT TOKEN->
If you set authSchema to be bearer, then you could use this header like this:
Authorization: BEARER <-HERE YOU SHOULD INSERT TOKEN->
Except for that you can put your JWT into URL like this:
https://example.com/me?auth_token=<-HERE YOU SHOULD INSERT TOKEN->
Or in object body that you are sending with property named auth_token:
https://example.com/me
Body:
{
auth_token: <-HERE YOU SHOULD INSERT TOKEN->
}
You can find this in the source code of passport-jwt in this file.

"invalid signature" JWT Token Opentok

I am trying to use the Opentok REST API with JWT to generate a video session token. I am using the following credentials to create the JWT following the JSONWebToken documentation at https://github.com/kylef/JSONWebToken.swift.
I have used the generated token for authorisation and followed the documentation at https://tokbox.com/developer/rest/#authentication and called the api from postman, but I am getting Invalid Signature error message. Where am i wrong?
var claims = ClaimSet()
claims["iss"] = "3*****2"
claims["ist"] = "account"
claims["iat"] = (Calendar.current.date(byAdding: .minute, value: 330, to: Date())?.timeIntervalSince1970)!
claims["exp"] = (Calendar.current.date(byAdding: .minute, value: 334, to: Date())?.timeIntervalSince1970)!
claims["jti"] = "\(NSUUID.init())"
claims["aud"] = "www.example.com"
let jToken = JWT.encode(claims: claims, algorithm: .hs256("334******************************d5af".data(using: .utf8)!))
print(jToken)
Hi Ram you are calling the session/create rest api. This api requires a project level authentication, however from you claims it looks like you are trying to generate a account level token.
The iss should be your API Key/ Project Id and the ist should be the string project.
Let me know if this helps and works.
I'm sure you figured your issue out by now but the archive mode and p2p.preference are not supposed to be header attributes but part of the body as json
At the Value field of "X-OPENTOK-AUTH" you first have to Write: "Bearer " + token.
If it doesn't work try changing "X-OPENTOK-AUTH" with "Authorization".