Using Ioto to Authenticate Requests

view-of-uranus

Ioto provides a powerful and flexible authentication framework to verify client requests.

This post is the sixth of a series on the Ioto embedded web server component and it covers how to authenticate HTTP requests to Ioto.

The posts in the series are:

Background

Web server authentication is the process by which a client’s identity and capabilities are verified before granting access to server resources. Authentication is essential when you have content to protect and provide only to specific, approved clients.

The Ioto web server implements a powerful and flexible authentication framework that verifies user credentials and controls client capabilities using a role based authorization mechanism.

Overview

Embedded device authentication over HTTP has historically used Basic or Digest authentication. However these two methods have security and usability flaws and should not be used.

A preferred method is to use a web form to capture the username and password and then transmit them securely using an encrypted TLS connection where the credentials are validated by the web server. Once authenticated, users are authorized with a role that defines their capabilities.

Access Roles

In Ioto, authentication roles for the Ioto web server are defined in the web.json5 configuration file via the auth.roles property. Access roles are an ordered list of user roles with increasing capabilities. Typical roles are: “user”, “admin” or “support”.

Here is a sample:

{
    auth: {
        roles: ["user", "admin"],
        login: '/api/public/login',
        logout: '/api/public/logout',
    }
}

The Ioto web server controls access to resources by requiring that a user possess a specific “role” before granting access to a document or resource. If the authenticated user has the required role or better, the user is granted access.

Documents and resources are partitioned by a route table that specifies the requesting URL and the required role to access.

For example:

web: {
    routes: [
        {match: '/api/admin/', role: 'admin'},
        {match: '/api/user/', role: 'user'},
        {match: '/api/'},
        {match: '/admin/', role: 'admin'},
        {match: '/user/', role: 'user'},
        {},
    ],
}

Each route specifies a matching URL prefix except for the last route which is a catchall. To restrict access, the route provides a role property that specifies the required user role to permit access.

Users and Passwords

Users are defined in the Ioto embedded database with their hashed and salted passwords and access role.

User entries are typically created by the device management or admin apps using the cryptMakePassword API. However, during development, users may be created in the database via the dbcmd and pass programs.

To generate a hashed password for a user, use the pass program provided with the Ioto distribution. For example:

pass --password demo-password ralph

This will print the hashed password for “ralph” that can then be edited into the config/seed.json5 file. The passwords can then be applied using the make “reset” target.

$ make reset

This command will run the following to update the user entry in the Ioto database:

$ dbcmd --reset --load config/seed.json5 --schema modes/$(MODE)/schema.json5 state/state.db

Authentication Schemes

Authentication schemes define how the user credentials are captured from the user and supplied to Ioto. There are two classes of authentication schemes.

Form Authentication

The form authentication scheme uses an HTML web form so a user can enter their username and password credentials, and an HTTP Post request to submit credentials to the server for verification. Ioto manages the login/logout process and if authentication succeeds, a login session is created and a cookie is returned to the client’s browser. Subsequent requests that include the cookie will be automatically authenticated using the session.

Web Form

Here is a sample login page:

<html><head><title>login.html</title></head>
<body>
    <p>Please log in</p>
    <form name="details" method="post" action="/api/public/login">
        Username <input type="text" name="username"><br/>
        Password <input type="password" name="password"><br/>
        <input type="submit" name="submit" value="OK">
    </form>
</body>
</html>

After the user enters their username and password and clicks OK, the form is posted and the /api/public/login URL is requested. This URL should be bound to an Action via webAddAction that will verify the password and then call webLogin.

To validate the user’s credentials in the server, Ioto invokes action routine bound to the login action URL. By default, these are bound in the web.json5 config file via the auth.login and auth.logout properties. The will invoke the webLoginUser and webLogoutUser APIs.

For example:

    auth: {
        login: '/api/public/login',
        logout: '/api/public/logout',
    },

If you require custom user authentication, you can define your own login action and leave the auth.login property unset. For example, the following custom action routine demonstrates how to retrieve and validate the user’s credentials:

static int login(Web *web)
{
    cchar *password, *username;

    username = webGetVar(web, "username", 0);
    password = webGetVar(web, "password", 0);

    // Your own custom password checking routine
    if (CheckPassword(username, password)) {
        webLogin(web, username, password);
        webRedirect(web, 302, "/welcome.html");
    } else {
        webRedirect(web, 401, "/login-failed.html");
    }
    return 0;
}
webAddAction(host, "/api/public/login", login);

The login function will retrieve the username and password entered by the user from the form post data and then validate these in the custom CheckPassword function.

SECURITY CAUTION: The login request should only be performed over a secure TLS encrypted connection and never over plain HTTP. You can use the following web.json5 directive to redirect all HTTP traffic over HTTPS.

{
    redirect: [{status: 302, to: 'https://'}]
}

Route Configuration

To implement the form based authentication, you should ensure the login and log out pages and all required graphics and stylesheets are accessible without authentication.

A typical set of routes for an authenticated web site is:

web: {
    routes: [
        {match: '/api/admin/', role: 'admin'},
        {match: '/api/user/', role: 'user'},
        {match: '/api/'},
        {match: '/admin/', role: 'admin'},
        {match: '/user/', role: 'user'},
        {},
    ],
}

Authenticated users can access documents under the /user section. Administrators have access to the /admin section. All other documents outside /user and /admin are public, such as the home page (/index.html), other HTML pages, stylesheets, javascript and images. These can be accessed without authentication.

The /api/admin section is accessible only to administrators and the /api/user is accessible to all authenticated users. All other actions under /api including the login and logout actions do not require authentication to access.

The {} route is used to match all other URLs and does not require authentication.

Route Table

URLDescriptionRequired Role
/index.htmlHome pagenone
/css/Stylesheetsnone
/js/JavaScriptsnone
/images/Imagesnone
/api/adminActions requiring an administrator useruser
/api/userActions requiring an authenticated useruser
/apiPublic actions including Login and logoutnone
/adminDocuments visible only to administrative usersadmin
/userDocuments visible to authenticated usersuser

Custom Authentication

You can create a custom or hybrid authentication scheme where the username and password is sent to the login action via another means such as an “Authorization” HTTP header or JSON payload. You would then adjust your login() function accordingly.

Authentication APIs

See the the following APIs to manage authentication and authorization under program control.

Samples

Ioto provides a sample for user login:

Want More Now?

To learn more about EmbedThis Ioto, please read:

Comments

{{comment.name}} said ...

{{comment.message}}
{{comment.date}}

Make a Comment

Thank You!

Messages are moderated.

Your message will be posted shortly.

Sorry

Your message could not be processed at this time.

Error: {{error}}

Please retry later.

OK