I've recently been working on accessing the gmail API from a python script.
As the script needs to run on a server, without any form of web browser, I've had to implement the OAuth for Devices flow, as detailed at https://developers.google.com/accounts/docs/OAuth2ForDevices
This has gone just fine, as I've been testing with the "profile" scope, I've been getting access tokens as expected.
However, to switch over to the the actual token that my script needs, as I need to be authorised to write to the user's email, I've tried running my script with the scope www.googleapis.com/auth/gmail.compose, which returns the following:
{u'error_uri': u'code.google.com/apis/accounts/docs/OAuth2.html', u'error_description': u'Not authorized to request the scopes: [www.googleapis.com/auth/gmail.compose]', u'error': u'invalid_scope'}
At this point I got curious, and tried a variety of scopes, it seems fairly arbitrary to me, which ones would, and would not work.
Working:
www.googleapis.com/auth/calendar
www.google.com/m8/feeds
Not working:
mail.google.com/
www.googleapis.com/auth/gmail.compose
www.googleapis.com/auth/gmail.readonly
www.googleapis.com/auth/gmail.modify
www.googleapis.com/auth/contacts.readonly
I've uploaded my code to https://gist.github.com/Hanse00/3a861430b1543599b3ed for anyone interested in having a look.
The Gmail API is indeed enabled in the project console.
If anyone has any clue why this would be happening, I'd be glad to hear from you!
Note: I've had to edit the scopes as they count as links, and my reputation is not high enough to post them all. The scopes I tried were in fact the correct ones.
The Oauth2 for devices flow is less secure and is fine/allowed for some less-sensitive APIs however not all of them. The Gmail API does not allow using it.
You could have your server implement the web server flow (if you can run an http server on it) and then your client will redirect back there with a proper token you can retrieve+store:
https://developers.google.com/accounts/docs/OAuth2WebServer
Related
There are several options while setting up a service account. And it is confusing. I'm a beginner as well.
How may i Set-up the service account so that the script will successfully keep posting on blogger as posts.
Or could someone please assist me in setting up the Oauth client. There are two kinds of uri and I'm not sure how to find or create or obtain them. Or if there any way to setup successful authentication with Google so that posts can be created infinitely without any error.
This script will be run in python termux.
Any help is very appreciated.
Yes, i tried setting up this oauth client Id and it didn't work. There are errors. Perhaps because i don't have uris.
I also tried setting up the service account, where after execution of script, it says in termux something like, we are sorry but... Global.... No access... Something like that ...
I created a Flask-Webservice with Python that runs independently inside a docker container. I then uploaded the docker image to an Azure Container Registry. From there I can create a WebService (for Containers) with some few clicks in the Azure Portal, that runs this container. So far so good. It behaves just as I want it to.
But of course I don't want anyone to access the service. So I need some kind if authentication. Luckily (or so I thought) there is a built-in authentication-mechanism (I think it is based on OAuth ... I am not that well versed in security issues). Its documentation is a bit sparse on what actually happens and also concentrates on solutions in C#.
I first created a project with Google as described here and then configured the WebApp-Authentication with the Client-Id and Secret. I of course gave Google a java script source and callback-url, too.
When I now log off my Google account and try a GET-Request to my Webservice in the Browser (the GET should just return a "hello world"-String), I am greeted with a Login Screen ... just as I expected.
When I now login to Google again, I am redirected to the callback-url in the browser with some kind of information in the parameters.
a token perhaps? It looks something like this:
https://myapp.azurewebsites.net/.auth/login/google/callback?state=redirxxx&code=xxx&authuser=xxx&session_state=xxx&prompt=xxx).
Here something goes wrong, because an error appears.
An error occurred.
Sorry, the page you are looking for is currently unavailable.
Please try again later.
If you are the system administrator of this resource then you should check the error log for details.
Faithfully yours, nginx.
As far as I now, nginx is a server software that hosts my code. I can imagine that it also should handle the authentication process. It obviously lets all requests through to my code when authentication is turned off, but blocks un-authenticated accesses otherwise and redirects to the google login. Google then checks if your account is authorized for the application and redirects you to the callback with the access token along with it. This then returns a cookie which should grant my browser access to the app. (I am just reproducing the documentation here).
So my question is: What goes wrong. Does my Browser not accept the cookie. Did I something wrong when configuring Google+ or the Authentication in the WebApp. Do I have to use a certain development stack to use the authentication. Is it not supported for any of the technologies I use (Python, Flask...).
EDIT
#miknik:
In Microsofts documentation of the authentication/authorization it says
The authentication and authorization module runs in the same sandbox
as your application code. When it's enabled, every incoming HTTP
request passes through it before being handled by your application
code.
...
The module runs separately from your application code and is
configured using app settings. No SDKs, specific languages, or changes
to your application code are required.
So while you are probably right that the information in the callback-redirect is the authorization grant/code and that after that this code should now be used to get an access token from Google, I don't quite understand how this would work in my situation.
As far as I can see it Microsofts WebApp for Container-Resource on Azure should take care of getting the token automatically and return it as part of the response to the callback-request. The documentation states 4 steps:
Sign user in: Redirects client to /.auth/login/.
Post-authentication: Provider redirects client to /.auth/login//callback.
Establish authenticated session: App Service adds authenticated cookie to response.
Serve authenticated content: Client includes authentication cookie in subsequent requests (automatically handled by browser).
It seems to me that step 2 fails and that that would be exactly what you wrote: that the authorization grant is to be used by the server to get the access token but isn't.
But I also don't have any control over that. Perhaps someone could clear things up by correcting me on some other things:
First I can't quite figure out which parts of my problem represent which role in the OAuth-scheme.
I think I am the Owner, and by adding users to the list in the Google+-Project I authorize them to use my service.
Google is obviously the authorization server
my WebService (or better yet my WebApp for Containers) is the resource server
and finally an application or postman that does the requests is the Client
In the descriptions of OAuth I read the problematic step boils down to: the resource server gets the access token from the authorization server and passes it along to the client. And Azures WebApps Resource is prompted (and enabled) to do so by being called with the callback-url. Am I right somewhere in this?
Alas, I agree that I don't quite understand the whole protocol. But I find most descriptions on the net less than helpful because they are not specific to Azure. If anyone knows a good explanation, general or Azure-specific, please make a comment.
I found a way to make it work and I try to explain what went wrong as good as I can. Please correct me if I go wrong or use the wrong words.
As I suspected the problem wasn't so much that I didn't understand OAuth (or at least how Azure manages it) but the inner workings of the Azure WebApp Service (plus some bad programming on my part). Azure runs an own Server and is not using the built-in server of flask. The actual problem was that my flask-program didn't implement a WSGI-Interface. As I could gather this is another standard for python scripts to interact with any server. So while rudimentary calls from the server (I think Azure uses nginx) were possible, more elaborate calls, like the redirect to the callback url went to dev/null.
I build a new app following this tutorial and then secured it by following the authentication/authorization-tutorial and everything worked fine. The code in the tutorial implements WSGI and is probably more conform to what Azure expects. My docker solution was too simple.
My conclusion: read up on this WSGI-standard that flask always warned me about and I didn't listen and implement it in any code that goes beyond fiddeling around in development.
I'm struggling to find the problem since two days without any idea why I get this error now even though the app was fully functional one month before.
Among the tasks done by the web app, it makes an Admin SDK API call to get the list of members of a group. The app has the scope https://www.googleapis.com/auth/admin.directory.group.readonly, but I get the 403 error "Not Authorized to access this resource/api" (I verified that the Admin SDK API was enabled in Google's Console).
By the way, the app had no problem to make a request to Google Classroom API before.
The most incredible thing here is that the app secrets have been generated by an admin account. And that I get this error when I log into the app with this same admin account. However, when I do tests with the documentation's (https://developers.google.com/admin-sdk/directory/v1/reference/members/list#response) I get a 200 response without a problem with exactly the same authorized scope.
Getting the list of members worked without a problem before with this admin account. Since then, nothing has changed in source code or in configuration so far as I know. So I think the problem may be related to the client secrets in some way, but I have no idea how.
This web app will only be used by this admin account
In my research on StackOverflow, I found most things talking about "Google Apps Domain-Wide Delegation of Authority" (https://developers.google.com/admin-sdk/directory/v1/guides/delegation), but I never used this when it worked before. And I would like to avoid this.
Do you have an idea why I get this 403 error with the web app even though it works when just testing the request in the documentation and I'm using a Super-Admin account ?
Edit: I've now tested a simple snippet with "Google Apps Domain-Wide Delegation of Authority" based on this gist https://gist.github.com/MeLight/1f4517560a9761317d13ebb2cdc670d3 and the snippet alone works.
However, when using it inside my app, I still get the 403 error. I'm getting insane, what could be the permission issue?
Finally, after knocking my head against the wall, I found what was the problem:
the groupKey used to get the members of a group https://www.googleapis.com/admin/directory/v1/groups/groupKey/members had a trailing whitespace.
Yes, that's all. I just had to strip the string in the code (one line in Python) and it all went well.
All I did, searching for permissions / client ID / Delegation of Authority was for nuts it seems.
Seriously, the 403 error “Not Authorized to access this resource/api” of Google's API could have been more explicit.
I'm using Spotipy to get some spotify data from an authorized user but I can't understand how to authorize my account.
Am I correct in thinking that a server is required that hosts something at http://www.myapp.com/callback ? What needs to be returned? Maybe I'm completely missing something here... I'm very confused by the whole required redirect URI thing...
I am trying to make a program, without website, so how should I handle authorization? What exactly should the redirect URI do?
Thanks
Edit:
Using http://localhost:8888/callback as my redirect URI now and that works. I'm not even sure why since nothing is running on that port.
Disclaimer: I know nothing about Spotify's API. But I have worked with similar APIs in the past (or even designed them). What I assume is that they use some kind of OpenID/OAuth authorization mechanism.
The very nature of these APIs is that they work through the browser! The idea is that MyApp doesn't have your actual Spotify credentials, but instead some signed token it can use.
To communicate this token to the MyApp, there are the server-callbacks, outlined in your question. Because all the browser can do is to redirect to a special URL you provide, with some info added.
So there are conceptually two ways to deal with this:
the easy, server-based one: you in fact register a myapp.com. When your app tries to authorize with spotify, it first creates a unique resource (myapp.com/authrequests/HASH-NUMBER), and communicates this as callback. Then it goes through the motions of making spotify authorize it, and once these are finished, there will have been a call to myapp.com/authrequests/HASH-NUMBER/ADDITIONAL-INFO. So while your app is waiting for this to happen, it has to poll (or open a websocket and listen to that) myapp.com. Complicated? Wait, it gets better!
the harder, OS-dependent one: you write an application that registers itself as protocol-provider with your browsers. E.g. my company does that with the protocol "ableton". Thus we can make the browser generate "ableton://AUTHORIZATION-REQUEST-RESULT" URLs which will then be communicated through Browser and OS to the running application, and thus you receive the necessary secret.
HTH
I'm looking to set up Django to use OAuth2 to authenticate users for a service that I'm running, but I'm having a bit of difficulty understanding how the tokens are passed around.
I've been working my way through this tutorial: https://django-oauth-toolkit.readthedocs.org/en/0.7.0/tutorial/tutorial_01.html. I've been able to get a server up and running as the OAuth provider, and it seems to be working as it should. I'm able to log in to it and set up an application. The difficulty I'm having is figuring out how the various tokens are passed around.
Suppose that my OAuth provider is sitting on one server - let's call this Provider.com - and my service that I'm wanting authenticated is on service.com. When a user first tries to make a request to the service, they first need to authenticate against the Provider. So they click on a login button which directs them to Provider.com. They enter their credentials. If everything is set up correctly on the server, they should be presented with a prompt that gives them a chance to allow or deny Service.com from accessing their account on Provider.com. Supposing that they click Allow, they are then redirected to Service.com, and are given a token. On future calls to Service.com, they pass in the token, and are, in theory, able to make authenticated calls.
The problem I'm having understanding is this: At what point do the Provider and the Service communicate? If a call comes in to the Service, how does it know that the authentication token passed in with the call is valid? There's know way it could know that a particular token is valid unless: A) it recognizes that same token from a previous call which was also authenticated or B) it talks to the OAuth 2 provider and verifies the authenticity of the token.
A diagram like the one found here shows the process in the browser:
At the end of this, it has the Client App sending the authentication code, client id, and client secret to the OAuth2 provider. In the previously mentioned tutorial, it isn't really clear how this is actually done. In the tutorial, the provider and the service are on the same machine, and it would appear that they also share the same database.
This this brings about my question: How does one host a Django-based OAuth provider on a separate server than the resource/service being accessed? Is this possible?
From this other post, it indicates that this might not be possible: https://stackoverflow.com/a/26656538/1096385 Is that indeed the case, at least with the existing Django OAuth2 provider framework?
It depends on the oauth2 flow you're using. It seems like you're using authentication code.
In that case:
service.com sends the browser to provider.com for user authentication (uri contains service.com client_id and redirect_uri)
User authenticates on provider.com, then the browser is redirected to service.com's redirect_uri with a ?code parameter.
On your server side, handle this code parameter and ask for a token with it.
See https://aaronparecki.com/articles/2012/07/29/1/oauth2-simplified#web-server-apps