Authentication module

Introduction

The µC/HTTP-server authentication system is built on top of the Control Layer. Without the control layer it wont work. It also requires the Authentication module of the µC/Common libraries that defines the User Management.

There is two subsystems for this module: The Control Layer Authentication Layer and the Control Layer Application Layer.

Authentication Layer : Cookie Checker

The cookie checker is basically a Control Layer instance bound to the Authentication layer. When a request is made without a valid cookie, it refuses to let it go and will take on the request. When a cookie is present and valid, the request is then checked to know which rights are required to continue. If the user bound to the cookie provided has the rights to continue, the request is marked as accepted.

Application Layer

Unprotected Requests: Cookie Creator

The cookie creator work after the cookie checker has done it's work and could not find a valid cookie. In that case, if the request is a POST with the "logme form", the request is "catched" and the parsed body is inspected to find a username and a password.This happens at the application phase of the Control Layer. If a username and a password is found within the request, it is then validated with known credentials from the user management layer. If a valid match is found, a session is created, the user is associated with it, and the cookie identifier is sent to the HTTP client.

Protected Requests

Once the session was created, requests related to log in should not occurred, but for safety the Authentication module will still "catched" them. A request to the login page will be redirect to the default page. A POST request with the "logme form" will be parsed even though a session is already existing and if the credentials are incorrect the session will be terminated.

Session Timeout

The Authentication module also configures a timer to periodically check if any created sessions have timed out. After a session timed out, a cookie received with the session ID number will be considered invalid and any not authorized requests will be redirect to the configured page for unauthorized requests.

How to Configure

To configure the µC/HTTP-server authentication, here are the steps to follow:

  1. Create users with their appropriate credentials and rights.
  2. Define an hook function which will return the right necessary to process a given request.
  3. Define an hook function which will check the requests received for a log in request but also set the pages for redirection for all of those requests.
  4. Define an hook function which will check the requests received for a log out request.
  5. Create the appropriate configurations and bind those three hook functions.
  6. Bind those configurations to their respective Control Layer Instance configuration.
  7. Use the three instances created in the Control Layer configuration.

Hook Functions

Get Required Rights Hook

This hook will be called by the Authentication Layer of the Control Layer when a request is received to check with the upper application what rights are associated with this request.


          AUTH_RIGHT  HTTPsAuth_GetRequiredRightsHook (const  HTTPs_INSTANCE  *p_instance,
                                                       const  HTTPs_CONN      *p_conn);

Login Hook

This hook will be called by the Application Layer of the Control Layer to found requests related to the log in process, e.g. the GET requests for the resources of the log in page (html, css, images) and the POST request with the form log in. This hook functions will be called at two occasions : when the URL and headers of the request were received and parse by the server (state HTTPs_AUTH_STATE_REQ_URL), and also when the request body was received and parse (state HTTPs_AUTH_STATE_REQ_COMPLETE).


          CPU_BOOLEAN  AppGlobal_Auth_ParseLoginHook (const  HTTPs_INSTANCE     *p_instance,
                                                      const  HTTPs_CONN         *p_conn,
                                                             HTTPs_AUTH_STATE    state,
                                                             HTTPs_AUTH_RESULT  *p_result);

Logout Hook

This hook will be called by the Application Layer of the Control Layer


          CPU_BOOLEAN  AppGlobal_Auth_ParseLogoutHook (const  HTTPs_INSTANCE     *p_instance,
                                                       const  HTTPs_CONN         *p_conn,
                                                              HTTPs_AUTH_STATE    state);

Example

The output of the "How to" steps should look like the example provided:

Authentication Functions Examples
/*
*********************************************************************************************************
*                                          AppGlobal_UsersInit()
*
* Description : Adds three users to the authentication system.
*
* Argument(s) : None.
*
* Return(s)   : DEF_OK,   if users initialization was successful.
*               DEF_FAIL, otherwise.
*
* Caller(s)   : Application.
*
* Note(s)     : None.
*********************************************************************************************************
*/

