Cloud Endpoints Authentication - python-2.7

I am using App Engine Standard with the Python 2 runtime and Endpoints Frameworks.
When making a request, the app just returns "Successful" if the request was completed. I am trying to implement authentication so unauthenticated users are not able to complete the request. I've done the following:
Modified my main.py decorator to include issuers and audience:
issuers={'serviceAccount': endpoints.Issuer('[MYSERVICEACCOUNT]', 'https://www.googleapis.com/robot/v1/metadata/x509/[MYSERVICEACCOUNT]')},
audiences={'serviceAccount': ['[MYSERVICENAME]-dot-[MYPROJECT].appspot.com']}
Modifed my main.py method to check for a valid user:
user = endpoints.get_current_user()
if user is None:
raise endpoints.UnauthorizedException('You must authenticate first.')
Regenerated and redeployed my openAPI document. It now has security and securityDefinitions sections.
Updated my app.yaml to reference that Endpoints version.
Redeployed my app
To make an authorized request to my app, I have done the following:
I gave the service account the Service Consumer role on my Endpoints service.
Generate a signed jwt using the generate_jwt function from Google's documentation. I am passing in credentials using the service account's json key file.
payload = json.dumps({
"iat": now,
"exp": now + 3600,
"iss": [MYSERVICEACCOUNT],
"sub": [MYSERVICEACCOUNT],
"aud": [MYSERVICENAME]-dot-[MYPROJECT].appspot.com
})
Make the request using make_jwt_request function from Google's documentation.
headers = {
'Authorization': 'Bearer {}'.format(signed_jwt),
'content-type': 'application/json'}
I am getting 401 Client Error: Unauthorized for url error. Am I missing something?

Your audiences don't match; in your code, you are requiring an audience of [MYSERVICEACCOUNT], but when generating the JWT, your audience is [MYSERVICENAME]-dot-[MYPROJECT].appspot.com. These need to match.

There are few details, which might be worth checking:
The list of allowed audiences should contain the value of aud claim of a client-generated JWT token. This is what Rose has pointed out.
All of the JWT claims presented in sample client documentation are present. Your code is missing the email claim in the JWT payload dictionary.
The method you're accessing requires no specific OAuth scopes. The scopes are set as the scopes field of #endpoints.method decorator.

After opening a support ticket with Google, it turns out Google's documentation was incorrect. The main.py function needs to check for an authenticated user in the below manner:
providers=[{
'issuer': '[YOUR-SERVICE-ACCOUNT]',
'cert_uri': 'https://www.googleapis.com/service_accounts/v1/metadata/raw/[YOUR-SERVICE-ACCOUNT]',
}]
audiences = ['[YOUR-SERVICE-NAME]-dot-[YOUR-PROJECT-NAME].appspot.com']
user = endpoints.get_verified_jwt(providers, audiences, request=request)
if not user:
raise endpoints.UnauthorizedException
After making that change, I got the following error when trying to make an authenticated request:
Encountered unexpected error from ProtoRPC method implementation: AttributeError ('unicode' object has no attribute 'get')
This was caused by how I was generating the payload with json.dumps(). I generated without json.dumps() like below:
payload = {
"iat": now,
"exp": now + 3600,
"iss": [MYSERVICEACCOUNT],
"sub": [MYSERVICEACCOUNT],
"aud": [MYSERVICENAME]-dot-[MYPROJECT].appspot.com
}
These two changes fixed my issue.

Related

facebook graph api Ad Insights do not return adset_name field

I'm using python for interacting with Facebook Graph API Ad Insights and I'm using a "system user" token to authenticate. I have no problem with any other field but I cannot get data for the adset_name field.
I have "ads_read" permission on my token.
I'm using facebook-business 12.0.1 package.
I'm calling facebook_business.adobjects.adaccount.AdAccount.get_insights_async
And the request params are as below:
fields: ['campaign_name', 'adset_name', 'impressions', 'unique_inline_link_clicks',
'spend', 'actions', 'conversions', 'conversion_values', 'action_values']
params: {'time_range': {'since': '2021-11-16', 'until': '2021-11-18'},
'level': 'campaign',
'export_format': 'csv',
'time_increment': 1,
'action_attribution_windows': '1d_click'}
I'm not posting a response but I can assure you that there is no sign of "adset_name" anywhere. And all the other fields listed here are returning successfully
is this about a lack of permission on my token ?
I just figured out that I cannot receive anything about adset_name or adset_id because of choosing AdsInsights.Level.ad as level. All resolved when I used AdsInsights.Level.ad

