I've been asked to provide a "Login with Facebook" functionality to an iOS app I am creating.
The app connects to a REST api created with Piston, the web application is created with Django and uses social_auth. The application also has a Facebook login.
My thought was to create a service 'FBLogin' providing just the Facebook profile UID (separate FB login procedure on iPhone to get the ID). Using the SocialAuth models I can query the DB with uid and provider to fetch the user... but how can i use the authentication mechanism to get this user instance authenticated?
Any ideas on getting this right?
This just doesn't feel good ... getting the user instance authenticated is a pain...
The username password authentication is already implemented ... without a problem.
Btw, don't have django experience ... do have a lot of other development experience so understanding python and django isn't that hard :)
Tx
Y
It doesn't really seem to be documented anywhere, but you can do this in your REST handler:
from social_auth.backends.pipeline.social import associate_user
from social_auth.backends.facebook import FacebookBackend
from social_auth.models import UserSocialAuth
myextra_data = {
'access_token' : 'jfkdlfsdgeyejfghfdsjdfpoweipuo',
'id' : 123456789,
}
usa, created = UserSocialAuth.objects.get_or_create(provider = 'facebook',
uid=123456789)
usa.user = user
usa.extra_data = myextra_data
usa.save()
if created:
associate_user(backend=FacebookBackend, user=user, uid=usa.uid)
These get pretty vendor-specific in terms of how data gets formatted in extra_data so YMMV
Related
A better title for this question would be: how to use keycloak in django grpc framework.
I want to handle user registration with keycloak. this is the my service without keycloak:
class UserService(Service):
def Create(self, request, context):
serializer = UserProtoSerializer(message=request)
serializer.is_valid(raise_exception=True)
serializer.save()
# some code for sending activation email
return serializer.message
and I'm using a custom user.
I have created a realm and client in keycloak admin console. what is the best way of relating these two? shall I try using drf-keycloak-auth or python-keycloak?
with a custom authentication it's better to use python-keycloak . you can create a user object both in keycloak and your database and save different information about user in them.
the documentation is almost clear if you have followed the steps in keycloak documentation. the thing I couldn't quite understand was input arguments for keycloak_admin.create_user to create a new user in a realm named 'test'. this the confusing code in documentation:
keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/",
username='example-admin',
password='secret',
realm_name="master",
user_realm_name="only_if_other_realm_than_master",
client_secret_key="client-secret",
verify=True)
now the thing you should know before jumping from keycloak documentations to python-keycloak documentations is that you should give one user in your new realm administration role. check this link.
keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/",
username='test#user.com',
password='123',
realm_name="test",
verify=True)
to generate client secret check: https://wjw465150.gitbooks.io/keycloak-documentation/content/server_admin/topics/clients/oidc/confidential.html
Using:
Django 1.11
Python 3.6
DRF with JWT in FE
I understand that the Django admin uses a session, and basic authentication.
What I did so far: Replaced the Django Admin authentication signin page with AWS-Cognito:
The user goes to domain/admin/*, redirected to signin in AWS
On successful signin the user is redirected to the redirect_uri, leads to a Django View
In the view I replace the code with tokens
I can't navigate to any Admin page - I am trying to redirect, but that doesn't work since I didn't login() the User
Stuck - I would like to associate the User with the fetched tokens and authenticate with every Admin page request, and when the user logs out delete the tokens
What to do next?
When I use JWT with the Front End application, every request.META has HTTP_AUTHORIZATION, and uses a suitable backend.
I know how to add backends, and potentially leverage the user.backend (I also use Cognito-JWT for other FE portions, so already wrote BE for that)
I need to find a way to replace the Django Admin sessions authentication with the fetched token
Thank you!
EDIT:
If I login() the user, and set it to a model backend that I have already I can navigate to any admin page - but using the session that I created when I logged the user in.
I would like to have the user be set to a new model backend, with authentication that uses a token (from Django backend docs):
class MyBackend:
def authenticate(self, request, token=None):
# Check the token and return a user.
...
How do I make the different Admin pages requests pass the token to the authentication?
Where do I store the token? (I could make a NewUserModel that is 1-1 with the Django User model, and place a token field there)
I am thinking of writing a middleware to capture all requests, and looking into the target URL - if Admin url, add the token to the HTTP_AUTHORIZATION once I fetch the user mentioned in #2 (the user is in every request due to DRF)
EDIT 2
My solution is getting more and more like this stack solution, I would have liked to know if there are any other options, but here is what I did so far:
I made a model that has a 1-1 user field, and a tokens field
As I am fetching/creating the user, I am also saving the tokens on the user's related model from #1 above
I created a middleware that is capturing any request in process_request, and has access to the user. I can see the tokens there as I access the user's related model from #1 above.
I am trying to set the HTTP_AUTHORIZATION header on the request, but cannot do that yet (currently stuck here)
In my backend, I am looking at the incoming request, and trying to fetch the HTTP_AUTHORIZATION - not there yet.
EDIT 3
I ended up just using the Django session as is - once the user authenticates with AWS-Cognito once, it is safe to assume that it is a legitimate User.
Then I just dump the Cognito-JWT, and login() the User.
Note: I am still interested in a solution that would drop the Django session for using the Cognito-JWT, and would love to hear suggestions.
I want to migrate flask_dance with my application to make the user authorize using google and another social networks.
I am getting this error:
Cannot get OAuth token without an associated user
Before i do the connection between the blueprint and sqlalchemy backend, the application worked just fine, if i removed the google_blueprint.backend line the error disappear.
Here is my __init__.py:
import os
from flask import Flask, redirect, url_for, current_app
from flask_login import current_user
from develop.models import (
db,
User,
OAuth
)
from flask_dance.contrib.google import make_google_blueprint
from flask_dance.consumer.backend.sqla import SQLAlchemyBackend
from flask_dance.consumer import oauth_authorized
from sqlalchemy.orm.exc import NoResultFound
def create_app(config_object):
app = Flask(__name__)
app.config.from_object(config_object)
db.init_app(app)
login_manager.init_app(app)
google_blueprint = make_google_blueprint(
client_id=app.config['GOOGLE_CLIENT_ID'],
client_secret=app.config['GOOGLE_CLIENT_SECRET'],
scope=["profile", "email"]
)
app.register_blueprint(google_blueprint, url_prefix='/login')
#oauth_authorized.connect_via(google_blueprint)
def google_logged_in(blueprint, token):
resp = blueprint.session.get("/oauth2/v2/userinfo")
if resp.ok:
account_info_json = resp.json()
email = account_info_json['email']
query = User.query.filter_by(email=email)
try:
user = query.one()
except NoResultFound:
user = User()
user.image = account_info_json['picture']
user.fullname = account_info_json['name']
user.username = account_info_json['given_name']
user.email = account_info_json['email']
db.session.add(user)
db.session.commit()
login_user(get_user, remember=True)
identity_changed.send(
current_app._get_current_object(),
identity=Identity(get_user.id)
)
#login_manager.user_loader
def load_user(userid):
return User.query.get(userid)
google_blueprint.backend = SQLAlchemyBackend(OAuth, db.session, user=current_user)
return app
Here is also my tables how i organized them in models.py:
class User(db.Model, UserMixin):
id = db.Column(db.Integer(), primary_key=True)
image = db.Column(db.String(), nullable=True)
fullname = db.Column(db.String())
username = db.Column(db.String(), unique=True)
password = db.Column(db.String())
email = db.Column(db.String(), unique=True)
class OAuth(OAuthConsumerMixin, db.Model):
user_id = db.Column(db.Integer(), db.ForeignKey(User.id))
user = db.relationship(User)
Please any help would be appreciated :)
TL;DR: You can disable this exception by setting user_required=False on the SQLAlchemyStorage object. However, the exception is being raised for a reason, and if you simply disable it like this, your database may get into an unexpected state where some OAuth tokens are not linked to users. There's a better way to solve this problem. Read on for details.
I am the author of Flask-Dance. This Cannot get OAuth token without an associated user exception is only present in version 0.13.0 and above of Flask-Dance. (CHANGELOG is here.) The pull request introducing this change has some more context for why the change was made.
There are several different ways to use OAuth. Here are some example use cases, all of which Flask-Dance supports:
I want to build a bot that can connect to one specific service, such as a Twitter bot that tweets to a specific account, or a Slack bot that connects to a specific Slack team. I want this bot to respond to HTTP requests, so it has to run as a website, even though I don't expect people to actually use this website directly.
I want to build a website where users can log in. Users need to create an account on my website using a username and password. After they have created an account, users may decide to link their account to other OAuth providers, like Google or Facebook, to unlock additional functionality.
I want to build a website where users can log in. Users should be able to create their account simply by logging in with GitHub (or any other OAuth provider). Users should not need to create a new password for my website.
Use case 1 is the simplest: do not pass a user or user_id argument to your SQLAlchemyStorage, and it will assume that your application does not use multiple user accounts. This means that your website can only link to one particular account on the remote service: only one Twitter account, only one Slack team, etc.
Use case 2 is also pretty simple: pass a user or user_id argument to your SQLAlchemyStorage. Flask-Dance will save the OAuth token into your database automatically, and link it to the user that is currently logged in.
Use case 3 is more complex, since it involves automatically creating both the OAuth token and the local user account at the same time. Different applications have different requirements for creating user accounts, and there's no way for Flask-Dance to know what those requirements are. As a result, Flask-Dance cannot handle this use case automatically. You must hook into the oauth_authorized signal, create the user account and associate it with the OAuth token manually, and return False to tell Flask-Dance to not attempt to handle the OAuth token automatically.
Before version 0.13.0, it was possible to accidentally create OAuth tokens in the database that were not linked with any users at all. In use case 3, the OAuth token is created before a local user account exists for that user, so Flask-Dance would save the OAuth token to the database without any linked local user account. You could use the oauth_authorized handler to associate the OAuth token with a local user account afterwards, but if your code is buggy and raises an exception, then the OAuth token could remain in your database, forever unlinked to any users.
Starting in version 0.13.0, Flask-Dance detects this problem and raises an exception, instead of saving an OAuth token to your database without an associated local user account. There are two ways to resolve this problem:
Rewrite your code to manually create the user account and associate it with the OAuth token. The documentation contains some example code you can use for this.
Disable this check, and tell Flask-Dance that it's OK to create OAuth tokens without associated users. You can disable this check by setting user_required=False on the SQLAlchemyStorage object.
I believe that option 1 is the better solution by far, but it requires more understanding of what Flask-Dance is actually doing behind the scenes. I've written some documentation that describes how to handle multi-user setups, which discusses this problem as well.
I'm currently writing a mobile application in Xamarin and Django with Python Social Auth, my application requires personalisation so I decided to use Facebook authentication instead of allowing users to store a username/password combination due to the security implications.
This works fine when using a normal web browser due to it remembering states, but the tricky part comes when trying to use Xamarin. I tried using Xamarin.Auth, but this caused an error due to the fact that it expected an access_token in the response [1], which I'm unable to get from the Python Social Auth library as it expects a hard-coded redirect after completion [2], which in turn causes a conflict between both libraries.
I tried looking at the following example code, but this expects the mobile app to ALREADY have an access token (taken from a similar question here):
from django.contrib.auth import login
from social.apps.django_app.utils import psa
# Define an URL entry to point to this view, call it passing the
# access_token parameter like ?access_token=<token>. The URL entry must
# contain the backend, like this:
#
# url(r'^register-by-token/(?P<backend>[^/]+)/$',
# 'register_by_access_token')
#psa('social:complete')
def register_by_access_token(request, backend):
# This view expects an access_token GET parameter, if it's needed,
# request.backend and request.strategy will be loaded with the current
# backend and strategy.
token = request.GET.get('access_token')
user = request.backend.do_auth(request.GET.get('access_token'))
if user:
login(request, user)
return 'OK'
else:
return 'ERROR'
If anyone has any experience with using Xamarin and Python Social Auth, it would be really appreciated if you could point me into the right direction with this.
I succesfully managed to login and logout instagram users in my application, but i want to create a new django user on first login, like "Register with Instagram", storing user tokens etc. to use their photos and data in my app
I'm totally new to python-social-auth, any tips on how to achieve this?
If you did everything(migrations) correctly, all those things are stored for you.
Here is how you can access access_token:
social_auth = UserSocialAuth.objects.get(user_id=id, provider='instagram')
access_token = social_auth.access_token
Then, you can use this access_token to access their photo and etc like this:
instagram_api = InstagramAPI(
access_token=access_token,
client_secret=INSTAGRAM_CLIENT_SECRET)
instagram_posts, next_ = instagram_api.user_recent_media(
user_id=id, count=33)
Hope it helps