CPU_BOOLEAN  AppGlobal_UsersInit(void) 
{
    AUTH_USER    admin;
    AUTH_USER    user;
    AUTH_RIGHT   right;
    RTOS_ERR     err_auth;
    CPU_BOOLEAN  result;


    result = Auth_Init(&err_auth);
    if (result != DEF_OK) {
        return (DEF_FAIL);
    }

    result = Auth_CreateUser("admin",
                             "password",
                             &admin,
                             &err_auth);
    if (result != DEF_OK) {
        return (DEF_FAIL);
    }

    right = (HTTP_USER_ACCESS | AUTH_RIGHT_MNG);
    result = Auth_GrantRight(right,
                            &admin,
                            &Auth_RootUser,
                            &err_auth);
    if (result != DEF_OK) {
        return (DEF_FAIL);
    }
    result = Auth_CreateUser("user0",
                             "",
                             &user,
                             &err_auth);
    if (result != DEF_OK) {
        return (DEF_FAIL);
    }

    result = Auth_GrantRight(HTTP_USER_ACCESS,
                            &user,
                            &admin,
                            &err_auth);
    if (result != DEF_OK) {
        return (DEF_FAIL);
    }

    result = Auth_CreateUser("user1",
                             "user1",
                             &user,
                             &err_auth);
    if (result != DEF_OK) {
        return (DEF_FAIL);
    }

    result = Auth_GrantRight(HTTP_USER_ACCESS,
                            &user,
                            &admin,
                            &err_auth);
    if (result != DEF_OK) {
        return (DEF_FAIL);
    }

    return (DEF_OK);
}
 

/*
*********************************************************************************************************
*                                    AppGlobal_Auth_GetRequiredRightsHook()
*
* Description : Returns the rights required to fulfill the needs of a given request.
*
* Argument(s) : p_instance  Pointer to the HTTP server instance object.
*
*               p_conn      Pointer to the HTTP connection object.
*
* Return(s)   : returns the authorization right level for this example application.
*
* Caller(s)   : HTTPsAuth_OnAuth() via p_auth_cfg->GetRequiredRights()
*
* Note(s)     : none.
*********************************************************************************************************
*/

AUTH_RIGHT  AppGlobal_Auth_GetRequiredRightsHook (const  HTTPs_INSTANCE  *p_instance,
                                                  const  HTTPs_CONN      *p_conn)
{
    return (HTTP_USER_ACCESS);
}


/*
*********************************************************************************************************
*                                     AppGlobal_Auth_ParseLoginHook()
*
* Description : (1) Check all the HTTP requests received to see if they are related to resources of the login page.
*                   (a) Check if the POST login is received.
*                   (b) For each request set the redirect paths on no, invalid & valid credentials.
*                   (c) Parse the form fields received in the body for the user and password.
*
*
* Argument(s) : p_instance  Pointer to the HTTP server instance object.
*
*               p_conn      Pointer to the HTTP connection object.
*
*               state       State of the Authentication module:
*                               HTTPs_AUTH_STATE_REQ_URL:      The url and the headers were received and parse.
*                               HTTPs_AUTH_STATE_REQ_COMPLETE: All the request (url + headers + body) was received and parse.
*
*               p_result    Pointer to the authentication result structure to fill.
*
* Return(s)   : DEF_YES, if the request is the POST login.
*               DEF_NO,  otherwise.
*
* Caller(s)   : AppGlobal_AppInst_AuthCfg.
*
* Note(s)     : (2) This hook will be called twice for a request processed by the Authentication module:
*                   (a) When the Start line of the request (with the url) and the headers have been
*                       received and parse -> HTTPs_AUTH_STATE_REQ_URL state.
*                   (b) When all the request has been completely received and parse including the body
*                       -> HTTPs_AUTH_STATE_REQ_COMPLETE state.
*
*               (3) for each request received the redirect paths is set as follow:
*                   (a) RedirectPath_OnValidCred    INDEX_PAGE_URL
*                   (b) RedirectPath_OnInvalidCred  LOGIN_PAGE_URL
*                   (c) RedirectPath_OnNoCred
*                       (1) if the path is an unprotected path, let it go. (DEF_NULL) (i.e. the logo)
*                       (2) otherwise               LOGIN_PAGE_URL
*********************************************************************************************************
*/

