New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile Application Flow Example #104

Open
Miserlou opened this Issue Jan 21, 2014 · 7 comments

Comments

Projects
None yet
4 participants
@Miserlou

Miserlou commented Jan 21, 2014

This part is still missing documentation https://requests-oauthlib.readthedocs.org/en/latest/oauth2_workflow.html#mobile-application-flow

Does anybody have an example on hand? I'll write the docs, but would be nice to have some working code to start from. Is this tested?

@Lukasa

This comment has been minimized.

Member

Lukasa commented Jan 21, 2014

I think @ib-lundgren is in the best place to answer this.

@ib-lundgren

This comment has been minimized.

Member

ib-lundgren commented Jan 22, 2014

@Miserlou Happy to hear you want to write docs :)

I've scribbled together an untested example based on the Google OAuth 2 example but for the UserAgent flow. Let me know what errors you run into and I'll investigate...

# Credentials you get from registering a new application
client_id = '<the id you get from google>.apps.googleusercontent.com'
redirect_uri = 'https://your.registered/callback'

# OAuth endpoints given in the Google API documentation
authorization_base_url = "https://accounts.google.com/o/oauth2/auth"
scope = [
    "https://www.googleapis.com/auth/userinfo.email",
    "https://www.googleapis.com/auth/userinfo.profile"
]

from oauthlib.oauth2 import MobileClient
client = MobileClient(client_id)

from requests_oauthlib import OAuth2Session
google = OAuth2Session(client_id, client=client, scope=scope, redirect_uri=redirect_uri)

# Redirect user to Google for authorization
authorization_url, state = google.authorization_url(authorization_base_url,
    # offline for refresh token
    # force to always make user click authorize
    access_type="offline", approval_prompt="force")
print 'Please go here and authorize,', authorization_url

# Get the authorization verifier code from the callback url
redirect_response = raw_input('Paste the full redirect URL here:')

# Fetch the access token
google.token_from_fragment(redirect_response)

# Fetch a protected resource, i.e. user profile
r = google.get('https://www.googleapis.com/oauth2/v1/userinfo')
print r.content

Oh, and in the docs it is worth noting that python web apps want should not use this OAuth flow but it is perfect for desktop apps controlling a browser.

@Miserlou

This comment has been minimized.

Miserlou commented Jan 22, 2014

Okay cool! I actually like writing docs.. what's the point of writing a library if you don't tell people how to use it?

So we're using django-oauth-toolkit to associate clients with accounts on our web application. Because it's for mobile clients, there is no need for any callback or redirect APIs - there is no user interaction at all. The user has the client_id and a secret, it just needs to get an access token.

My purpose here is to write a python client with requests-oauthlib which can test an API configured in such a way.

So far, the only way I've found that is to do this:

response = requests.post(BASE_URL + ACCESS_TOKEN_PATH, {'grant_type': 'client_credentials', 'client_id': CLIENT_ID, 'client_secret': CLIENT_SECRET})
token = response.json() # {u'access_token': u'QMa1rtXXXXXXXXXXXXXXd', u'token_type': u'Bearer', u'expires_in': 36000, u'scope': u'read write'}
client = MobileApplicationClient(CLIENT_ID)
session = OAuth2Session(CLIENT_ID, client=client, token=token)
print session.get(API_URL + 'hello/') # <Response [200]>

Is there any way for requests-oauthlib to do the first part so I don't have to make the call directly with requests.post?

@ib-lundgren

This comment has been minimized.

Member

ib-lundgren commented Jan 23, 2014

Yes. Try this code

client = oauthlib.oauth2.BackendApplicationClient(CLIENT_ID)
session = requests_oauthlib.OAuth2Session(CLIENT_ID, client=client)
token = session.fetch_token("https://your.token/endpoint", client_id=CLIENT_ID, client_secret=CLIENT_SECRET)
# store token if you fancy
session.get(API_URL + 'hello/')

It might be worth adding an alias for this way/flow along the lines of "SingleUserClient" (or "NonInteractiveSingleUserClient") as that is the second use case for this flow and possibly even more common.

Note that CLIENT_ID is supplied three times but only the last one matters as the first two are for consistency with other flows but unused. Whereas the last one forces client_id and client_secret to be included in the request (since this is not mandated in the oauth 2 spec its not always done). By using BackendApplicationClient the grant_type will be set for you.

Also note that the type of client only matters when obtaining the token. Once you have the token you can just use default.

# sometime later
token = load_token_from_db(CLIENT_ID)
session = OAuth2Session(CLIENT_ID, token=token)
print session.get(API_URL + 'hello/') 
@whitechris969

This comment has been minimized.

whitechris969 commented Feb 27, 2014

I've tried the code with the Github API but I must be doing something wrong:

import requests_oauthlib
from oauthlib.oauth2 import BackendApplicationClient

client_id = "my_client_id"
client_secret = "my_client_secret"
token_url = 'https://github.com/login/oauth/access_token'

client = BackendApplicationClient(client_id)
session = requests_oauthlib.OAuth2Session(client_id, client=client)
token = session.fetch_token(token_url, client_id=client_id, client_secret=client_secret)
# store token if you fancy
session.get('https://api.github.com/user')

I get a response from github:
"error=bad_verification_code&error_description=The+code+passed+is+incorrect+or+expired.&error_uri=http%3A%2F%2Fdeveloper.github.com%2Fv3%2Foauth%2F%23bad-verification-code"

Then, I get an error:
"oauthlib.oauth2.rfc6749.errors.MissingTokenError"

I've double and triple-checked my client_id and client_secret keys

@whitechris969

This comment has been minimized.

whitechris969 commented Feb 27, 2014

I tried the above code (changing the client_id, secret, token url, etc) against my flask-oauthlib server and get an error: {"error": "invalid_client"}. The client_id and secret is valid and are in my database (and work when I do a web authorization flow).

EDIT: @lepture solved this issue with the flask-oauthlib server but still can't access github api above

@ib-lundgren

This comment has been minimized.

Member

ib-lundgren commented Mar 20, 2014

Sorry for the late reply. Great that it works with flask-oauthlib now.

For GitHub, I am not certain they actually support the client credentials grant that you are trying to use. I think they only support the authorization code one. However, you can use their API with your token using basic auth which is quite similar https://developer.github.com/v3/oauth/#non-web-application-flow .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment