Permalink
Browse files

Initial commit of refactored simpleopenid module.

  • Loading branch information...
0 parents commit 0d55b99e84d5ebaad4a46cf5f899e603be37cb8a @mitsuhiko committed May 6, 2010
Showing with 4,625 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +130 −0 docs/Makefile
  3. BIN docs/_build/doctrees/environment.pickle
  4. BIN docs/_build/doctrees/index.doctree
  5. +4 −0 docs/_build/html/.buildinfo
  6. +229 −0 docs/_build/html/_sources/index.txt
  7. +455 −0 docs/_build/html/_static/basic.css
  8. +247 −0 docs/_build/html/_static/doctools.js
  9. BIN docs/_build/html/_static/file.png
  10. +279 −0 docs/_build/html/_static/flasky.css
  11. +151 −0 docs/_build/html/_static/jquery.js
  12. BIN docs/_build/html/_static/minus.png
  13. BIN docs/_build/html/_static/plus.png
  14. +69 −0 docs/_build/html/_static/pygments.css
  15. +501 −0 docs/_build/html/_static/searchtools.js
  16. +16 −0 docs/_build/html/_static/underscore.js
  17. +221 −0 docs/_build/html/genindex.html
  18. +492 −0 docs/_build/html/index.html
  19. BIN docs/_build/html/objects.inv
  20. +74 −0 docs/_build/html/py-modindex.html
  21. +74 −0 docs/_build/html/search.html
  22. +1 −0 docs/_build/html/searchindex.js
  23. +12 −0 docs/_themes/flasky/layout.html
  24. +279 −0 docs/_themes/flasky/static/flasky.css_t
  25. +4 −0 docs/_themes/flasky/theme.conf
  26. +216 −0 docs/conf.py
  27. +86 −0 docs/flaskstyle.py
  28. +229 −0 docs/index.rst
  29. +155 −0 docs/make.bat
  30. +166 −0 example/example.py
  31. 0 example/setup.py
  32. BIN example/static/openid.png
  33. +45 −0 example/static/style.css
  34. +22 −0 example/templates/create_profile.html
  35. +16 −0 example/templates/edit_profile.html
  36. +10 −0 example/templates/index.html
  37. +18 −0 example/templates/layout.html
  38. +13 −0 example/templates/login.html
  39. +1 −0 flaskext/__init__.py
  40. +391 −0 flaskext/openid.py
  41. +13 −0 tests.py
