How to grant acces permantly with OAuth2 - oauth-2.0

I have tried to use OAuth2 to build a group settings service with the following:
def groupSettingsService(request):
CLIENT_SECRETS = os.path.join(os.path.dirname(__file__), 'client_secrets.json')
FLOW = client.flow_from_clientsecrets(CLIENT_SECRETS, scope=['https://www.googleapis.com/auth/apps.groups.settings'], message=tools.message_if_missing(CLIENT_SECRETS))
storage = Storage('groups-settings.dat')
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = run(FLOW, storage)
http = httplib2.Http()
http = credentials.authorize(http)
return discovery.build('groupssettings', 'v1', http=http)
But the problem is when the token isn't valid anymore (expires) it redirect to a page to tell a user to grant access again to that scope...things that is inappropriate for API calls !
is there a way to work with a username/password or client_secret to grant a full access permanently to the API without asking to grant access or not ?

You need to ask for access_type=offline when you redirect the user to Google.
You will than get an code, which can be exchanged (by POSTing with your client_id and client_secret) into an access_token (that is the one you are already using) and a refresh_token.
When your access_token expires, you can POST the refresh_token, client_id and client_secret to get another access_token. You can do that multiple times if you need (or weeks later...)

Did you save the credentials to storage upon getting a credentials successfully?

Related

Google OAuth2 refresh expires & does not renew

