As per google's docs, I'm generating my oauth access token like this:
export TOKEN=$(~/go/bin/oauth2l fetch -jwt -json ~/.google/my-service-key.json cloud-platform)
I'm then doing requests to Google's REST API like this:
curl -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d (...) $URL
The response I'm getting back from Google is that I'm not providing an OAuth token, when I clearly am:
Request had invalid authentication credentials. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
Other posts suggest to use gcloud auth application-default print-access-token instead of the OAuth token, but I know this to be the incorrect approach, as Google's API responds back that it wants a service account OAuth token and not an identity.
Any idea what's happening here?
Sometimes an old (bad) token gets cached from before you rotated the service_account.json.
Delete Cache
Try this:
rm ~/.oauth2l
Token vs JWT
And try getting an API token before you sign the JWT:
oauth2l fetch cloud-platform
Scope vs Audience
Also, the API token requires a scope (shown above), whereas the JWT requires an audience aud, which is a URL:
oauth2l fetch --jwt https://www.googleapis.com/auth/cloud-platform
ENVs
You may also want to make sure that you don't have competing configuration, see if GOOGLE_APPLICATION_CREDENTIALS is set.
echo $GOOGLE_APPLICATION_CREDENTIALS
unset GOOGLE_APPLICATION_CREDENTIALS
Or potentially use it instead of --json ./service_account.json:
export GOOGLE_APPLICATION_CREDENTIALS=$HOME/.config/google/service_account.json
HTH
Related
I deployed a 3rd party webapp which uses basic authentication for access on Google Cloud Run. I additionally wanted to protect the endpoint by allowing only Google-authenticated users of the organization access. Both these methods use the Authorization header of the request and I cannot get it to work.
I tried following this post, providing both credentials in one field. My reasoning was, that GCP should select the strongest credential mechanism it supports - bearer - and leave the basic credentials for the webap. I have no idea if the webapp could have dealt with it because Google's reverse proxy already barred my access.
curl "-HAuthorization: bearer ${bearer_token}, basic ${base64_userpw}" https://my-google-endpoint.com
-> 401 Unauthorized
I also tried Proxy-Authorization with no different result.
curl "-HProxy-Authorization: bearer ${bearer_token}" "-HAuthorization: basic ${base64_userpw}" https://my-google-endpoint.com
Is there a way to get nested authentication to work with Google's reverse proxy? I was able to get past the reversed proxy by only supplying the bearer but naturally hit the wall at the service. With deactivated authentication on proxy side I was able to authenticate with the service using the basic credentials.
P.S.:
I am not using a browser to access the webapp but command line tools.
You cannot mix Authorization mechanisms with IAP. Everything after the bearer keyword is considered the token string.
One solution is to change your Basic Authorization HTTP header from using Authorization to a custom HTTP header. Something like X-MyApp-Authorization. Then your app processes the custom header to handle the Basic Authorization mechanism.
[Update 2021-08-17]
My original answer is partially wrong. Google's solution is currently broken.
Cloud Run is behind Google Cloud IAP. The client making a request can use two HTTP Authorization headers:
Authorization: <application authorization token>
Proxy-Authorization: Bearer <IDENTITY_TOKEN>
If a valid ID token is found in a Proxy-Authorization header, IAP authorizes the request with it. After authorizing the request, IAP passes the Authorization header to your application without processing the content.
Authenticating from Proxy-Authorization Header
This means the OP was on the right track using the Proxy-Authorization header. However, this feature does not work.
Create an Identity Token:
Use curl to verify that the token works with a Cloud Run endpoint that requires the Invoker role:
curl -H "Authorization: Bearer $TOKEN" $endpoint
That works. Now try the Proxy-Authorization header:
curl -H "Proxy-Authorization: Bearer $TOKEN" $endpoint
That fails with a 403.
Now try both headers:
curl -H "Proxy-Authorization: Bearer $TOKEN" -H "Authorization: Bearer $ANOTHER_TOKEN" $endpoint
That fails with 401 "The access token could not be verified"
gcloud auth print-identity-token
I am using documented methods to use two authorization headers, but this feature does not work.
The PHP SDK did not have the proxy-authorization header support added until June 25, 2021. I created a test application from Google's example. That also failed with the same errors.
June 25, 2021 Patch
Does it happen to work if you send two Authorization headers, like curl -H "Authorization: bearer foo" -H "Authorization: basic bar" ?
--Matthew, Google Cloud IAP engineering
I have successfully configured the API Manager, ID and IS according to the documentation: https://docs.wso2.com/display/AM260/JWT+Grant#JWTGrant-UsingtheJWTgrant.
I invoke the WSO2 token endpoint to exchange an external JWT for a WSO2 access token:
curl -i -X POST -k -d 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImF1ZCI6WyJ3c28yIl0sImVudmlyb25tZW50Ijp7InByb2ZpbGVzIjpbImRldiJdfSwiYXV0aCI6eyJyb2xlcyI6WyJVU0VSIiwiQURNSU4iXX0sInVzZXJfbmFtZSI6IjAwMDAwMDk5Iiwic2NvcGUiOlsib3BlcmF0ZSJdLCJpc3MiOiJQQVMiLCJleHAiOjE1NDUzNDgyODcsImdlbmVyYXRlZEJ5IjoiUEFTIiwianRpIjoiOWQ4ZWU3ZTgtNDBlZS00MTZjLTlkYjgtYjU2NDZhYTZhN2JmIiwiY2xpZW50X2lkIjoiZnJvbnQtcG9saXphcyJ9.Ccs1OxjteRsvHTump-ZTawEsqlTrIeO0LJUzt5Ita8udvMOa_tB1rHOtI8GAa2mDCPMD_Z_jtZ2SlXPs10GvsYlF4jS_wcCVAPtHsoigzuNtg5t7CVfeCI2Bzhak721LdYBcjB9s0Jn24G9eb2jqx8NF0RPlKgmhbxwdY0b8XeigLp-kGCsFKY_fDIjFUM0oifzCWOmtaCRMtMx3CKVZOWq9dBIokheCi2foL8YkBCz57yo4vb782AYWXdiHj38TPPe4IguARuoc9FSymyiL1gWHJmyMZFvAeAJkDnHHEnnezqPmcWQweC1ylLwUYGNVLM8YSfuBDtcGBWSO0F-WKw' -H 'Content-Type: application/x-www-form-urlencoded' https://localhost:9443/oauth2/token -d 'client_id=w_paekjnDDY8zcCfCRgj_81g2eYa'
This answers successfully with an access token, a refresh token etc etc.
I have created an application in the WSO2 APIM store. In the production/sandbox tabs, the only checked Grant Type item is JWT.
The point is, I use the previously gotten access token (which is itself an JWT token) to invoke an API subscribed with the above application:
curl -k -X GET "https://192.168.179.129:8243/myapp/api/v1/customers" -H "accept: application/json;charset=UTF-8" -H "Authorization: Bearer eyJ4NXQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVEiLCJraWQiOiJOVEF4Wm1NeE5ETXlaRGczTVRVMVpHTTBNekV6T0RKaFpXSTRORE5sWkRVMU9HRmtOakZpTVEiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImF1ZCI6IndfcGFla2puRERZOHpjQ2ZDUmdqXzgxZzJlWWEiLCJuYmYiOjE1NDUzNDgxOTgsImF6cCI6IndfcGFla2puRERZOHpjQ2ZDUmdqXzgxZzJlWWEiLCJzY29wZSI6ImRlZmF1bHQiLCJpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0Ojk0NDNcL29hdXRoMlwvdG9rZW4iLCJleHAiOjE1NDUzNDgyODcsImlhdCI6MTU0NTM0ODE5OCwianRpIjoiNjRlM2I5N2UtOTNlNC00YzQ2LThlNmQtMzlmZjQzOWQxM2Y0In0.UBLOsCCD3t4Wf8nXBnDkkGXxefYySelDzEcs1F_IrbshMJXohxcL92Av1nmcpdNdjin7GdC8Y305rrkBt9T1L_cMAHLYYcI5cI1J7wmAgEd1CEv9gI7IUYfAdbga2AeV4kIlNsgiV6PKnU34WnY7rEVqXD908eEHY5UvaNXc0Bz6C8d-p39-SqKUblGHPh9vdkpcCGcK0CgGKjtiU2lai_JkRALdgEgonT37R5eqmuxPxUouWNz9TCJgTuonKPA-9bYOsMvbzGlm--0m0j9gdxnv-3N1Kv_2JqSCR4pToDClhSKgFCE1L025LIICM-sLd_PDU5pwYge_iKseiIDZfA" -d 'client_id=w_paekjnDDY8zcCfCRgj_81g2eYa'
I get the following error (900908) - Resource forbidden:
<ams:fault xmlns:ams="http://wso2.org/apimanager/security"><ams:code>900908</ams:code><ams:message>Resource forbidden </ams:message>
<ams:description>Access failure for API: /myapp/api/v1, version: v1 status: (900908) - Resource forbidden </ams:description></ams:fault>%
I must be missing the final step which is how to allow those access tokens gotten in the JWT grant to be used to access an API subscribed by an application.
The error code 900908 means the API is not subscribed by the application. Please double check.
Technically Bee was right in his answer, but I would like to point specifically at what I was doing wrong in case it happens to others:
The problem was that the client_id/client_secret I was using when exchanging the JWT to get the access token were the ones from the Service Provider I had created. WRONG!
The ones was that need to be sent are those from the subscribed application. With that the resource forbidden error doesn't show up anymore.
I tried to access Google's Datastore through their REST Api. It says that they allow authentication through the API-key. However it doesn't seems that I can get it to work any where. I copied the snippet generated from their Try this API page.
curl --request POST \
'https://datastore.googleapis.com/v1/projects/PROJECT_ID:runQuery?key=[YOUR_API_KEY]' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"query":{"filter":{"compositeFilter":{"op":"AND","filters":[{"propertyFilter":{"property":{"name":"id"},"op":"EQUAL","value":{"stringValue":"ID"}}}]}},"kind":[{"name":"NAME"}]},"partitionId":{"namespaceId":"NAMESPACE_ID","projectId":"PROJECT_ID"}}' \
--compressed
But it keeps returning me an 401 error.
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
It looks like it require me to use OAuth instead, which is not what their documentation says. Anyone experienced something similar?
You are using an API key which is incorrect.
This link details which services support API Keys. Cloud Datastore is not one of them.
Using API Keys
You want to use an Access Token which is derived from Service Account credentials.
Review this document.
Using OAuth 2.0
The steps to generate an Access Token:
Load the service account credentials json file.
Extract the client_email, private_key and private_key_id.
Create a json payload.
Call the authorization URL: https://www.googleapis.com/oauth2/v4/token
This returns a json object. Extract the access_token.
Use the access_token instead of an API Key.
There are examples on the Internet in various languages. The link will get you started. The process appears complicated, and it is, but once you understand it, generating Access Tokens is easy and they can be reused until they expire (typically 60 minutes which you control).
This document on Google Cloud Storage authentication is the same for Cloud Datastore. The key is understanding "Authorization: Bearer" which is a header you need to include with your curl request.
Authentication
Reading the google docs (https://developers.google.com/identity/protocols/OAuth2WebServer#callinganapi), it says i can revoke token (and thus force a login with credentials) by calling credentials.revoke.
What would be the flask-oathlib way to do this?
Flask-OAuthlib itself didn't provide a way to revoke token. (I'm the author of Flask-OAuthlib)
My new project Authlib has provided a revoke_token method for OAuth 2.0. However, Google's revoke token endpoint doesn't respect RFC7009, which means the revoke_token method provided by Authlib can not be used.
You can send a HTTP request directly to revoke token endpoint:
curl -H "Content-type:application/x-www-form-urlencoded" \
https://accounts.google.com/o/oauth2/revoke?token={token}
BTW, if you need a RFC7009 revoke token method, checkout the source code in https://github.com/lepture/authlib/blob/master/authlib/client/oauth2.py
I want to develop a Django application to send message thr google c2dm server to andriod device.it uses OAuth2 to authorization.
first i got the credentials and store it in the storage as storage and then i want to get the credentials from
storage and send this credentials together other params and headers to c2dm api.
i could get the credentials for scope https://android.apis.google.com/c2dm and store it in storage.
please some one guide me,how can i make the request with credentials and send to https://android.clients.google.com/c2dm/send to deliver.
Thanks in advance,
I suppose you've been able to perform step 2 Exchange authorization code for tokens on Google OAuth 2.0 Playground. Then you should have acquired a refresh token and an access token (if you didn't receive a refresh token, verify that you have checked Force approval prompt and selected offline for Access type in the OAuth 2.0 Configuration.
The access token will expire after some time (usually 1 hour), but the refresh token does not. The refresh token (together with the OAuth Client ID and the OAuth Client secret) can be used to obtain a new access token:
curl --data-urlencode "client_id=OAuthClientID"
--data-urlencode "client_secret=OAuthClientSecret"
--data-urlencode "refresh_token=RefreshToken"
-d "grant_type=refresh_token" "https://accounts.google.com/o/oauth2/token"
(Replace OAuthClientID, OAuthClientSecret, RefreshToken). For futher reading refer to: Using OAuth 2.0 for Web Server Applications - Offline Access
Now you can use this access token and the registration ID of the device to send messages to that device using C2DM:
curl -k -H "Authorization: Bearer AccessToken"
--data-urlencode "registration_id=RegistrationID"
--data-urlencode "collapse_key=0"
--data-urlencode "data.message=YourMessage"
"https://android.apis.google.com/c2dm/send"
(Replace AccessToken, RegistrationID and YourMessage)