Necessary to implement fetch_token if using access token immediately? - authlib

When using Authlib with a service like GitHub, is it necessary to implement fetch_token if we only want to use the token once in order to retrieve a user's profile (from https://api.github.com/user)? I see that "If OAuth login is what you want ONLY, you don’t need fetch_token at all," though technically we want a bit more than that, since we're calling github.get after calling github.authorize_access_token:
import os
from authlib.flask.client import OAuth
from authlib.client.apps import github
from flask import Flask, redirect, session, url_for
app = Flask(__name__)
oauth = OAuth(app)
app.config["GITHUB_CLIENT_ID"] = os.getenv("GITHUB_CLIENT_ID")
app.config["GITHUB_CLIENT_SECRET"] = os.getenv("GITHUB_CLIENT_SECRET")
app.config["GITHUB_CLIENT_KWARGS"] = {"scope": "user:email"}
github.register_to(oauth)
...
#app.route("/login")
def login():
redirect_uri = url_for("authorize", _external=True)
return github.authorize_redirect(redirect_uri)
#app.route("/authorize")
def authorize():
token = github.authorize_access_token()
user = github.get("user").json()
session["login"] = user["login"]
return redirect(url_for("index"))
It appears that github.get("user") succeeds (as does a call to, e.g., github.profile) even without storing token in, e.g., session or a database and returning it via calls to a fetch_token function?

No, you don't need a fetch_token.
github.authorize_access_token()
print(github.session.token)

Related

Limit token scope server-side

My login procedure allows admins to select an account that they would like to login-as. For that I can login as that particular user and issue the authorization code, as usual.
Now, what I would like is to extend this setup to allow some other admins to login with "read-only" access. This can easily be mapped to our API by use of certain scopes and removing some other scope.
For the oauth process to work, I do need a way to issue oauth tokens that come with a scope that has been limited server side (less scope than the actual client - server-side because read-only is enforced).
I imagine that I might need to write a new GrantType and probably also have to track state somehow, but I am unclear on how exactly I should use create_authorization_response() in this case.
Ok, after some fiddling around, I found a solution. It essentially creates a custom Oauth2Request (usually client-provided, in our case, modified server-side).
Some rough outline of the code:
from urllib.parse import urlencode, parse_qs, urlparse
# obtain query string as dictionary
query_dict = parse_qs(request.query_string.decode("utf-8"))
# customize scope for this request
query_dict["scope"] = ["profile"]
# We here setup a custom Oauth2Request as we have change the scope in
# the query_dict
req = OAuth2Request(
"POST", request.base_url + "?" + urlencode(query_dict, doseq=True)
)
return authorization.create_authorization_response(grant_user=user, request=req)

Flutter + Django OAuth integration

I am using Flutter as front end and Django for back end purpose. I am trying to integrate Google and Facebook OAuth in the app and using some flutter libraires I am able to fetch user details and access token in front end. Now the question is how do I handle users and access tokens for them and verify them through drf. I could totally depend on drf for OAuth and create users using http request in front end using OAuth toolikt for Django but is there a way that I handle incoming auth tokens in front end and verify them in drf so as to register them in backend.
#api_view(http_method_names=['POST'])
#permission_classes([AllowAny])
#psa()
def exchange_token(request, backend):
serializer = SocialSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
# This is the key line of code: with the #psa() decorator above,
# it engages the PSA machinery to perform whatever social authentication
# steps are configured in your SOCIAL_AUTH_PIPELINE. At the end, it either
# hands you a populated User model of whatever type you've configured in
# your project, or None.
user = request.backend.do_auth(serializer.validated_data['access_token'])
if user:
# if using some other token back-end than DRF's built-in TokenAuthentication,
# you'll need to customize this to get an appropriate token object
token, _ = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
else:
return Response(
{'errors': {'token': 'Invalid token'}},
status=status.HTTP_400_BAD_REQUEST,
)
There’s just a little more that needs to go in your settings (full code), and then you’re all set:
AUTHENTICATION_BACKENDS = (
'social_core.backends.google.GoogleOAuth2',
'social_core.backends.facebook.FacebookOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
for key in ['GOOGLE_OAUTH2_KEY',
'GOOGLE_OAUTH2_SECRET',
'FACEBOOK_KEY',
'FACEBOOK_SECRET']:
# Use exec instead of eval here because we're not just trying to evaluate a dynamic value here;
# we're setting a module attribute whose name varies.
exec("SOCIAL_AUTH_{key} = os.environ.get('{key}')".format(key=key))
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.user.get_username',
'social_core.pipeline.social_auth.associate_by_email',
'social_core.pipeline.user.create_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
)
Add a mapping to this function in your urls.py, and you’re all set!

