Google App Engine auth for Google APIs using service account - python

TL;DR What is the preferred way of doing this now that service accounts need domain-wide delegation set up?
I have a GAE project (python 2.7 runtime) that uses the Google Calendar API v3. Up until last week, I had been using the default GAE service account to connect to the calendar API, and the service account was given read/write permission to the calendar under calendar sharing settings.
Since last Thursday Oct 20, the service account cannot write to the calendar (events.post or events.patch) -- returns a 403 Forbidden -- and reads (events.list) return 200, but no records. Checking the permissions for the account under calendar sharing, it has been changed to "See Free/Busy only".
I believe this is related to this announcement from Google about winding down OAuth 1.0 service accounts on Oct 20: https://developers.googleblog.com/2016/04/saying-goodbye-to-oauth-10-2lo.html
The recommendation seems to be to grant Domain Wide Delegation to the service account: https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority However, the GAE default service account does not list a client_id. So we set up a new service account, and granted it Domain-Wide Delegation.
But the new service account doesn't work any better. I tried using both AppAssertionCredentials, and created a JSON key file and tried using ServiceAccountCredentials, as detailed on https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
AppAssertionCredentials doesn't seem to work because the account is not the default service account, even passing in a service_account_id param.
ServiceAccountCredentials does refresh the key etc. but I get the same behavior as with the default service account: 403 Forbidden errors on writes and no records returned on reads.
I have double-checked the new service account has domain-wide delegation set up and under the calendar permissions it is listed as "Make changes to events".
EDIT:
We also were able to set domain-wide delegation for the default GAE service account. However, it still does not work using either AppAssertionCredentials or ServiceAccountCredentials. I get the same behavior: 403 Forbidden errors on writes and no records returned on reads.
EDIT #2:
We also tried setting up a new service account from scratch, setting DwD, and permissions for the account on the calendar. Same behavior, and the calendar permissions are forced to "See only free/busy", with the other options greyed out.

Related

Having trouble getting MSAL Authorization Code workflow to work

I am building a desktop python application that uses the MSAL authorization code workflow by opening up a browser window for authentication. I keep getting back an invalid grant error (code 70000) for some accounts but not others when trying to get an authorization token. It seems to work just fine for the personal Microsoft account through which the application is registered in the Azure portal. It also works fine for my university account (a school Microsoft account), but not for other personal Microsoft accounts.
Through the Azure portal, the application is registered with the ability for all Microsoft accounts to work with it. The scopes listed there also match the scopes that I am requesting in the python application.
The authorize endpoint does return a valid looking authorization code, but then when I try to use that code to get a valid token, I get the error. More specifically, the message associated with the error says:
AADSTS70000: The request was denied because one or more scopes requested are unauthorized or expired. The user must first sign in and grant the client application access to the requested scope.\r\nTrace ID: 6afddbd2-308e-44df-8640-976dc1c1f601\r\nCorrelation ID: bdb626d0-0a3d-4333-ac8f-b5ff510ca046\r\nTimestamp: 2022-07-24 18:50:23Z
What might be causing this issue to occur?
It turns out this was an issue with the scopes I was providing to the authorization endpoint. The scopes profile, openid, and offline_access should be specified to allow some features of Microsoft's Graph API to function properly. In my case, it was the offline_access scope that did the trick. Also note that these scopes cannot be added to the authorization token request, at least through the Python MSAL library. These scopes need to be specified during the process of getting the authorization code only, not the token.

Trouble with access role when inserting shared calendar to my list from google calendar API using service account

Initial situation:
In one of my python app, we use a service account to call Google
Calendar API.
User can share their calendars with my service account via its proper email address
User also put shared calendars ids on my system
On my system, i have a proper calendars ids with proper permissions from shared calendars with my service account
I am trying to accept shared calendars with me by calendar id via Calendar Api with this endpoint https://developers.google.com/calendar/v3/reference/calendarList/insert?authuser=1
I am not using G-Suite for service account
Current issue:
When i accept shared calendar via API it returns the correct response with external calendar data but actually not inserting calendar to my list. I think it's through access role returned in response accessRole=reader and looks like i don't have writer permissions from the service account.
I am using oauth2client and googleapiclient libraries to authenticate my service account.
Issue:
You're sharing some Calendars with a service account, and you cannot see those Calendars on the UI.
Answer:
A service account, unlike regular accounts, does not have a Calendar UI. If you're looking for these Calendars on your regular account's UI, you won't find them, since the service account is not you. But this doesn't mean these have not been shared. The only way to see which Calendars are in the service account's CalendarList is to list them through the API (see CalendarList: list).
With that said, if your purpose is to be able to see the shared Calendars on the UI, you have to share them with a regular (that is, a user) account, either via API or directly through the UI.
Further reading:
Using OAuth 2.0 for Server to Server Applications

Google Calendar API Python Authorization

