Services/Authentication: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
(Created page with "{{draft}} = Authentication = == Goal == To allow services apps to authenticate users in a flexible and secure fashion. To ensure apps don't take the "easy way out" and use in...")
 
No edit summary
Line 6: Line 6:


To allow services apps to authenticate users in a flexible and secure fashion.
To allow services apps to authenticate users in a flexible and secure fashion.
To let us swap in different authentication schemes without changing application code.


To ensure apps don't take the "easy way out" and use insecure schemes like HTTP Basic Auth.
To ensure apps don't take the "easy way out" and use insecure schemes like HTTP Basic Auth.
Line 14: Line 16:


In code, the user is represented by a dict of their user data.  It will always include the keys "username" and "userid".  Applications may also arrange for other keys to be loaded into this dict.
In code, the user is represented by a dict of their user data.  It will always include the keys "username" and "userid".  Applications may also arrange for other keys to be loaded into this dict.


== Workflow ==
== Workflow ==
Line 92: Line 92:
If no backend is in use (e.g. because users are being authenticated against a third-party service) then these objects will be None.
If no backend is in use (e.g. because users are being authenticated against a third-party service) then these objects will be None.


== Configuration ==


== Configuration ==
To use the default authentication setup, you need only configure a user backend.  In your application config file, create an "auth" action like this:
 
    [auth]
    backend = services.user.sql.SQLUser
    sqluri = sqlite:////tmp/account.db
 
The default configuration will interrogate the backend to see what auth schemes it supports, and will provide all of them.
 
Finer control over the different stages of authentication can be achieved by configuring individual repoze.who plugins.  TODO link to config description.  For example the following configuration with authenticate against the user backend using *only* digest authentication.
 
    [who.plugin.digest]
    use = repoze.who.plugins.digestauth:make_plugin
    realm = 'Sync'
 
    [who.plugin.backend]
    use = services.whoauth.backendauth:make_plugin
 
    [who.identifiers]
    plugins = digest
 
    [who.challengers]
    plugins = digest
 
    [who.authenticators]
    plugins = backend





Revision as of 22:39, 28 November 2011

Draft-template-image.png THIS PAGE IS A WORKING DRAFT Pencil-emoji U270F-gray.png
The page may be difficult to navigate, and some information on its subject might be incomplete and/or evolving rapidly.
If you have any questions or ideas, please add them as a new topic on the discussion page.

Authentication

Goal

To allow services apps to authenticate users in a flexible and secure fashion.

To let us swap in different authentication schemes without changing application code.

To ensure apps don't take the "easy way out" and use insecure schemes like HTTP Basic Auth.

Data Model

Every user of a services app has two unique identifiers: a username by which they identify themselves, and a userid by which they are identified in persistent storage. The username may be mutable and is selected by the user; the userid is immutable and is generated automatically.

In code, the user is represented by a dict of their user data. It will always include the keys "username" and "userid". Applications may also arrange for other keys to be loaded into this dict.

Workflow

Authentication in services apps is loosely based on the workflow of the repoze.who framework, and is explicitly divided into four stages: identify, authenticate, challenge and acknowledge.

The details of each stage in this workflow are hidden from the application code, but they exist as distinct extension and configuration points as described below.

Identify

The first step is to extract credentials from an incoming request. For an interactive application this might mean gathering the information that was submitted from a login form. For automation APIs this will most likely mean parsing credentials from the HTTP "Authorization" header in conformance with RFC 2617.

The outcome of this stage is a dict of authentication credentials. The precise contents of the dict will depend on the authentication scheme being used, but it must contain at least the following:

  • username: the string username by which the user identifies themselves
  • scheme: a string naming the authentication scheme to use

Authenticate

The extracted credentials dict is then checked for authenticity according to the particular authentication scheme in use. If successfully authenticated, this produces the dict of user information for use by the application.

Challenge

If the client provides invalid credentials (or no credentials) then the application will respond with one or more authentication challenges. For interactive applications a challenge would normally be a HTML form for them to submit their credentials. For automation APIs the challenge would be a "401 Authorization Required" response.

Acknowledge

If the client provides valid credentials, the server may include headers in its response to acknowledge the successful authentication. For example, it may set a session cookie.

Application API

Loading the Framework

Applications built using the new pyramid-based framework can hook into this system by including the "mozsvc.user" package into their pyramid config:

   def main(global_config, **settings):
       config = get_configurator(global_config, **settings)
       # adds Mozilla default views
       config.include("mozsvc")
       # adds the Mozilla auth framework
       config.include("mozsvc.user")
       ...etc...
       return config.make_wsgi_app()

Applications using the old server-core framework will have authentication loaded automatically.

Getting the User

Each request object will have a "user" attribute that provides the identity dict for a successfully authenticated user. If the user is not authenticated then request.user will be false.

   def some_view(request):
       if not request.user:
           return Response("You're not logged in")
       return Response("Hello %s" % (request.user["username"],))

Demanding Credentials

For pyramid applications, you can force the user to provide valid credentials using the standard permission system, either by assigning permissions to your views or by explicitly raising Forbidden when the user is not authenticated.

For server-core applications, you must configure your route match to include the key "auth" with value "True".

User Backends

User account information is stored in a generic "backend" with the following interface:

XXX TODO describe the api

For pyramid applications, the active user backend can be accessed through the pyramid registry:

   request.registry["auth"].create_user("testuser", "testpass", "test@example.com")

For server-core applications, the active user backend is available as an attribute of the controller:

   self.auth.create_user("testuser", "testpass", "test@example.com")

If no backend is in use (e.g. because users are being authenticated against a third-party service) then these objects will be None.

Configuration

To use the default authentication setup, you need only configure a user backend. In your application config file, create an "auth" action like this:

   [auth]
   backend = services.user.sql.SQLUser
   sqluri = sqlite:////tmp/account.db

The default configuration will interrogate the backend to see what auth schemes it supports, and will provide all of them.

Finer control over the different stages of authentication can be achieved by configuring individual repoze.who plugins. TODO link to config description. For example the following configuration with authenticate against the user backend using *only* digest authentication.

   [who.plugin.digest]
   use = repoze.who.plugins.digestauth:make_plugin
   realm = 'Sync'
   [who.plugin.backend]
   use = services.whoauth.backendauth:make_plugin
   [who.identifiers]
   plugins = digest
   [who.challengers]
   plugins = digest
   [who.authenticators]
   plugins = backend


Authentication Schemes

Basic

Digest

SRP-MAC

A combination of this SRP-HMAC proposal from bugzilla: https://bugzilla.mozilla.org/show_bug.cgi?id=356855 And the MAC access protocol for OAuth2: http://tools.ietf.org/html/draft-hammer-oauth-v2-mac-token

BrowserID

Bearer Token