List of Spreadsheets Gdata OAuth2

Getting a list of spreadsheets with spreadsheet api in Gdata,
Oauth1 Way
spreadSheetService = gdata.spreadsheet.service.SpreadsheetsService()
spreadSheetService.SetOAuthInputParameters(gdata.auth.OAuthSignatureMethod.HMAC_SHA1,self.CONSUMER_KEY,self.CONSUMER_SECRET,two_legged_oauth=True, requestor_id=self.requestor_id)
spreadSheetService.GetSpreadsheetsFeed(query = q)
But since spreadSheetService is not available for OAuth2 because of this won't fix issue #594
How do I query for a list of spreadsheets with gdata.spreadsheets.client.SpreadsheetClient ?
(assuming Python)
I was able to use gd_client.auth_token = gdata.gauth.OAuth2TokenFromCredentials(credentials) to take a credentials object created by an OAuth2 flow (using the oauth2client) and use this with the gdata library.
Full example here (for a command-line app):
# Do OAuth2 stuff to create credentials object
from oauth2client.file import Storage
from oauth2client.client import flow_from_clientsecrets
from oauth2client.tools import run
storage = Storage("creds.dat")
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = run(flow_from_clientsecrets("client_secrets.json", scope=["https://spreadsheets.google.com/feeds"]), storage)
# Use it within gdata
import gdata.spreadsheets.client
import gdata.gauth
gd_client = gdata.spreadsheets.client.SpreadsheetsClient()
gd_client.auth_token = gdata.gauth.OAuth2TokenFromCredentials(credentials)
print gd_client.get_spreadsheets()
If you're specifically looking for 2-legged, the same technique works, but you will need to create a different type of credentials object. See the following recent answer regarding how to create this: Using Spreadsheet API OAuth2 with Certificate Authentication
Here's a variation that writes an OAuth 2.0 Bearer auth header directly to the request and allows you to continue to use the older gdata.spreadsheet.service.SpreadsheetsService style client code:
import httplib2
# Do OAuth2 stuff to create credentials object
from oauth2client.file import Storage
from oauth2client.client import flow_from_clientsecrets
from oauth2client.tools import tools
storage = Storage("creds.dat")
credentials = storage.get()
if credentials is None or credentials.invalid:
flags = tools.argparser.parse_args(args=[])
flow = flow_from_clientsecrets("client_secrets.json", scope=["https://spreadsheets.google.com/feeds"])
credentials = tools.run_flow(flow, storage, flags)
if credentials.access_token_expired:
credentials.refresh(httplib2.Http())
# Use it within old gdata
import gdata.spreadsheet.service
import gdata.service
client = gdata.spreadsheet.service.SpreadsheetsService(
additional_headers={'Authorization' : 'Bearer %s' % credentials.access_token})
#public example
entry = client.GetSpreadsheetsFeed('0AoFkkLP2MB8kdFd4bEJ5VzR2RVdBQkVuSW91WE1zZkE')
print entry.title

Automatic mediawiki import with powershell_script