I followed this tutorial EXACTLY, however
#app.route('/test')
def test_api_request():
if 'credentials' not in flask.session:
return flask.redirect('authorize')
# Load credentials from the session.
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
drive = googleapiclient.discovery.build(
API_SERVICE_NAME, API_VERSION, credentials=credentials)
files = drive.files().list().execute()
# Save credentials back to session in case access token was refreshed.
# ACTION ITEM: In a production app, you likely want to save these
# credentials in a persistent database instead.
flask.session['credentials'] = credentials_to_dict(credentials)
return flask.jsonify(**files)
However in this part:
credentials = google.oauth2.credentials.Credentials(
**flask.session['credentials'])
The refresh token expires after an hour, with this error:
The credentials do not contain the necessary fields need to refresh the access token. You must specify refresh_token, token_uri, client_id, and client_secret.
But clearly in the flask session the dict object is there:
{'client_id': '<COMMENTED_OUT>.apps.googleusercontent.com',
'client_secret': '<COMMENTED_OUT>',
'refresh_token': None,
'scopes': ['https://spreadsheets.google.com/feeds',
'https://www.googleapis.com/auth/drive',
'https://mail.google.com/'],
'token': '<COMMENTED_OUT>',
'token_uri': 'https://oauth2.googleapis.com/token'}
I believe the google tutorial auto-refreshes the token
Two questions
1) Do i need to manually "refresh" the refresh token? The comment in the tutorial says "Save credentials back to session in case access token was refreshed".. which implies that it's refreshed automatically
2) Is this because the app is still in unverified status?
Looking at the dict, the refresh token is missing:
'refresh_token': None,
You need this token in order to refresh your access token after it expires. The refresh token is only provided in the JSON response if the user saw a consent screen (the one that lists the scopes being requested). If the user has previously approved access, and the scopes haven't changed, the OAuth flow will skip that screen if the user is sent back into the flow, and therefore not return the refresh token.
What likely happened is that during your testing you approved access once, but didn't store the refresh token correctly. Further attempts to approve access didn't return the refresh token, hence your inability to refresh the access token.
To ensure a refresh token is always returned, set the URL parameter prompt=consent in the authorization URL:
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true'
prompt='consent')
(It's documented in the "HTTP/REST" tab here).
Alternatively, visit and revoke access to your application. The next time you go through the OAuth flow you should see the consent screen again, and get a new refresh token.

When exchanging the Amazon Alexa grant_code for an access_token, where are the credentials?

I am trying to write the exchange and access endpoints and the docs here (https://developer.amazon.com/public/solutions/alexa/alexa-skills-kit/docs/linking-an-alexa-user-with-a-user-in-your-system#h2_login) are not clear on a couple of things:
how is the call to exchange a grant code for access token made - is it GET with credentials in QS or is it a POST with credentials in a body?
Is the access token delivered only in the JSON for an intent call or is it set properly as a bearer token?
It's a POST with credentials in the request body. Amazon follows the Oauth2 RFC correctly in this case.
The access token is delivered by Amazon only in the JSON for the intent request and not properly set as a bearer. This is annoying.
In my case, I had to hack around it by first validating if the request was a valid alexa request which contained a session with an access token, then setting the HTTP_AUTHORIZATION header to Bearer <token>, then using existing request auth logic to authenticate (I was using Django with django-oauth-toolkit, so YMMV if you're using something else).
That code looks something like this:
# get the access_token from the POST request
if access_token is not None:
request.META["HTTP_AUTHORIZATION"] = "Bearer " + access_token
if not hasattr(request, 'user') or request.user.is_anonymous():
user = authenticate(request=request)
if user:
request.user = request._cached_user = user
if request.user.is_authenticated():
# Do whatever with the logged in user

Google OAuth Token Force Expiration Not Happening

I was forcibly trying to expire the Google API OAuth Access Token for the Contacts API.
Here is the general outline of my test:
Get the code:
auth_uri = flow.step1_get_authorize_url()
redirect(auth_uri) code here
Exchange it for the access token:
code = request.GET.get('code')
credentials = flow.step2_exchange(code)
store_for_reuse(key, credentials)
store_for_refresh(key, credentials)
Get a new access token:
credentials_original = get_for_reuse(key)
credentials_for_refresh = get_for_refresh(key)
credentials_for_refresh.refresh(http)
Now, I can verify that the two credentials object have different access token values
credentials_original.access_token != credentials_for_refresh.acesss_token
Surprisingly, I can still make successful API calls with the original access token. Shouldn't the original one expire when the new access token is issued?
Getting a new token does not expire existing tokens. Tokens will be valid for their designated lifespan unless explicitly revoked.

get token, store it, refresh it if expired using oauth2 gem in ruby

I am working on script to get google contacts using google contacts api gem. I am able to access the token successfully using this code:
require 'rubygems'
require 'launchy'
require 'oauth2'
require 'googlecontacts'
require 'google_contacts_api'
# Get your credentials from the console
CLIENT_ID = 'your Id'
CLIENT_SECRET = 'your Secret'
OAUTH_SCOPE = 'https://www.google.com/m8/feeds'
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
client = OAuth2::Client.new(CLIENT_ID, CLIENT_SECRET,site: 'https://accounts.google.com',token_url: '/o/oauth2/token', authorize_url: '/o/oauth2/auth')
url = client.auth_code.authorize_url(scope: OAUTH_SCOPE, redirect_uri: REDIRECT_URI)
Launchy.open(url)
$stdout.write "Enter authorization code: "
code = gets.chomp
token = client.auth_code.get_token(code, :redirect_uri => REDIRECT_URI)
PROBLEM:
I know that this is not the best way to do it, because it is tiring. every time I run the script the user has give access instructions. And also I have to manually copy paste the token from the browser to the terminal.
QUESTION:
How can be able to store the retrieved token, and when it expired how can I refresh it?
It looks like you're using the oauth2 library to get the access token. The AccessToken class has to_hash() and from_hash() methods, which you can use to serialize and deserialize the token once you've gotten it, as well as a refresh() method to refresh the access token once it's expired. If this is a command line script you can use a hidden file in the user's home directory to store the serialized token.
During the first authentication, you got an authorization token and a refresh token.
Store the refresh_token (in session if it's a web app, or any other "volatile" persistence scheme, or in last case in database).
Using the refresh_token, ask for a new token like described in Google OAuth2 WebServer documentation.
If this is not a webserver application, maybe you should consider use other OAuth2 authentication flows.
In order to receive a refresh token, you need to alter the url.
in OAuth2:
url = client.auth_code.authorize_url(scope: OAUTH_SCOPE, access_type: "offline", redirect_uri: REDIRECT_URI)
Then it will be available like Erik Koleda mentions.

Oauth token invalid when trying to use the same token twice?

Hey guys I'm trying to use the Dailybooth api, and getting access to the user via oauth.. I'm very new to ruby/rails and this is my first time working with an api and oauth. Here's the thing though
if params[:code] #if the code is avaible, use the code
dailybooth.oauth_token(params[:code])
#oauth_token_ = params[:code]
session[:oauth_code] = #oauth_token_
else #else, attempt to use the session
if session[:oauth_code] #If session is set
dailybooth.oauth_token(session[:oauth_code]) #sign in using session
else #else, check if the params[:code] is set and use, or redirect to dailybooth authorize
if params[:code]
dailybooth.oauth_token(params[:code])
#oauth_token_ = params[:code]
session[:oauth_code] = #oauth_token_
else
#first redirect the user to the authorize_url
redirect_to dailybooth.authorize_url
end
end
end
#make request to the api
#info = dailybooth.get('/users.json')
if #info['error']
if #info['error']['error_code']
if #info['error']['error_code'] == 302 #if getting invalid token, request another token.
session[:oauth_code] = nil
#info = "Getting Errorcode 302, invalid token."
#first redirect the user to the authorize_url
# redirect_to dailybooth.authorize_url
end
end
end
So, that is the index of my app. When I first go to the index, I'm immediatly transfered to dailybooth to authorize my account, after authorizing, dailybooth returns me to my site with a URL that looks like so "http://site.com/?code=XXX" and "dailybooth.get('/users.json')" goes to work, it actually get's the user information. but if I try to refresh, going to the same "http://site.com/?code=XXX" it'll say the token is invalid. Why is that? The token was just valid and now it's not working. Any suggestions? Is this an error on my end?
My guess is that the problem is you are storing the dailybooth OAuth Token and not the Access Token. Your call to dailybooth.oauth_token(...) probably generates an Access Token and you should be storing that, not the params[:code] (OAuth Token). Generally OAuth works like this:
The user is redirected to the site to give your application access.
The user is redirected back to your site with an OAuth Token giving you access to certain parts of the API or external site.
You trade that OAuth Token in for an Access Token that you can use to access the API and site at any time.
Once that OAuth Token has been traded in for an Access Token it can't be used again, but the Access Token can.
Check the dailybooth object, you can probably access the actual Access Token somehow (dailybooth.token or dailybooth.access_token maybe?). Make sure you store that access token in your session or database, and then use that access token upon returning to the page instead of the params[:code] that has at this point become invalid.
I cannot help you with Ruby as I've never used it, however I can add to the comment above what is the flow in OAuth 2 (which is definitely the preferred choice to use):
The user goes to your site and needs to access functionality that requires the use of a Third-Party OAuth provider API (your site is Consumer and the Third Party is Provider and I will call them this way below)
Consumer makes internal HTTPS request to the Provider to get unauthorized Request token.
Consumer redirects the user to the Provider with the Request token it just received in step 2, in order for this token to be authorized by the user at the Provider's site.
After the user authorize the Request token (eventually), the Provider creates Verifier String and redirects the user back to the callback with this Verifier String and the Request Token, which is very important. In fact, this is the part of OAuth 2, which identifies that the user has really authorized the token (the Verifier String).
Consumer makes internal HTTPS request with the Request token and this Verifier String to get Authorized Access Token.
At this point if the Consumer receive Access Token, he has the right to access the protected API with the user's permission.
Here is a good explanation of why the Verifier is important and added to the protocol:
http://hueniverse.com/2009/04/explaining-the-oauth-session-fixation-attack/

Resources