CPU_BOOLEAN  AppGlobal_Auth_ParseLoginHook (const  HTTPs_INSTANCE     *p_instance,
                                            const  HTTPs_CONN         *p_conn,
                                                   HTTPs_AUTH_STATE    state,
                                                   HTTPs_AUTH_RESULT  *p_result)
{
    CPU_INT16S      cmp_val;
    CPU_BOOLEAN     is_login = DEF_NO;
#if (HTTPs_CFG_FORM_EN == DEF_ENABLED)
    HTTPs_KEY_VAL  *p_current;
    CPU_INT16S      cmp_val_username;
    CPU_INT16S      cmp_val_password;
#endif
    p_result->UsernamePtr = DEF_NULL;
    p_result->PasswordPtr = DEF_NULL;

                                                                /* Set redirect paths for each requests.                */
    p_result->RedirectPathOnInvalidCredPtr = LOGIN_PAGE_URL;
    p_result->RedirectPathOnValidCredPtr   = INDEX_PAGE_URL;

    switch (state) {
                                                                /* --------------- REQUEST URL RECEIVED --------------- */
        case HTTPs_AUTH_STATE_REQ_URL:
                                                                /* Set redirect paths for each requests.                */
             cmp_val = Str_Cmp(p_conn->PathPtr, LOGIN_PAGE_URL);
             if (cmp_val != 0) {
                 cmp_val = Str_Cmp(p_conn->PathPtr, MICRIUM_LOGO_URL);
                 if (cmp_val != 0) {
                     cmp_val = Str_Cmp(p_conn->PathPtr, MICRIUM_CSS_URL);
                 }
             }
             p_result->RedirectPathOnNoCredPtr = (cmp_val == 0) ? DEF_NULL : LOGIN_PAGE_URL;
                                                                /* Check if POST login received.                        */
             cmp_val = Str_Cmp(p_conn->PathPtr, LOGIN_PAGE_CMD);
             if (cmp_val == 0) {
                 is_login = DEF_YES;
             }
             break;
                                                                /* -------------- REQUEST BODY RECEIVED --------------- */
        case HTTPs_AUTH_STATE_REQ_COMPLETE:
#if (HTTPs_CFG_FORM_EN == DEF_ENABLED)
                                                                /* Parse form fields received for user/password.        */
             p_current = p_conn->FormDataListPtr;
             while ((p_current              != DEF_NULL)  &&
                    ((p_result->UsernamePtr == DEF_NULL)  ||
                     (p_result->PasswordPtr == DEF_NULL))) {
                 if (p_current->DataType == HTTPs_KEY_VAL_TYPE_PAIR) {
                     cmp_val_username = Str_CmpIgnoreCase_N(p_current->KeyPtr,
                                                            FORM_USERNAME_FIELD_NAME,
                                                            p_current->KeyLen);
                     cmp_val_password = Str_CmpIgnoreCase_N(p_current->KeyPtr,
                                                            FORM_PASSWORD_FIELD_NAME,
                                                            p_current->KeyLen);
                     if (cmp_val_username == 0) {
                         p_result->UsernamePtr = p_current->ValPtr;
                         is_login = DEF_YES;
                     } else if (cmp_val_password == 0) {
                         p_result->PasswordPtr = p_current->ValPtr;
                     }
                 }
                 p_current = p_current->NextPtr;
             }
#endif
             break;

        default:
             break;
    }

    return (is_login);
}