get auth token using dj-rest-auth when user logged in

Previously I was using django-rest-auth package and I was getting the auth token when user log in in response response.data.key and this auth token or key was working fine with the api calls as authentication auth
Previously for django-rest-auth:
"/rest-auth/login/"
was getting the response.data.key as auth token and that was working
I have stored in the cookie for later use
.get("/rest-auth/user/", {
headers: {
"Content-Type": "application/json",
Authorization: "Token " + response.data.key + "",
},
})
It was working to get info on user and also was working when used in other api calls by setting it in
Authorization: "Token " + response.data.key + ""
But now, I'm using dj-rest-auth package instead of django-rest-auth and shifted my urls to
/dj-rest-auth/login
and I'm not getting any key or auth token that I can use for authorization in header.
.get("/dj-rest-auth/user/", {
headers: {
"Content-Type": "application/json",
Authorization: "Token " + response.data.? + "",
},
})
It's not working because now I'm getting access_token , refresh_token and user info. I tried to use access_token and refresh_token for authorization in header but it's not working because I'm not getting key or auth token in response when user log in
Note: django-rest-auth is no more maintained and dj-rest-auth is forked from the previous one and have more functions (this is the reason why I'm switching)
How to get auth token or key by using dj-rest-auth package so that I can use it in the header of API calls for authorization?
Check that you don't have an REST_USE_JWT = True in your settings. That setting will enable JWT authentication scheme instead of a (default) token-based.
In django-rest-auth you get the key from a GET request, but according to dj-rest-auth's documentation, you get the token key as a response when you make a post request to /dj-rest-auth/login/.
When you make a POST request to /dj-rest-auth/login/, you can access the key at response.data. But now you need to store it so you can use it in your get requests.
I recommend checking out this question's answers to learn more about storing tokens. How you choose to do this will depend on how to the frontend of your application is built, as the javascript needs access to the token key.
I know I'm late to answer this, but hopefully I can help someone other folks who find this.

Django REST social login with dj_rest_auth does not authenticate the user

I am building an application. The client is built with Next.js and the backend with Django and Django REST framework.
In this application, I would like to have social login.
So far, my situation is this.
I have set up the OAuth on the Google dashboard
On the client, I am using next-auth - The client is successfully calling Google and getting an access token from there.
On the client, the callback that runs after getting the access token from Google makes a call my Django API.
I have set up the backend with dj_rest_auth - My settings are almost identical to the ones described here.
Once the client callback runs and calls my Django API with the access token from Google, I successfully get on the client an access token and a refresh token.
If it is a new user loggin in the first time, a new user is created in Djangos DB
const response = await fetch(`${djangoAPIurl}/api/social/login/google/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
access_token: accessToken,
id_token: idToken
})
});
const data = await response.json();
const { access_token, refresh_token } = data;
Both access_token and refresh_token are defined and appear to be valid tokens.
So far, everything happens as expected. My issue appears after this point.
In my api, I have another view defined.
#api_view(['GET'])
#authentication_classes([SessionAuthentication, BasicAuthentication, TokenAuthentication])
#permission_classes([IsAuthenticated])
def test_view(request):
current_user = request.user
print('current_user.auth: ', current_user.is_authenticated)
response = JsonResponse({"received": True})
return response
From my client, I am attempting to call this view in the following way.
const response = await fetch(`${djangoAPIurl}/api/test/test_view/`, {
headers: new Headers({
Authorization: `Bearer ${session.accessToken}`
})
});
The header is constructed correctly, with session.accessToken being the value I got from the api/social/login/google/ call and the request is routed correctly, however, it fails with Forbidden 403 because the user is not authenticated. I have removed the authentication and permission decrators and the request ends up being processed by the view, and there, upon inspection of the user, it is an Anonymous user. I have also tried changing Bearer to Token, to no avail.
Do you have any advice what I might be doing wrong or missing? Have I completely missunderstood how to use the token I get back from api/social/login/google/? All advice is much appreicated!
I think this is because your secret for hashing JWTS on the client side and server side is not same. Next-Auth automatically creates a secret key for hashing jwt's and dj_rest_auth does the same, unless you explicitly tell them both to use the same secret for hashing jwts. I'm a bit late to answer this, but Hope this will help future people😁😁.

Pubsub signs the JWT token for push with a wrong key

I'm using the following stack for my backend:
Cloud Endpoints
Cloud Run Gateway with ESPv2 => check API keys and validate JWTs
Cloud Run gRPC server implementation
When a change of interest happen I publish a message on pubsub
Now, attached to that pubsub topic I have a push subscription that triggers a Cloud Run method with a service account and a specified audience.
Setup:
Now I need to configure the Cloud Run Gateway to validate the JWT that pubsub generates for the specified service account. This is done in the api_config.yaml as per documentation here: https://cloud.google.com/pubsub/docs/push#jwt_format
The issuer is https://accounts.google.com and the audience in the one I specified in the pubsub subscription.
authentication:
providers:
- id: gcloud
jwks_uri: https://www.googleapis.com/robot/v1/metadata/x509/875517523825-compute#developer.gserviceaccount.com
issuer: https://accounts.google.com
audiences: project.clounrun.admin
rules:
- selector: project.v2.Events.*
requirements:
- provider_id: gcloud
The jwks for this service account can be found here:
https://www.googleapis.com/robot/v1/metadata/x509/SERVICE-ACCOUNT-ID
In my case:
https://www.googleapis.com/robot/v1/metadata/x509/875517523825-compute#developer.gserviceaccount.com
https://cloud.google.com/endpoints/docs/grpc/service-account-authentication#set_up_authentication_in_the_grpc_api_configuration
I've activated debug logs for the ESPv2 and this is what I get when it tries to validate the JWT:
13:07:37.027 request headers complete (end_stream=false):\n\':authority\', \'project-gateway-v2-dev-cuvfttrlpq-de.a.run.app\'\n\':path\', \'/v2/sessions:event\'\n\':method\', \'POST\'\n\'content-type\', \'application/json\'\n\'authorization\', \'Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImQwNWVmMjBjNDUxOTFlZmY2NGIyNWQzODBkNDZmZGU1NWFjMjI5ZDEiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJ5aXRub3cuY2xvdW5ydW4uYWRtaW4iLCJhenAiOiIxMDk0NDIwMDAxNjU1ODQ3Nzc4MDciLCJlbWFpbCI6Ijg3NTUxNzUyMzgyNS1jb21wdXRlQGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImV4cCI6MTYwMzgwNzAwMSwiaWF0IjoxNjAzODAzNDAxLCJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJzdWIiOiIxMDk0NDIwMDAxNjU1ODQ3Nzc4MDcifQ.PwLvaBz-_dM3_5VjgzVlaueoRhacUE39FdFuVSfQI2w4V3OD79tIA6t_0cuvE1-kIpNrfDda5RSavVcs4SV4Y5P8AvW5jDtHdCELP3yb8HzVT9nCJlLac-v5ZKuv06syBN9F2Ve7VtZHHZOE2VS4B7uw0Q__1rIVzIllYWBYHkYGUBP2mZ3VhRw9VXARMr-EICanXfETe_MMfoKsX4202L_O4LffPdv16pA5hYtwzKi67gFYuubKI1XNkVQVatQieYQrhkz5jMyNyhVKy8ZY5a2UXagQL7erdsm-uPJo6ujoq0Yxtl8iKMdRv4XfrQSLyFZHCYdO6n2LHJle_FQzCQ\'\n\'content-length\', \'335\'\n\'accept\', \'application/json\'\n\'from\', \'noreply#google.com\'\n\'user-agent\', \'APIs-Google; (+https://developers.google.com/webmasters/APIs-Google.html)\'\n\'x-cloud-trace-context\', \'c3027b54c6aad09d85ce75f6dcaf07d5/8778433264575104852\'\n\'x-forwarded-for\', \'66.249.82.169\'\n\'x-forwarded-proto\', \'https\'\n\'forwarded\', \'for=\"66.249.82.169\";proto=https\'\n\'accept-encoding\', \'gzip,deflate,br\'
13:07:37.027 Called Filter : setDecoderFilterCallbacks
13:07:37.027 matched operation: project.v2.Events.OnSessionEvent
13:07:37.027 Called Filter : decodeHeaders
13:07:37.027 use filter state value project.v2.Events.OnSessionEvent to find verifier.
13:07:37.028 extract authorizationBearer
13:07:37.028 extract x-goog-iap-jwt-assertion
13:07:37.028 gcloud: JWT authentication starts (allow_failed=false), tokens size=1
13:07:37.028 gcloud: startVerify: tokens size 1
13:07:37.028 gcloud: Verifying JWT token of issuer https://accounts.google.com
13:07:37.028 gcloud: JWT token verification completed with: Jwks doesn\'t have key to match kid or alg from Jwt
13:07:37.028 Called Filter : check complete Jwks doesn\'t have key to match kid or alg from Jwt
13:07:37.028 Sending local reply with details jwt_authn_access_denied
As you can see the jwks doesn't contain the JWT kid. And this is true.
The header of this JWT is this:
{
"alg": "RS256",
"kid": "d05ef20c45191eff64b25d380d46fde55ac229d1",
"typ": "JWT"
}
and the payload is this:
{
"aud": "project.clounrun.admin",
"azp": "109442000165584777807",
"email": "875517523825-compute#developer.gserviceaccount.com",
"email_verified": true,
"exp": 1603807001,
"iat": 1603803401,
"iss": "https://accounts.google.com",
"sub": "109442000165584777807"
}
This indicates that pubsub does indeed use that service account but it looks like that specific kid is missing from the jwks response.
{
"4366ae10d4a79728de14f6f89a628b4fe640140f": "-----BEGIN CERTIFICATE-----\nMIIC+jCCAeKgAwIBAgIIapShdpj8y6QwDQYJKoZIhvcNAQEFBQAwIDEeMBwGA1UE\nAxMVMTA5NDQyMDAwMTY1NTg0Nzc3ODA3MB4XDTIwMDQzMDIyNDE0N1oXDTMwMDQy\nODIyNDE0N1owIDEeMBwGA1UEAxMVMTA5NDQyMDAwMTY1NTg0Nzc3ODA3MIIBIjAN\nBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArvpvYFbpJiXk4cEOo2xXzIFjLZEq\nIe1753Oe00IXmq5u/Glf6H0TdShqSn/mHd283UOeDGyjcz/AZO3iKyGv+GndSfiq\ny9TbXfeinCUoVtLUU500P32Ciej/t8Hf4UZYl6XlBVSMZK5ZVCqdWHs9vfPH8k6w\nSJm456BwjL3xty5AjuBooTSHec92SGe2DYSpMJL9NHGELdSnNRoxEaXpEUBV93vr\nTKfbBKa/1WaumVvIn54rAIMkaFq7dJRFr98U2yfHFvUhMtqAwX7HkdvgM74sjfV0\nduVfVz3T/m/oG/7lCllpI4LhVHpxxuNhimFo5quXjShJLPNEjFbESndMaQIDAQAB\nozgwNjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAK\nBggrBgEFBQcDAjANBgkqhkiG9w0BAQUFAAOCAQEAZqBZCbFswKBxa4S+fvC6qb5c\nF/29sxM4WijC4e+/Ti+e6tcRrf3LyOF0jIpHbHWcwjTiYXX+D+UUmMrgh0ZVLCOR\nTDj29JSIUEhzYsGmBzBBcPLfMO2zl6c0aMdUkO+3vXQoTHUNjcs8UoN6BlPo3oIG\n2BjOXhEmuuUA3BQVDsMIM5g4G5r28WaprV7GaTa4fsyCh9oRquTtqL34CZLxiLXv\ncqK+oRMFU4tsLKvZjcfTeKp3fbXDpo7R1R7/+SyxAJTQOe1uPeAc5qhlVK6Ky/Zy\nTv3SUzAifJ3BDz1eNKYTqQNWiXi3QnX6qwebHgHKcJ01qnbCqMqbv6HicpULdQ==\n-----END CERTIFICATE-----\n",
"e0b3368c1646eb88d93b9d0b2d65e2d6fbae27b7": "-----BEGIN CERTIFICATE-----\nMIIDNDCCAhygAwIBAgIIQ2si/YIYDg0wDQYJKoZIhvcNAQEFBQAwPTE7MDkGA1UE\nAxMyODc1NTE3NTIzODI1LWNvbXB1dGUuZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3Vu\ndC5jb20wHhcNMjAwOTMwMTM1NjM5WhcNMjIxMDEzMjAxNTU3WjA9MTswOQYDVQQD\nEzI4NzU1MTc1MjM4MjUtY29tcHV0ZS5kZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50\nLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALejPchRlDY8EE5D\nvD1CuPyGsbCoXHDnJpaA4P23CLNUmooBydxxzfV611vTUTBCFjq5Pcg3fpGusoMt\nyF9TqQVq4bGZxrXv+yxVs24uFdHAB7mY9JUE8GKN5i7IMP3egDcns4LmNWsB0iKN\niK1gK5q7gZIISjo3igLrup1G6wM02qym2VS6raKn/12WY+pa/PiZrO79eAkYPyqr\n73AvLdcLsik9U7lNDfxiev3/IE+tP0B68Uo5Ff+Wai+RDnNmDX3Fy50hv8vfniRe\nB/b57Kn6SMtMz4IMD4BeNQpOXdZe780cubuwS1oPLcRLHCXTqEzsGVIMLoMxrxF9\nhRK743kCAwEAAaM4MDYwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYD\nVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADggEBAKF+dNSK+GFN\ndKFBs5PZufLUMlyaIXYysDnZgCk9oWMBiBpa6WDg00SllAz5z9koVYFr3ySQq6mK\nddkJLUXVt+NkR6Jh4k4OYshBV4v6q/BOXEjpP6/+jNV9qD5vVuq+w1eReouCl8eO\ng/rfUAivAPpU1srXSmhs6Uw2E4jDf8ArJWLsPfHjoqWLJEICGBG0i1nlJBRpGNWj\nyBfN1cEp64pYynpnVY9kuIEKdBCue8QEXVhsKURHGcOfCWP9+vVerhlyDatL1tQW\n5RAjvzS8NGLe8QnMdy+63TwP4qKGkWSEPTxP0fpQpxuLbqKHHsSeA7WL6nS6zSwx\nRXHDLiUfU0A=\n-----END CERTIFICATE-----\n",
"c0191d2d00f89eb1905886a04335a79a124885e1": "-----BEGIN CERTIFICATE-----\nMIIDNDCCAhygAwIBAgIIaTwbvrNhTkYwDQYJKoZIhvcNAQEFBQAwPTE7MDkGA1UE\nAxMyODc1NTE3NTIzODI1LWNvbXB1dGUuZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3Vu\ndC5jb20wHhcNMjAxMDI4MDA0NDEwWhcNMjAxMTEzMTI1OTEwWjA9MTswOQYDVQQD\nEzI4NzU1MTc1MjM4MjUtY29tcHV0ZS5kZXZlbG9wZXIuZ3NlcnZpY2VhY2NvdW50\nLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKJZDpcJAHyot7z9\n0EAOOEC6+dwlkxduThH5FQXlaCUoHRAoCtTHrxeITSsZg55cXGXKzTTf1Nv+UE9E\nTY5nhU70nEK77B5FbxwzX9Q/OkOhP3NQnl0U0O6nedfCJvOtCMdopHnrRa+ZIWhG\nPoW2RKQTv7gr4bGFJnQshcYDtrahH6Xv/RfyyTnI9AUzCX6eVO3g7odLSdcv+qnV\niqsZLCqz2lflKky+Rti/1f8LGXySTs+r7bdvqCdpbIm6f79WqQ720hmx+4JaIuS3\nYmYztK5J6JwsDLqULyyMvZs9vVA6cK1+580Vb76pkF9MHqTZdZ2gPP1qoKCO0ezI\nK3lYfGECAwEAAaM4MDYwDAYDVR0TAQH/BAIwADAOBgNVHQ8BAf8EBAMCB4AwFgYD\nVR0lAQH/BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADggEBAHB+6GiylFKU\nq9nyzrvlTRe0tph7kT6D8WhlnxGDgnDOn9xAB360xh+KPczq7+8DJczSb5145No/\nefrCUpvgkraVe/eTsu8EAjDjk/XPOeV4117EU+PRVV5nitYiVGJ0Z+3V3kHCnqLT\ntTxQlKDRpJ02GjhQRkW03fhLEFr7eu79fiwfjfaFSoAAIrNxBi3mtrhcidu43zD1\n/VXAReH26S129UoYz7fPlsnULX/oUyZPidaKIZ2Fl5N2QTgXnd1PnU3HgesU4HWs\nYJeiq6Z1pFxTw9192VD3MgDSDVQAeHXs+wTQsUHftMcNl7nPWl9YfiPjzD4sNWL3\n8whVkhIN8r4=\n-----END CERTIFICATE-----\n"
}
We have:
4366ae10d4a79728de14f6f89a628b4fe640140f
e0b3368c1646eb88d93b9d0b2d65e2d6fbae27b7
c0191d2d00f89eb1905886a04335a79a124885e1
But no
d05ef20c45191eff64b25d380d46fde55ac229d1
If pubsub is using a different key that where can I find the jwks?
UPDATE:
I verified all my services accounts JWKS links and none of them contain that kid. So this is very strange.
You are using a Google signing key (Google is signing for you).
This means you need to lookup the KID from Google's published public keys.
You will find the public certificate kid here: https://www.googleapis.com/oauth2/v1/certs
For some added context, each service account has a sort of "hidden` key. Google manages and controls this key. When you request an Identity Token from Google, the JWT is signed using this special key. That is why you need to look this up in Google's public certificate list instead of your service account's certificate list.

How to POST or PATCH users role and access_level for BIM360 on Postman

I have been successful in creating a new Account User from following this tutorial: https://forge.autodesk.com/en/docs/bim360/v1/reference/http/users-POST/#example, and have used the PATCH method to set their status to active on Postman.
I would like to set their role and access_level but I am having trouble doing so. I have followed the link below to try and perform this function, but it requires the user to already be a BIM 360 Project Admin for it to work.
https://forge.autodesk.com/en/docs/bim360/v1/reference/http/projects-project_id-users-user_id-PATCH/
I also tried following the next link below to add a User to a project, but I am getting errors that I am unsure how to fix.
https://forge.autodesk.com/en/docs/bim360/v1/reference/http/projects-project_id-users-import-POST/
URI: https://developer.api.autodesk.com/hq/v2/accounts/:account_id/projects/:project_id/users/import
Method: PATCH
Authorization: *******************************************
Content-Type: application/json
x-user-id: {{user_id}}
Body:
{
"email": "john.smith#mail.com",
"services": {
"document_management": {
"access_level": "account_admin"
}
},
"company_id": ************************************,
"industry_roles": [
************************************
]
}
(The id for industry_role is IT).
Error:
{
"code": 1004,
"message": "this user doesn't exist."
}
I am unsure how I am getting this error since the User Id used for x-user-id is the same user_id associated with the email given in the request body. Is there a way to fix this or another method I can use?
The x-user-id header is not for specifying the user to import but rather:
x-user-id
string
In a two-legged authentication context, the app has access to all users specified by the administrator in the SaaS integrations UI. By providing this header, the API call will be limited to act on behalf of only the user specified.
Remove this field if that's not what you intended.
Verify the user id and email match each other via /GET users and /GET users:userid.
And be sure to provide either the user's email or the user ID and don't provide them both:
Note that you need to specify either an email, or a user_id. However, you cannot specify both.
See doc here