I've requested access to an OAuth key through the google api console for the calendar api. I've also set my scope up to match the read/write permission setting, but during testing - had authenticated with the "read-only" permissions. Now when I'm testing to insert an event into my calendar, I receive the error "Insufficient Permission". **I'm using python for all of this.
I've tried resetting the client_secret, disabling the api (and renabling), as well as adjusting the auth url.... but to no avail.
Any advice on how to change my permissions in order to create events?
To fix it , I had to revoked access to the app at https://accounts.google.com/IssuedAuthSubTokens and retired after then I was able to access the API correctly.
Despite having the scope in the list, and the scope showing up on Google's OAuth2 grant page, the additional scope wasn't granted.

Python Google Analytics OAuth Service Account and 2LO; "sub" parameter and domain wide delegation

I've read a few comments in some reports on the use of the sub parameter which (this is how i understand it), when passed with the credentials object to google analytics for 2LO with service accounts, can be used to act as a substitute for directly authorizing an account manually through the admin console which I've pictured below.
Analytics Google API Error 403: "User does not have any Google Analytics Account"
Am I wrong in my understanding in thinking that if i passed 'sub': 'superuseremail#account.com' it would take the place of manually setting account permissions in GA?
More importantly, I was granted access to a Google Analytics account via account permissions (such that I don't have super-user authority, or access to the super user email), and I passed this sub parameter using that email. I received the 403 error attempting this route.
Put briefly:
The only way to get access with the service account I'm gleaning is either to have access to the super-user email and managing account permissions by manually adding the service account email or to pass the sub parameter with the credentials object having a sub email that is the correct super-user email?
TLDR: No!, Google Analytics is not a G-Suite product, and a service account does not have the authority to access another's G-Suite account without direct authorization.
If you take a close look at the source code for the method in question:
def create_delegated(self, sub):
"""Create credentials that act as domain-wide delegation of authority.
Use the ``sub`` parameter as the subject to delegate on behalf of
that user.
For example::
>>> account_sub = 'foo#email.com'
>>> delegate_creds = creds.create_delegated(account_sub)
Args:
sub: string, An email address that this service account will
act on behalf of (via domain-wide delegation).
Returns:
ServiceAccountCredentials, a copy of the current service account
updated to act on behalf of ``sub``.
"""
new_kwargs = dict(self._kwargs)
...
You will see that this service account will act on behalf of the user via domain-wide delegation.
Then the answer to your question is "what is
Domain wide delegation?".
In enterprise applications you may want to programmatically access a user's data without any manual authorization on their part. In G-Suite domains, the domain administrator can grant third-party applications with domain-wide access to its users' data — this is referred as domain-wide delegation of authority.

Authentication with Azure Active Directory - how to accept user credentials programmatically

Is there any way to login via web application or web api to Azure Active Directory (with AD credentials) using my own username and password page which is hosted outside of Azure?
From my investigation it seems there is no programmatic way to send username and password to authenticate users with Azure AD (if you hosted an app outside of Azure)
Not sure if they consider this to be a security hole of some sort (i dont think it is it https is enforced?)
Seems like you can only authenticate users by going through the code grant (which means popping out of our application to sign on to an external site).
Ultimately I want to create a python flask api that can authenticate against Azure AD directly if possible.
I have done this in the past (with other auth systems) with the Oauth grant_type=password to send username and pass, but dont think this is supported in Azure AD (correct me if im wrong?)
I know grant_type=client_credentials is supported, but that seems like its service to service auth, which is not quite what im after
http://msdn.microsoft.com/en-us/library/azure/dn645543.aspx
If its not possible to have a login page hosted outside of Azure for this, is it even possible to have one inside of Azure, seems like from examples here:
http://msdn.microsoft.com/en-us/library/azure/bc8af4ff-66e7-4d5b-b3d4-c33d2c55d270#BKMK_Browser
There is no custom login page with a password field .. (only open id logins it seems)
The Resource Owner Password Credentials Grant (grant_type=password) flow is supported by Azure Active Directory. However, before using it, consider if it is truly required. As it says in the OAuth 2.0 RFC:
The resource owner password credentials (i.e., username and password) can be used directly as an authorization grant to obtain an access token. The credentials should only be used when there is a high degree of trust between the resource owner and the client (e.g., the client is part of the device operating system or a highly privileged application), and when other authorization grant types are not available (such as an authorization code).
If you have determined that the other supported flows will definitely not work for your scenario, then also be sure to follow the second bit of advice in the RFC:
Even though this grant type requires direct client access to the resource owner credentials, the resource owner credentials are used for a single request and are exchanged for an access token. This grant type can eliminate the need for the client to store the resource owner credentials for future use, by exchanging the credentials with a long-lived access token or refresh token.
(Emphasis added in both cases.)
There's a .NET and ADAL sample on GitHub that uses this flow, and it should be simple enough to implement in Python: https://github.com/AzureADSamples/NativeClient-Headless-DotNet
Edit: You can host your application anywhere you want, it doesn't need to be on Azure. This applies to all flows.

Categories