Getting 401 Client Error when using firebase - python

I'm new to firebase and I'm trying to update some data in an existing project but I'm getting the following error: 401 Client Error: Unauthorized for url. So how to fix this problem and thank you! I just want to mention also that I have been added to the project, so I'm not the owner.
this is my code:
from firebase import firebase
firebase = firebase.FirebaseApplication("the http path", None) # None bcz we are testing
firebase.put("/esco-lebanon/device-configs/atest-dev", "brightness", 50)
print("Updated")

According to the documentation, a 401 error means one of the following:
The auth token has expired.
The auth token used in the request is invalid.
Authenticating with an access_token failed.
The request violates your Firebase Realtime Database Rules.
The understanding here is that your client code needs to correctly identify a Firebase Authentication user with a token in access_token, and that user account must have access to read the data in the database according to its security rules. Since you haven't provided a token, your access is coming in anonymously, so you could only query data where security rules allow universal access.
If you haven't investigated using authentication in your request, you should read the documentation about that.

Related

unauthorized_client error when trying to impersonate an account

On Google Business Profile API (Google MyBusiness), I am getting following error when I try to impersonate with an email address:
('unauthorized_client: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.', {'error': 'unauthorized_client', 'error_description': 'Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.'})
I am trying to achieve this using a Python script which is using Google API Client Library:
file_location = '/some/folder/file.json'
impersonated_email = 'abc#abc.com'
scope = ["https://www.googleapis.com/auth/business.manage"]
credentials = ServiceCredentials.from_service_account_file(file_location, scopes=scope, subject=impersonated_email)
If I try to access the endpoint without using the subject parameter, it is working. But of course the main purpose here is impersonating. And the Google documentation says it is possible to impersonate with the subject parameter (or via with_subject function).
By the way, if I tried some invalid mail address, my error is changing to: ('invalid_grant: Invalid email or User ID', {'error': 'invalid_grant', 'error_description': 'Invalid email or User ID'})
So I assume that I can get the user credentials; but have no idea what could have been wrong.
Does anybody have any idea about the issue? What could be the possible issues?
Well, Google explains that JWT error code like that:
error field: unauthorized_client
error_description field: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
Meaning:
A service account was authorized using the client email address rather than the client ID (numeric) in the Admin console.
How to resolve:
In the Domain-wide delegation page in the Admin console, remove the client, and re-add it with the numeric ID.
I tried that but I still got the error.
Then, after some digging; I saw that you only can impersonate G-Suite accounts.
So, after that I tried same code with another real-person mail which is a G-Suite account and it worked.

Firebase admin SDK Python - cannot verify custom tokens

I'm trying to play with the firebase admin sdk for python for making custom tokens and verify those while testing my app. Problem is that while I try to verify the token I always get such an error:
ValueError: Firebase ID token has incorrect "aud" (audience) claim. Expected "my_project_id" but got "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit". Make sure the ID token comes from the same Firebase project as the service account used to authenticate this SDK. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
I followed the guide to create the app and making the tokens:
import firebase_admin
from firebase_admin import auth, credentials
cred = credentials.Certificate('/path/to/file.json')
app = firebase_admin.initialize(cred)
custom_token = auth.create_custom_token('some-uid', app=app)
auth.verify_id_token(custom_token, app=app)
and here I get the error. It seems that _TokenGenarator is initialised with the defaults that are coming back from the error. I thought when passing the app it should automatically change those but it's not happening. Am I missing something?
verify_id_token() only accepts ID tokens. Custom tokens do not fall into that category. See this test case. Raising a ValueError is the expected behavior in this case.
ID tokens can be obtained from a client SDK. You can exchange a custom token for an ID token by calling one of the provided signInWithCustomToken() methods.

Verify Firebase idToken via Python

I'm using Firebase for the first time and am unable to verify that a user is authenticated from the Firebase idToken.
I've attempted to use Google App Engine's example in flask: https://github.com/GoogleCloudPlatform/python-docs-samples/tree/master/appengine/standard/firebase/firenotes.
I've also tried using python libraries mentioned by Firebase here: https://jwt.io/
Any help would be greatly appreciated.
Simply to identify a user you have to send the token that generates javascript as a header in the request to the server
Authorization:key=eyJhbGc .... ZgeFONFh7HgQ
id_token = request.headers['Authorization'].split('=').pop()
Once you have that token with the library google.oauth2.id_token will ask for the UID of that generated token
claims = google.oauth2.id_token.verify_firebase_token(id_token, HTTP_REQUEST)
If not claims:
         Return 'Unauthorized', 401
UID = claims['sub']
Already with that UID is like your PK in any DB that you use

Gmail API watch() not working properly