@@ -0,0 +1,6 @@
+.DS_Store
+*.pyc
+*.pyo
+env
+dist
+*.egg-info
@@ -0,0 +1,130 @@
+# Makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS =
+SPHINXBUILD = sphinx-build
+PAPER =
+BUILDDIR = _build
+
+# Internal variables.
+PAPEROPT_a4 = -D latex_paper_size=a4
+PAPEROPT_letter = -D latex_paper_size=letter
+ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
+
+.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
+
+help:
+ @echo "Please use \`make <target>' where <target> is one of"
+ @echo " html to make standalone HTML files"
+ @echo " dirhtml to make HTML files named index.html in directories"
+ @echo " singlehtml to make a single large HTML file"
+ @echo " pickle to make pickle files"
+ @echo " json to make JSON files"
+ @echo " htmlhelp to make HTML files and a HTML help project"
+ @echo " qthelp to make HTML files and a qthelp project"
+ @echo " devhelp to make HTML files and a Devhelp project"
+ @echo " epub to make an epub"
+ @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
+ @echo " latexpdf to make LaTeX files and run them through pdflatex"
+ @echo " text to make text files"
+ @echo " man to make manual pages"
+ @echo " changes to make an overview of all changed/added/deprecated items"
+ @echo " linkcheck to check all external links for integrity"
+ @echo " doctest to run all doctests embedded in the documentation (if enabled)"
+
+clean:
+ -rm -rf $(BUILDDIR)/*
+
+html:
+ $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
+
+dirhtml:
+ $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
+ @echo
+ @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
+
+singlehtml:
+ $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
+ @echo
+ @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
+
+pickle:
+ $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
+ @echo
+ @echo "Build finished; now you can process the pickle files."
+
+json:
+ $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
+ @echo
+ @echo "Build finished; now you can process the JSON files."
+
+htmlhelp:
+ $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
+ @echo
+ @echo "Build finished; now you can run HTML Help Workshop with the" \
+ ".hhp project file in $(BUILDDIR)/htmlhelp."
+
+qthelp:
+ $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
+ @echo
+ @echo "Build finished; now you can run "qcollectiongenerator" with the" \
+ ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
+ @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Classy.qhcp"
+ @echo "To view the help file:"
+ @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Classy.qhc"
+
+devhelp:
+ $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
+ @echo
+ @echo "Build finished."
+ @echo "To view the help file:"
+ @echo "# mkdir -p $$HOME/.local/share/devhelp/Classy"
+ @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Classy"
+ @echo "# devhelp"
+
+epub:
+ $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
+ @echo
+ @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
+
+latex:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo
+ @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
+ @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
+ "run these through (pdf)latex."
+
+latexpdf: latex
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
+ @echo "Running LaTeX files through pdflatex..."
+ make -C $(BUILDDIR)/latex all-pdf
+ @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
+
+text:
+ $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
+ @echo
+ @echo "Build finished. The text files are in $(BUILDDIR)/text."
+
+man:
+ $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
+ @echo
+ @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
+
+changes:
+ $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
+ @echo
+ @echo "The overview file is in $(BUILDDIR)/changes."
+
+linkcheck:
+ $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
+ @echo
+ @echo "Link check complete; look for any errors in the above output " \
+ "or in $(BUILDDIR)/linkcheck/output.txt."
+
+doctest:
+ $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
+ @echo "Testing of doctests in the sources finished, look at the " \
+ "results in $(BUILDDIR)/doctest/output.txt."
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,4 @@
+# Sphinx build info version 1
+# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
+config: 5d0688a917367d40a9469a88cb6b1bb2
+tags: fbb0d17656682115ca4d033fb2f83ba1
@@ -0,0 +1,229 @@
+Flask-OpenID
+============
+
+.. module:: flaskext.openid
+
+Flask-OpenID is an extension to `Flask`_ that allows you to add `OpenID`_
+based authentication to your website in a matter of minutes. It depends
+on Flask and `python-openid`_ 2.x. You can install the requirements from
+PyPI with `easy_install` or `pip` or download them by hand.
+
+Features
+--------
+
+- support for OpenID 2.x
+- friendly API
+- perfect integration into Flask
+- basic support for AX and SReg extensions to OpenID that make it possible
+ to fetch basic profile information from a user's OpenID provider.
+
+Installation
+------------
+
+Install the extension with one of the following commands::
+
+ $ easy_install Flask-OpenID
+
+or alternatively if you have `pip` installed::
+
+ $ pip install Flask-OpenID
+
+How to Use
+----------
+
+To integrate Flask-OpenID into your application you need to create an
+instance of the :class:`OpenID` object first::
+
+ from flaskext.openid import OpenID
+ oid = OpenID('/path/to/store')
+
+By default it will use the filesystem as store for information needed by
+OpenID for the authentication process. You can alternatively implement
+your own store that uses the database or a no-sql server. For more
+information about that, consult the python-openid documentation.
+
+The current logged in user has to memorized somewhere, we will use the
+``'openid'`` key in the `session`. This can be implemented in a
+`before_request` function::
+
+ from flask import g, session
+
+ @app.before_request
+ def lookup_current_user():
+ g.user = None
+ if 'openid' in session:
+ g.user = User.query.filter_by(openid=openid).first()
+
+This assumes the openid used for a user is stored in the user table
+itself. As you can see from the example above, we're using SQLAlchemy
+here, but feel free to use a different storage backend. It's just
+important that you can somehow map from openid URL to user.
+
+Next you need to define a login handling function. This function is a
+standard view function that is additionally decorated as
+:meth:`~OpenID.loginhandler`::
+
+ @app.route('/login', methods=['GET', 'POST'])
+ @oid.loginhandler
+ def login():
+ if g.user is not None:
+ return redirect(oid.get_next_url())
+ if request.method == 'POST':
+ openid = request.form.get('openid')
+ if openid:
+ return oid.try_login(openid, ask_for=['email', 'fullname',
+ 'nickname'])
+ return render_template('login.html', next=oid.get_next_url(),
+ error=oid.fetch_error())
+
+What's happening inside the login handler is that first we try to figure
+out if the user is already logged in. In that case we return to where we
+just came from (:meth:`~OpenID.get_next_url` can do that for us). When
+the data is submitted we get the openid the user entered and try to login
+with that information. Additionally we ask the openid provider for email,
+nickname and the user's full name. If that information is available, we
+can use it to simplify the account creation process in our application.
+
+The template also needs the URL we want to return to, because it has to
+forward that information in the form. If an error happened,
+:meth:`~OpenID.fetch_error` will return that error message for us.
+
+This is what a login template typically looks like:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block title %}Sign in{% endblock %}
+ {% block body %}
+ <h2>Sign in</h2>
+ <form action="" method=post>
+ {% if error %}<p class=error><strong>Error:</strong> {{ error }}</p>{% endif %}
+ <p>
+ OpenID:
+ <input type=text name=openid size=30>
+ <input type=submit value="Sign in">
+ <input type=hidden name=next value="{{ next }}">
+ </form>
+ {% endblock %}
+
+See how `error` and `next` are used. The name of the form field `next` is
+required, so don't change it.
+
+Responding to Successful Logins
+-------------------------------
+
+Next we have to define a function that is called after the login was
+successful. The resonsibility of that function is to remember the user
+that just logged in and to figure out if it's a new user to the system or
+one with an existing profile (if you want to use profiles).
+
+Such a function is decorated with :meth:`~OpenID.after_login` and must
+remember the user in the session and redirect to the proper page::
+
+ from flask import flash
+
+ @oid.after_login
+ def create_or_login(resp):
+ session['openid'] = resp.identity_url
+ user = User.query.filter_by(openid=resp.identity_url).first()
+ if user is not None:
+ flash(u'Successfully signed in')
+ g.user = user
+ return redirect(oid.get_next_url())
+ return redirect(url_for('create_profile', next=oid.get_next_url(),
+ name=resp.fullname or resp.nickname,
+ email=resp.email))
+
+The `resp` object passed is a :class:`OpenIDResponse` object with all the
+information you might desire. As you can see, we memorize the user's
+openid and try to get the user with that OpenID from the database. If
+that fails we redirect the user to a page to create a new profile and also
+forward the name (or nickname if no name is provided) and the email
+address. Please keep in mind that an openid provider does not have to
+support these profile information and not every value you ask for will be
+there. If it's missing it will be `None`. Again make sure to not lose
+the information about the next URL.
+
+Creating a Profile
+------------------
+
+A typical page to create such a profile might look like this::
+
+ @app.route('/create-profile', methods=['GET', 'POST'])
+ def create_profile():
+ if g.user is not None or 'openid' not in session:
+ return redirect(url_for('index'))
+ if request.method == 'POST':
+ name = request.form['name']
+ email = request.form['email']
+ if not name:
+ flash(u'Error: you have to provide a name')
+ elif '@' not in email:
+ flash(u'Error: you have to enter a valid email address')
+ else:
+ flash(u'Profile successfully created')
+ db_session.add(User(name, email, session['openid']))
+ db_session.commit()
+ return redirect(oid.get_next_url())
+ return render_template('create_profile.html', next_url=oid.get_next_url())
+
+If you're using the same names for the URL parameters in the step before
+and in this form, you have nice looking and simple templates:
+
+.. sourcecode:: html+jinja
+
+ {% extends "layout.html" %}
+ {% block title %}Create Profile{% endblock %}
+ {% block body %}
+ <h2>Create Profile</h2>
+ <p>
+ Hey! This is the first time you signed in on this website. In
+ order to proceed we need a couple of more information from you:
+ <form action="" method=post>
+ <dl>
+ <dt>Name:
+ <dd><input type=text name=name size=30 value="{{ request.values.name }}">
+ <dt>E-Mail:
+ <dd><input type=text name=email size=30 value="{{ request.values.email }}">
+ </dl>
+ <p>
+ <input type=submit value="Create profile">
+ <input type=hidden name=next value="{{ next }}">
+ </form>
+ <p>
+ If you don't want to proceed, you can <a href="{{ url_for('logout')
+ }}">sign out</a> again.
+ {% endblock %}
+
+Logging Out
+-----------
+
+The logout function is very simple, it just has to unset the openid from
+the session and redirect back to where the user was before::
+
+ @app.route('/logout')
+ def logout():
+ session.pop('openid', None)
+ flash(u'You were signed out')
+ return redirect(oid.get_next_url())
+
+Full Example
+------------
+
+To see the full code of that example, you can download the code `from
+github <http://github.com/mitsuhiko/flask-openid>`_.
+
+API References
+--------------
+
+The full API reference:
+
+.. autoclass:: OpenID
+ :members:
+
+.. autoclass:: OpenIDResponse
+ :members:
+
+.. _Flask: http://flask.pocoo.org/
+.. _OpenID: http://openid.net/
+.. _python-openid: http://openidenabled.com/python-openid/
Oops, something went wrong.

0 comments on commit 0d55b99

Please sign in to comment.