/*
*********************************************************************************************************
*                                     AppGlobal_Auth_ParseLogoutHook()
*
* Description : Parse requests received for logout URL and form data logout info.
*
* Argument(s) : p_instance  Pointer to HTTPs instance object.
*
*               p_conn      Pointer to HTTPs connection object.
*
*               state       State of the Authentication module:
*                               HTTPs_AUTH_STATE_REQ_URL:      The url and the headers were received and parse.
*                               HTTPs_AUTH_STATE_REQ_COMPLETE: All the request (url + headers + body) was received and parse.
*
* Return(s)   : DEF_YES, if Logout received.
*               DEF_NO,  otherwise.
*
* Caller(s)   : AppGlobal_AppInst_AuthCfg.
*
* Note(s)     : (1) This hook will be called twice for a request processed by the Authentication module:
*                   (a) When the Start line of the request (with the url) and the headers have been
*                       received and parse -> HTTPs_AUTH_STATE_REQ_URL state.
*                   (b) When all the request has been completely received and parse including the body
*                       -> HTTPs_AUTH_STATE_REQ_COMPLETE state.
*********************************************************************************************************
*/

CPU_BOOLEAN  AppGlobal_Auth_ParseLogoutHook (const  HTTPs_INSTANCE    *p_instance,
                                             const  HTTPs_CONN        *p_conn,
                                                    HTTPs_AUTH_STATE   state)
{
    CPU_BOOLEAN     is_logout = DEF_NO;
#if (HTTPs_CFG_FORM_EN == DEF_ENABLED)
    HTTPs_KEY_VAL  *p_current;
    CPU_INT16S       cmp_val;
#endif

    switch (state) {
                                                                /* --------------- REQUEST URL RECEIVED --------------- */
        case HTTPs_AUTH_STATE_REQ_URL:
                                                                /* Check if POST logout received.                       */
             cmp_val = Str_Cmp(p_conn->PathPtr, LOGOUT_PAGE_CMD);
             if (cmp_val == 0) {
                 is_logout = DEF_YES;
             }
             break;
                                                                /* -------------- REQUEST BODY RECEIVED --------------- */
        case HTTPs_AUTH_STATE_REQ_COMPLETE:
#if (HTTPs_CFG_FORM_EN == DEF_ENABLED)
                                                                /* Parse form fields received for logout.               */
             p_current = p_conn->FormDataListPtr;
             while (p_current != DEF_NULL) {
                 if (p_current->DataType == HTTPs_KEY_VAL_TYPE_PAIR) {
                     cmp_val = Str_CmpIgnoreCase_N(p_current->KeyPtr,
                                                   FORM_LOGOUT_FIELD_NAME,
                                                   p_current->KeyLen);
                     if (cmp_val == 0) {
                         is_logout = DEF_YES;
                         break;
                     }
                 }
                 p_current = p_current->NextPtr;
             }
#endif
             break;

        default:
             break;
    }

    return (is_logout);
}
Authentication Configuration Variables
/*
*********************************************************************************************************
*********************************************************************************************************
*                                              GLOBAL VARIABLES
*********************************************************************************************************
*********************************************************************************************************
*/

HTTPs_AUTHORIZATION_CFG  HTTPs_AppGlobal_AuthInst_Cfg = {
        AppGlobal_Auth_GetRequiredRightsHook
};

HTTPs_AUTH_CFG  HTTPs_AuthAppCfg = {
        AppGlobal_Auth_ParseLoginHook,
		AppGlobal_Auth_ParseLogoutHook
};

HTTPs_CTRL_LAYER_AUTH_INST  AppGlobal_AuthInst = {
        &HTTPsAuth_CookieHooksCfg,
        &AppGlobal_AuthInst_Cfg
};

HTTPs_CTRL_LAYER_APP_INST  AppGlobal_AppInst_AuthUnprotected = {
        &HTTPsAuth_AppUnprotectedCookieHooksCfg,
        &AppGlobal_AppInst_AuthCfg
};

HTTPs_CTRL_LAYER_APP_INST  AppGlobal_AppInst_AuthProtected = {
       &HTTPsAuth_AppProtectedCookieHooksCfg,                
       &AppGlobal_AppInst_AuthCfg                              
};