RPXnow to serwis świadczący usługę agregacji źródeł autoryzacji i autentykacji takich jak OpenID, Facebook Connect, Twitter, Google Account, Blogger, Myspace, czy Windows Live ID. Strona korzystająca z RPXnow w bardzo prosty sposób może umożliwić rejestrację i logowanie użytkowników posiadających wspomniane konta (a w erze serwisów społecznościowych może to być ważnym źródłem nowych użytkowników). Za darmo dostaniemy podstawową funkcjonalność usługi i do 6 źródeł autoryzacji. Konta płatne mogą wybrać do 12 takich źródeł, oraz mają szerszy zakres możliwości.
Żeby zacząć używać RPX należy zarejestrować się na stronie i założyć "aplikację" podając jej nazwę, oraz domeny, na których usługa będzie działać. Gdy mamy konto możemy wybrać dostępców i ich kolejność:
Niektórzy dostawcy wymagają dodatkowej konfiguracji - np. Facebook wymaga założenia aplikacji Facebook Connect. Po zapisaniu zmian można wykorzystać RPX na własnej stronie.
Po założeniu aplikacji dostanie ona swój klucz API - będzie on potrzebny do żądań wysyłanych do API rpxnow.
- Na własnej stronie umieszczamy JavaScriptowy widżet lub ramkę zawierającą listę dostawców.
- Użytkownik wybiera dostawcę i podaje swój login/identyfikator. RPX zajmuje się obsługą logowania do danego dostawcy.
- Po potwierdzeniu zalogowania przed serwis-dostawcę (np. Facebooka) RPX przekierowuje na naszą stronę z tokenem pozwalającym pobrać dane o użytkowniku.
- Dzięki tokenowi pobieramy dane użytkownika, w tym unikalny identyfikator, po którym możemy zalogować/zarejestrować we własnym serwisie użytkownika.
- Możesz użyć widżet popupa, lub wyświetlić gotową ramkę - prezentujące listę dostawców. Kody dostępne są w zakładce Quick Start twojego konta
- Jeżeli chcesz popupa to na końcu strony wklej:
<script src="https://rpxnow.com/openid/v2/widget"
type="text/javascript"></script>
<script type="text/javascript">
RPXNOW.overlay = true;
RPXNOW.language_preference = 'pl';
</script>
Oraz w wybranym miejscu strony link wywołujący go:
<a class="rpxnow" onclick="return false;"
href="https://NAZWA_APLIKACJI.rpxnow.com/openid/v2/signin?token_url=TWÓJ_URL">
Zaloguj się
</a>
Gdzie TWÓJ_URL to pełen adres URL do strony w twoim serwisie odbierającej token z RPX (o czym za chwilę), a NAZWA_APLIKACJI to nazwa twojej aplikacji na RPX (zobacz wklejki na ich stronie pod "Quick Start").
- Dla opcji z ramką wystarczy wkleić w wybranym miejscu:
<iframe src="https://NAZWA_APLIKACJI.rpxnow.com/openid/embed?token_url=TWÓJ_URL"
scrolling="no" frameBorder="no" style="width:400px;height:240px;">
</iframe>
- Strona podana pod TWÓJ_URL to strona, na którą zostanie przekierowany użytkownik po zalogowaniu się na stronie dostawcy. Wraz z przekierowaniem przesłany zostanie token (POST, "token"), za pomocą którego będziemy mogli pobrać dane o tym użytkowniku (jeżeli wszystko przebiegło pomyślnie i użytkownik zalogował się u swojego dostawcy), oto fragment widoku Django obsługującego RPX:
def handle_rpx_post(request):
"""
RPX posting token after logging
"""
if 'token' not in request.POST:
return HttpResponseRedirect('/')
# token z przekierowania
token = str(request.POST['token'])
# klucz API aplikacji z rpxnow
apiKey = settings.RPXNOW
# adres URL API rpxnow
url = 'https://rpxnow.com/api/v2/auth_info'
# żądanie danych użytkownika
# przesyłamy token i klucz API
req = urllib2.Request(url=url, data='token=%s&apiKey=%s' % (token, apiKey))
f = urllib2.urlopen(req)
response = f.read()
try:
jsn = json.loads(response)
except:
pass
else:
# stat == ok to poprawna autoryzacja u dostawcy
if jsn['stat'] == 'ok':
profile = jsn['profile']
username = profile['preferredUsername']
# identyfikator, openID, czy np. adres profilu (np. Facebook)
identifier = profile['identifier']
# nie wszyscy dostawcy udostępniają adres email, to pole jest opcjonalne
if profile.has_key('email') and len(profile['email']) > 1:
email = profile['email']
else:
email = False
- Mając identyfikator użytkownika (identifier) możemy sprawdzić czy dla niego istnieje już konto, czy też nie. Dobrym rozwiązaniem jest w takim przypadku automatyczna rejestracja i zalogowanie użytkownika, oraz zapamiętanie powiązania identyfikatora z danym kontem użytkownika Django.
Potrzebujemy obsługę logowania po RPX w AUTHENTICATION_BACKENDS, oraz model powiązań. Dodatkowo warto dać opcje takie jak przypisanie identyfikatora do starego konta. Poniżej przedstawiłem kluczowe elementy mojej implementacji obsługi RPXnow w Bibliotekach. Dostępna jest też aplikacja Django - django-rpx, lecz jej akurat nie testowałem.
- Backend autoryzacji wygląda tak:
from django.contrib.auth.models import User
from django.conf import settings
from diamandas.userpanel.models import *
class RPXBackend:
"""
Authenticate a user that has associated RPX to his account
"""
def authenticate(self, user_id=False, rpx=False):
if user_id and rpx:
try:
user = User.objects.get(id=user_id)
except:
return None
if rpx.user == user:
return user
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
- Model powiązań może wyglądać tak:
class RPXAssociation(models.Model):
"""
Assoction of user accounts and RPX
"""
user = models.ForeignKey(User, verbose_name=_('User'), limit_choices_to={'is_staff': False})
identifier = models.CharField(max_length=255, verbose_name=_('Identifier'))
ask_for_mail = models.BooleanField(blank=True, default=False, verbose_name=_('No email specified'))
is_new = models.BooleanField(blank=True, default=True, verbose_name=_('Account made by social source'))
def __str__(self):
return self.identifier
def __unicode__(self):
return self.identifier
class Meta:
verbose_name = _('RPX Association')
verbose_name_plural = _('RPX Associations')
- Kompletny widok obsługujący przekierowanie z RPX przedstawiony został poniżej. Zajmuje się on także zalogowaniem/rejestracją użytkownika:
from random import choice
import urllib2
import json
from django.contrib.auth import authenticate, login
#....
def handle_rpx_post(request):
"""
RPX posting token after logging
"""
if 'token' not in request.POST:
return HttpResponseRedirect('/')
token = str(request.POST['token'])
apiKey = settings.RPXNOW
url = 'https://rpxnow.com/api/v2/auth_info'
req = urllib2.Request(url=url, data='token=%s&apiKey=%s' % (token, apiKey))
f = urllib2.urlopen(req)
response = f.read()
try:
jsn = json.loads(response)
except:
pass
else:
if jsn['stat'] == 'ok':
profile = jsn['profile']
username = profile['preferredUsername']
username = normalise_string(username)
identifier = profile['identifier']
if profile.has_key('email') and len(profile['email']) > 1:
email = profile['email']
else:
email = False
try:
rpx = RPXAssociation.objects.get(identifier=identifier)
except:
# no account
randompass = ''.join([choice('1234567890qwertyuiopasdfghjklzxcvbnm') for i in range(7)])
bla = ''.join([choice('1234567890qwertyuiopasdfghjklzxcvbnm') for i in range(3)])
if email:
try:
user = User.objects.create_user(username, email, randompass)
except:
username = '%s_%s' % (username, bla)
user = User.objects.create_user(username, email, randompass)
r = RPXAssociation(identifier=identifier, user=user)
else:
try:
user = User.objects.create_user(username, randompass, randompass)
except:
username = '%s_%s' % (username, bla)
user = User.objects.create_user(username, randompass, randompass)
r = RPXAssociation(identifier=identifier, user=user, ask_for_mail=True)
r.save()
user = authenticate(user_id = str(user.id), rpx=r)
if user is not None:
login(request, user)
if email:
request.user.message_set.create(message=_('You have been logged-in successfully :)'))
else:
request.user.message_set.create(message=_('You have been logged-in successfully. You can set your email address in account preferences :) '))
else:
# we have a user for that identifier
user = authenticate(user_id = str(rpx.user.id), rpx=rpx)
if user is not None:
login(request, user)
request.user.message_set.create(message=_('You have been logged-in successfully :)'))
return HttpResponseRedirect('/')
# this is error
return HttpResponseRedirect('/user/rpx/error/')
- Dodałem także możliwość przypisania identyfikatora do istniejącego konta - użytkownik podaje login i hasło do niego i jeżeli jest poprawne to konto stworzone automatycznie jest kasowane, a przypisanie RPX przestawiane na stare konto. Oto kod widoku:
class AssignRPXForm(forms.Form):
login = forms.CharField(label=_("Username"), max_length=30, widget=forms.TextInput())
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput())
@login_required
def assign_rpx(request):
#only for autocreated accounts from RPX
r = RPXAssociation.objects.get(user=request.user)
if not r.is_new:
return HttpResponseRedirect('/')
form = AssignRPXForm()
if request.POST:
form = AssignRPXForm(request.POST)
if form.is_valid():
data = form.cleaned_data
# try to authenticate the user
user = authenticate(username=data['login'], password=data['password'])
if user is not None:
try:
request.user.get_profile().delete()
except:
pass
# delete the new user account
request.user.delete()
# login on the old account
login(request, user)
# update the rpx accociation
r.user = user
r.is_new = False
r.save()
request.user.message_set.create(message=_('You have been connected to the existing account :)'))
return HttpResponseRedirect('/')
return render_to_response(
'userpanel/assign_rpx.html',
{'form': form, 'rpx': r},
context_instance=RequestContext(request))
- Dodane: 09.09.2009 przez riklaunim