I am trying to run watch() on my inbox and send it to a pub/sub.
However, I keep getting this error:
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://www.googleapis.com/gmail/v1/users/me.com/watch?alt=json returned "Invalid topicName does not match projects/western-oarlock/topics/*">
The code I am sending is:
request = {
'labelIds': ['INBOX'],
'topicName': 'projects/flask-app/topics/myTopic'
}
service.users().watch(userId='me', body=request).execute()
Why is it attempting to contact western-oarlock instead of flask-app?
Check if access token you are using is the right one.
Check if .p12 key you are using is from the same project, or try using a new key.
I had the same problem, in my case the cause was access token I used in Google Cloud API OAuth2 authentication which was generated using wrong service account. Hovewer I've read then somewehere on the Internet that wrong .p12 key also can cause this issue.
It ended up having to do with the JSON Secrets file. I was authenticating on the wrong project.

Getting user info with Cloud Endpoints (using other API Endpoints)

I'm trying to setup endpoints api (with google app engine, python), but I'm having some trouble getting user profile info. API is working, I can create entities through API Explorer on my localhost.
My goal is to allow user to register for my app by providing just an email, and authorizing the app to get the reset of the info from their profile. I have this endpoints method:
#User.method(http_method="POST",
auth_level=endpoints.AUTH_LEVEL.REQUIRED,
allowed_client_ids=[
endpoints.API_EXPLORER_CLIENT_ID
],
scopes=[
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/plus.me',
],
user_required=True,
request_fields=('email',),
response_fields=('id',),
name="register",
path="users")
def UserRegister(self, instance):
logging.info(os.getenv( 'HTTP_AUTHORIZATION' ))
# 'Beared __TOKEN__'
logging.info(endpoints.users_id_token._get_token(None))
# '__TOKEN__'
instance.put()
return instance
This works fine, I receive authorization token and user is created in datastore, but I can't figure out how to get the profile info. If I enter the token in OAuth2 API (through API Explorer):
POST https://www.googleapis.com/oauth2/v2/tokeninfo?access_token=__TOKEN__
I get token info with some data I need { "user_id": "__ID__", "verified_email": true, ...}, and if I use user_id in +API:
GET https://www.googleapis.com/plus/v1/people/__ID__
I can get the rest of the data I need (name, image, etc).
What do I need to do to achieve this in my UserRegister() method? I'd prefer to return just entity ID and do the rest of registration asynchronously, but that's another issue, I'll figure it out (; Just need some guidance how to call other endpoints from my code...
EDIT:
I've managed to figure out how to call other APIs (code on Gist), now only have one issue with Plus API:
I did some queries and eventually got anonymous quota error. Then I added key parameter and set it to WEB_CLIENT_ID or SERVICE_ACCOUNT:
WEB_CLIENT_ID is OAuth2 Client ID (type: Web Application) from console.developers.google.com/apis/credentials,
SERVICE_ACCOUNT is default App Engine service account - MY_APP#appspot.gserviceaccount.com...
and now I'm getting following error:
HttpError: <HttpError 400 when requesting https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key=__WEB_CLIENT_ID__or__SERVICE_ACCOUNT__&alt=json returned "Bad Request">
When I use +API explorer I get results as expected:
REQUEST:
https://www.googleapis.com/plus/v1/people/__VALID_USER_ID__?key={YOUR_API_KEY}
RESPONSE:
200 OK + json data for user...
Anyone knows why is this happening?
Why am I getting BadRequest response?
Problem with BadRequest was that I didn't send authorization token... I did try to send it as access_token, but seams like +api docs are outdated - it should be oauth_token. When I included this parameter issue was resolved:
build('plus', 'v1').people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token).execute()
HINT: Use http://localhost:8001/_ah/api/discovery/v1/apis/, and discoveryRestUrl property it has to see real properties of your API - this is where I found the answer.
oauth_token can be obtained like this:
token = os.getenv('HTTP_AUTHORIZATION').split(" ")[1]
# or like in my question:
token = endpoints.users_id_token._get_token(None)
I'd suggest HTTP_AUTHORIZATION variable, because users_id_token docs state that it's a:
Utility library for reading user information from an id_token.
This is an experimental library that can temporarily be used to extract
a user from an id_token. The functionality provided by this library
will be provided elsewhere in the future.
How to call other API Endpoints?
This is also an answer to my first question:
from googleapiclient.discovery import build
service = build('plus', 'v1')
request = service.people().get(userId=user_id, key=SERVICE_ACCOUNT, oauth_token=token)
response = request.execute()
data = dict(self.response.POST)
Code that worked for me is here.
NOTE: WEB_CLIENT_ID obtained from https://console.developers.google.com/apis/credentials (OAuth2 Client ID of type Web Application) will NOT work in this case. I had to use SERVICE_ACCOUNT - I didn't try to generate one through console, default service account I got from App Engine worked fine.
...things are much clearer now that I got this working. Hope it will help someone else (;

Categories