Services/Authentication: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
No edit summary
Line 1: Line 1:
{{draft}}
{{draft}}


= Authentication =
= Goals =
 
== Goal ==


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.
Line 11: Line 9:
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.


== Data Model ==
= 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.
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.
Line 17: Line 15:
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 =


Authentication in services apps is loosely based on the workflow of the [http://docs.repoze.org/who/2.0/ repoze.who] framework, and is explicitly divided into four stages: identify, authenticate, challenge and acknowledge.
Authentication in services apps is loosely based on the workflow of the [http://docs.repoze.org/who/2.0/ repoze.who] framework, and is explicitly divided into four stages: identify, authenticate, challenge and acknowledge.
Line 23: Line 21:
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.
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 ===
== 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 [http://www.ietf.org/rfc/rfc2617.txt RFC 2617].
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 [http://www.ietf.org/rfc/rfc2617.txt RFC 2617].
Line 32: Line 30:
* scheme:  a string naming the authentication scheme to use
* scheme:  a string naming the authentication scheme to use


=== Authenticate ===
== 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.
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 ===
== 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.
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 ===
== 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.
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 ==
= Application API =


=== Loading the Framework ===
== 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:
Applications built using the new pyramid-based framework can hook into this system by including the "mozsvc.user" package into their pyramid config:
Line 61: Line 59:
Applications using the old server-core framework will have authentication loaded automatically.
Applications using the old server-core framework will have authentication loaded automatically.


=== Getting the User ===
== 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.
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.
Line 70: Line 68:
         return Response("Hello %s" % (request.user["username"],))
         return Response("Hello %s" % (request.user["username"],))


===  Demanding Credentials ===
==  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 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.
Line 76: Line 74:
For server-core applications, you must configure your route match to include the key "auth" with value "True".
For server-core applications, you must configure your route match to include the key "auth" with value "True".


=== User Backends ===
== User Backends ==


User account information is stored in a generic "backend" with the following interface:
User account information is stored in a generic "backend" with the following interface:
Line 92: Line 90:
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:
To use the default authentication setup, you need only configure a user backend.  In your application config file, create an "auth" action like this:
Line 120: Line 118:
     plugins = backend
     plugins = backend


== Authentication Schemes ==
= Authentication Schemes =
 
== Basic ==
 
HTTP Basic Access Auth, aka password auth.  The credentials dict contains "username" and "password".
 
== Digest ==


=== Basic ===
HTTP Digest Access Auth, as described in [http://www.ietf.org/rfc/rfc2617.txt RFC 2617].  The credentials dict contains all of the parameters from the Authorization header, along with "request-method" and "content-md5".  This makes the credentials self-contained so they can be verified by an aribitrary backend that may not have access to the metadata about the request.


=== Digest ===
== SRP-MAC ==


=== SRP-MAC ===
A custom protocol designed to be better than Digest Auth, using the [http://srp.stanford.edu/ Secure Remote Password Protocol].


A combination of this SRP-HMAC proposal from bugzilla: https://bugzilla.mozilla.org/show_bug.cgi?id=356855
A combination of this SRP-HMAC proposal from bugzilla: https://bugzilla.mozilla.org/show_bug.cgi?id=356855
Line 133: Line 137:


=== BrowserID ===
=== BrowserID ===
A custom protocol based on BrowserID/VEP.  The credentials dict contains "username" and "assertion", where "assertion" is a valid browserid assertion.


=== Bearer Token ===
=== Bearer Token ===
Something like signed cookies.  For login sessions etc. 
= Implementation Plan =
== User Backend API ==
Change the user backend API to support generic dicts of credentials, and to allow interrogation of the supported auth schemes.
Bug #TODO
== Add repoze.who to server-core ==
Replace the custom Authenticator class in server-core with one based on repoze.who.  It will load a default configuration by default, and look in the config file for overrides.
Bug #TODO
== Add shortcuts to mozsvc ==
Provide the mozsvc.user package to make things easier for pyramid apps.
Bug #TODO
== Add Auth Schemes to Sync Client ==
Patch the sync client in mozilla-central to support different authentication schemes.  Currently targeting just basic and digest.
Bug #TODO
== Add auth schemes to LDAP backend ===
Make the necessary changes to let the LDAP backend authenticate with different schemes, e.g. digest-auth or browserid.
== Get Rid of LDAP ==
Replace LDAP with something better, what has native support for these various auth schemes.

Revision as of 22:56, 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.

Goals

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

HTTP Basic Access Auth, aka password auth. The credentials dict contains "username" and "password".

Digest

HTTP Digest Access Auth, as described in RFC 2617. The credentials dict contains all of the parameters from the Authorization header, along with "request-method" and "content-md5". This makes the credentials self-contained so they can be verified by an aribitrary backend that may not have access to the metadata about the request.

SRP-MAC

A custom protocol designed to be better than Digest Auth, using the Secure Remote Password Protocol.

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

A custom protocol based on BrowserID/VEP. The credentials dict contains "username" and "assertion", where "assertion" is a valid browserid assertion.

Bearer Token

Something like signed cookies. For login sessions etc.

Implementation Plan

User Backend API

Change the user backend API to support generic dicts of credentials, and to allow interrogation of the supported auth schemes.

Bug #TODO

Add repoze.who to server-core

Replace the custom Authenticator class in server-core with one based on repoze.who. It will load a default configuration by default, and look in the config file for overrides.

Bug #TODO

Add shortcuts to mozsvc

Provide the mozsvc.user package to make things easier for pyramid apps.

Bug #TODO

Add Auth Schemes to Sync Client

Patch the sync client in mozilla-central to support different authentication schemes. Currently targeting just basic and digest.

Bug #TODO

Add auth schemes to LDAP backend =

Make the necessary changes to let the LDAP backend authenticate with different schemes, e.g. digest-auth or browserid.

Get Rid of LDAP

Replace LDAP with something better, what has native support for these various auth schemes.