I found a nice script to import xml using powershell
http://slash4.de/tutorials/Automatic_mediawiki_page_import_powershell_script
Currently I don't get them run. I'm sure, this is a problem with the permissons.
First I set the wiki to allow anybody to upload an import
$wgGroupPermissions['*']['import'] = true;
$wgGroupPermissions['*']['importupload'] = true;
Then I get this error: Import failed: Loss of session data.
I try to figure out to pass the user and password to this line in powershell
$req.Credentials = [System.Net.CredentialCache]::DefaultCredentials
and changed it to
$req.Credentials = [System.Net.CredentialCache]::("user", "pass")
Import failed: Loss of session data. Again?
How can I pass the user/password to the website?
The Loss of session data error is generated when the edit token sent with the request does not have the expected value.
In the script you linked to, the $wikiURL string contains editToken=12345. That does not look like a valid MediaWiki edit token, so it's not surprising that it will fail.
In current versions of MediaWiki, the edit token for non-logged-in users is always +\. You could try replacing 12345 in the script with that (or, rather, with its URL-encoded version %2B%5C) and see if it helps.

Nothing except "None" returned for my Python web.py Facebook app when I turn on "OAuth 2.0 for Canvas"

I am a beginning Facebook app developer, but I'm an experienced developer. I'm using web.py as my web framework, and to make matters a bit worse, I'm new to Python.
I'm running into an issue, where when I try to switch over to using the newer "OAuth 2.0 for Canvas", I simply can't get anything to work. The only thing being returned in my Facebook app is "None".
My motivation for turning on OAuth 2.0 is because it sounds like Facebook is going to force it by July, and I might as well learn it now and now have to rewrite it in a few weeks.
I turned on "OAuth 2.0 for Canvas" in the Advanced Settings, and rewrote my code to look for "signed_request" that is POSTed to my server whenever my test user tries to access my app.
My code is the following (I've removed debugging statements and error checking for brevity):
#!/usr/bin/env python
import base64
import web
import minifb
import urllib
import json
FbApiKey = "AAAAAA"
FbActualSecret = "BBBBBB"
CanvasURL = "http://1.2.3.4/fb/"
RedirectURL="http://apps.facebook.com/CCCCCCCC/"
RegURL = 'https://graph.facebook.com/oauth/authorize?client_id=%s&redirect_uri=%s&type=user_agent&display=page' % (FbApiKey, RedirectURL)
urls = (
'/fb/', 'index',
)
app = web.application(urls, locals())
def authorize():
args = web.input()
signed_request = args['signed_request']
#split the signed_request via the .
strings = signed_request.split('.')
hmac = strings[0]
encoded = strings[1]
#since uslsafe_b64decode requires padding, add the proper padding
numPads = len(encoded) % 4
encoded = encoded + "=" * numPads
unencoded = base64.urlsafe_b64decode(str(encoded))
#convert signedRequest into a dictionary
signedRequest = json.loads(unencoded)
try:
#try to find the oauth_token, if it's not there, then
#redirect to the login page
access_token = signedRequest['oauth_token']
print(access_token)
except:
print("Access token not found, redirect user to login")
redirect = "<script type=\"text/javascript\">\ntop.location.href=\"" +_RegURL + "\";\n</script>"
print(redirect)
return redirect
# Do something on the canvas page
returnString = "<html><body>Hello</body></html>"
print(returnString)
class index:
def GET(self):
authorize()
def POST(self):
authorize()
if __name__ == "__main__":
app.run()
For the time being, I want to concentrate on the case where the user is already logged in, so assume that oauth_token is found.
My question is: Why is my "Hello" not being outputted, and instead all I see is "None"?
It appears that I'm missing something very fundamental, because I swear to you, I've scoured the Internet for solutions, and I've read the Facebook pages on this many times. Similarly, I've found many good blogs and stackoverflow questions that document precisely how to use OAuth 2.0 and signed_request. But the fact that I am getting a proper oauth_token, but my only output is "None" makes me think there is something fundamental that I'm doing incorrectly. I realize that "None" is a special word in python, so maybe that's the cause, but I can't pin down exactly what I'm doing wrong.
When I turn off OAuth 2.0, and revert my code to look for the older POST data, I'm able to easily print stuff to the screen.
Any help on this would be greatly appreciated!
How embarrassing!
In my authorize function, I return a string. But since class index is calling authorize, it needs to be returned from the class, not from authorize. If I return the return from authorize, it works.