Dashboard > RIFE > ... > Authentication > Authentication system internals
RIFE Log In | Sign Up   View a printable version of the current page.
Authentication system internals


Added by Steven Grimm, last edited by Geert Bevin on Feb 06, 2007  (view change)
Labels: 
(None)

(Originally a message from Geert Bevin on the RIFE users' mailing list)

Authentication basically is built on RIFE's stateful Aspect oriented technique that I call behavioral inheritance (and more recent threedimensional flow). This concept is sadly under documented and my books will take care of that in a month or two. What happens is that you put one site structure on top of another one, and you declare variables that need to be watched. When RIFE hits the upper layer, it will detect that the request was intended for the lower layer and preserve all its data over any number of subsequent requests. The logic in the upper layer executes until one of the watched variables gets a value. Then the childTriggered method is executed and your code is able to evaluate the value to indicate whether the logic should stay in the top layer, or if it should fall through and restore the original request.

All what RIFE's authentication relies on, is a string authentication ID, since that's what need to work with child triggers. See the SessionManager Javadoc.

Now RIFE also has a collection of authorization features that rely on Credentials and a CredentialsManager. Credentials are actually an empty interface, they can contain anything, as long as the associated CredentialsManager is able to verify if they are correct and return a user ID. RIFE provides one sub-interface of Credentials, RoleUserCredentials and one implementation of this, RoleUser. The associated manager classes implement the CredentialsManager interface, but also the RoleUserManager interface. The latter provides a lot of additional functionality to actually maintain the users in the back-end store. The rest are concrete memory or database implementations of the managers.

The last part is SessionValidator, it is essentially a bridge between a CredentialsManager and a SessionManager. The validity of a session is often dependent on external attributes which define the context for a valid session that goes beyond a valid session ID. This interface allows you to write optimized queries to speed up the validation of an active authentication session (ie. a single database query instead of having to use the separate manager classes to achieve this). You can find the explanation of it in the SessionValidator Javadoc.

Flow of execution

Note: This is a work in progress.

Here's a simple authenticated site declaration.

<element id="AuthElement" extends="rife/authenticated/memory.xml">
    <property name="template_name">auth.login</property>
    <property name="role">admin</property>
    <property name="authvar_type">cookie</property>

    <!-- The fields on the login form need to be in a "credentials" submission,
         since that's what RIFE's built-in authentication elements will read
         to construct the Credentials object for verification. -->
    <submission name="credentials">
        <param name="login"/>
        <param name="password"/>
        <param name="remember"/>
    </submission>

    <!-- If a valid "authid" cookie is present, fall through to the element
         that extends this one (i.e., allow access to the protected page.) -->
    <childtrigger name="authid"/>
    <incookie name="authid"/>
    <outcookie name="authid"/>

    <!-- Allow the authentication elements to set an additional cookie for
         constructing a new login session without prompting for a password. -->
    <incookie name="rememberid"/>
    <outcookie name="rememberid"/>
</element>

<!-- A group of elements that require authentication. -->
<group inherits="AuthElement">
    <element id="MyAccount" implementation="com.foo.MyAccount" url="myaccount"/>
</group>

Now let's trace a few requests through the authentication system to see how RIFE actually does the authentication.

Our first request is an anonymous user, with no rememberid or authid cookies.

  • Since the MyAccount element is in a group or subsite that inherits from another element, the parent element is invoked to handle the request first. AuthElement is implemented by RIFE's PurgingMemoryAuthenticated class, which has a number of superclasses: MemoryAuthenticated, RoleUserAuthenticated, Authenticated, Identified, Element, and finally ElementSupport.
  • There is a <childtrigger> tag in the AuthElement declaration, so RIFE looks for the input named by the tag, in this case a cookie named authid. There isn't one, so AuthElement is processed as usual (its processElement() method is called.)
  • The implementation of processElement(), which is shared by all of RIFE's built-in authentication elements, is in the Authenticated class. It looks for a "remember me" cookie. This cookie's name is set by the remembervar_name property; the default value is rememberid. In this case there is no such cookie.
  • Next it looks for a submission with the name given by the submission_name property; the default is credentials. In this case there is no submission.
  • Finally, it prints the template named by the template_name property, in this case auth.login. That template typically contains a login form.

The user fills in the login form with an incorrect password. The login form submits to the same URL that it originally came from; the same authentication element that displayed the form will handle the submission.

  • Once again, control is first given to the parent element, AuthElement.
  • There is still no authid cookie, so control is passed to Authenticated.processElement().
  • There is still no rememberid cookie.
  • But this time there is a submission called credentials. The contents of the submission are fetched as a submission bean, whose class is determined by the particular subclass of Authenticated that's being used. The class must implement the Credentials interface. In this case, since our authentication element is a subclass of RoleUserAuthenticated, the RoleUserCredentials class is used. TO DO: document AuthenticatedDeployer mechanism and how it determines the credentials class
  • The validate() method of the Credentials object is called to ensure that the user entered syntactically valid information. Validation capability is required for credentials objects; the Credentials interface is a subinterface of the Validated interface used by RIFE's validation system. In this case, the user didn't enter the correct password, but the password isn't invalid input per se, so the validation succeeds.
  • Next, the authentication element's SessionValidator is queried for its CredentialsManager. The session validator in this case is MemorySessionValidator and it returns a MemoryUsers instance. TO DO: document how the session validator and credentials managers are chosen
  • The credentials manager is the class that's actually responsible for verifying a user's password. Its verifyCredentials() method is called. In this case the password is incorrect, so the method returns -1.
  • The login template is displayed again.

Now the user supplies the correct password and leaves the "Remember Me" checkbox unchecked.

  • Once again, control is first given to the parent element, AuthElement.
  • There is still no authid cookie, so control is passed to Authenticated.processElement().
  • There is still no rememberid cookie.
  • There is a credentials submission.
  • The credentials validate successfully.
  • The credentials manager is called to verify the password. In this case the password is correct, so the method returns the user's positive user ID.
  • The credentials object is checked to see if it implements the RememberMe interface. The default credentials class does, so the "Remember Me" setting is fetched. In this case it's set to false.
  • The startNewSession() method is called.
  • The user did not check the "Remember Me" box, so startNewSession() doesn't try to create a new remember ID.
  • The authentication element's SessionValidator is queried for its SessionManager. In this case the session manager is a
    MemorySessions.
  • The session manager's startSession() method is called. It is passed the user ID (from the credentials manager), the client IP address, and a flag indicating whether the session was restored from a "remember me" cookie (false in this case). If the session manager is satisfied with its inputs, it returns an authentication session ID, which is just a unique string identifier. The built-in session managers use the UniqueIdGenerator class to generate their authentication session IDs.
  • Since the authvar_type of this element is set to cookie, startNewSession() sets the cookie (authid, the default name) to the new authentication session ID. It does this by calling the setCookie() method defined by its ElementSupport superclass.
  • The element's setCookie() method delegates to the setCookie() method in the ElementContext instance that the Web engine assigns to each invocation of an element. TO DO: link to documentation on element context. Maybe already discussed in the docs somewhere?
  • The element context's setCookie() does more than just set the cookie: if the current element has a child element, it checks to see if the cookie name matches the child trigger. If so, it calls the element's childTriggered() method. Since the cookie name matches, that happens in this case.
  • TO DO: document childTriggered() flow
  • The childTriggered() method returns true to indicate that the child was successfully fired. Control returns to the element context (which, you will recall, called childTriggered() while setting the authentication session ID cookie). The element context, seeing the true return value, throws a ChildTriggeredException, which aborts the authentication element's processElement() call and ends the request.

Now the user reloads the page.

  • TO DO: document childTriggered() flow of control

The user goes away for a while, long enough that his session expires, then tries to reload the page again.

  • TO DO: document childTriggered() with expired/invalid session

Not wanting to be asked to log in again, the user checks the "Remember Me" checkbox and supplies his username and password.

  • TO DO: document creation of remember-me session, including RememberManager lookup

Now the user goes away again and reloads after his session has expired.

  • TO DO: document reestablishment of session with rememberid

Hooks

The authentication system has hooks that user-written authentication elements can use to intercept the flow of execution. These are all methods defined in the Authenticated class; their default implementations are all empty.

Method When it's called
initializeAuthentication At the start of processElement().
entrance After the template instance has been instantiated.
unvalidatedCredentials On login form submission when the credentials object's validate() method fails.
validatedCredentials On login form submission when the credentials object's validate() method succeeds, but before the credentials manager verifies them.
acceptedCredentials After the credentials manager verifies that the credentials are valid.
refusedCredentials After the credentials manager rejects the supplied credentials.
authenticated After a new authentication session has been successfully created.
sessionCreationError When the session manager couldn't create a new authentication session.
sessionNotValid When a session manager doesn't accept a provided authentication ID that a user provides after having been logged in.

Both refusedCredentials and sessionCreationError have default implementations that set the appropriate validation errors in the template, so if you want to preserve this, you need to call the super method in your own version.



Are you enjoying Confluence? Please consider purchasing it today.
Powered by Atlassian Confluence, the Enterprise Wiki. (Version: 2.2.1a Build:#515 May 19, 2006) - Bug/feature request - Contact Administrators