Melissa Guo
Melissa Guo

Reputation: 1088

Cloud Endpoints Authentication

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:

  1. 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']}
  1. 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.')
  1. Regenerated and redeployed my openAPI document. It now has security and securityDefinitions sections.

  2. Updated my app.yaml to reference that Endpoints version.

  3. Redeployed my app

To make an authorized request to my app, I have done the following:

  1. I gave the service account the Service Consumer role on my Endpoints service.

  2. 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
    })
  1. 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?

Upvotes: 0

Views: 238

Answers (3)

Melissa Guo
Melissa Guo

Reputation: 1088

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.

Upvotes: 0

Tomasz Boczkowski
Tomasz Boczkowski

Reputation: 36

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.

Upvotes: 1

Rose Davidson
Rose Davidson

Reputation: 652

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.

Upvotes: 1

Related Questions