Project

General

Profile

RESTful API authentification example

Added by Thomas Saquet over 11 years ago

Hello,

I needed a RESTful API with authentication, I wrote code below.

I use it as mother class of another resource, using the "authentified" boolean in the sub class to handle authentification. Can you tell me if it is a good/secure "wt way" to do it or if there is a better one ? It works with the Wt Auth example.

#include <Wt/WServer>
#include <Wt/WResource>
#include <Wt/Http/Response>
#include "your_session"

class PublicApiResource : public Wt::WResource {
public:

    virtual ~PublicApiResource() {
        beingDeleted();
    }

protected:
    Wt::WString login;
    Wt::WString password;
    bool authentified;

    virtual void handleRequest(const Wt::Http::Request &request, Wt::Http::Response &response) {
        Wt::log("info") << "[PUBLIC API] Identifying";
        // Setting the session
        Session session("your_connection_string");
        Session::configureAuth();

        // default : not authentified
        this->authentified = false;

        // setting login and password, ending the request if empty
        if (!request.getParameterValues("login").empty()) 
        {
            this->login = request.getParameterValues("login")[0];
        }
        else
        {
            Wt::log("error") << "[PUBLIC API] login parameter missing";
            return;
        }
        if (!request.getParameterValues("password").empty()) 
        {
            this->password = request.getParameterValues("password")[0];
        }
        else
        {
            Wt::log("error") << "[PUBLIC API] password parameter missing";
            return;
        }

        const Wt::WString pass = this->password;

        // transaction
        {
            try {
                Wt::Dbo::Transaction transaction(session);

                // check whether the user exists
                Wt::Dbo::ptr<AuthInfo::AuthIdentityType> authIdType = session.find<AuthInfo::AuthIdentityType > ().where("\"identity\" = ?").bind(this->login);
                if (authIdType.id() != -1)
                {
                    // find the user from his login
                    Wt::Auth::User user = session.users().findWithIdentity(Wt::Auth::Identity::LoginName,this->login);

                    if (!user.isValid()) 
                    {
                        Wt::log("info") << "[PUBLIC API] User invalid";
                        return;
                    }

                    // verify
                    switch (session.passwordAuth().verifyPassword(user, pass))
                    {
                        case Wt::Auth::PasswordValid:
                            session.login().login(user);
                            this->authentified = true;
                            Wt::log("info") << "[PUBLIC API] " << user.id() << " logged.";
                            break;
                        case Wt::Auth::LoginThrottling:
                            Wt::log("info") << "[PUBLIC API] too many attempts.";
                            break;
                        case Wt::Auth::PasswordInvalid:
                            Wt::log("info") << "[PUBLIC API] " << user.id() << " failure number : " << user.failedLoginAttempts();
                            break;
                        default:
                            break;
                    }
                } 
                else 
                {
                    Wt::log("error") << "[PUBLIC API] User not found";
                }
            } catch (Wt::Dbo::Exception const& e) 
            {
                Wt::log("error") << "[PUBLIC API] " << e.what();
            }
        }
    }
};

int main(int argc, char **argv)
{
    try
    {
        PublicApiResource pub;

        Wt::WServer server(argv[0]);

        server.setServerConfiguration(argc, argv);
        server.addResource(&pub, "/pub");

        if (server.start())
        {
            Wt::WServer::waitForShutdown();
            server.stop();
        }
    }
    catch (Wt::WServer::Exception& e)
    {
        std::cerr << e.what() << std::endl;
    }
    catch (std::exception &e)
    {
        std::cerr << "exception: " << e.what() << std::endl;
    }
}

Replies (3)

RE: RESTful API authentification example - Added by Thomas Saquet over 11 years ago

Sorry for the frenglish "authentification" in the title...

RE: RESTful API authentification example - Added by Wim Dumon over 11 years ago

Hello Thomas,

If you access the API from JS that runs in the context of an already authenticated session, you can use a session-bound resource and the url() method to obtain an auto-generated URL, which needs no further authentication.

Here you're indeed using a static resource, that can be accessed without an active session, so you cannot use the session security mechanisms for authentication. It's up to you to decide on how to write your authentication; I like how you reuse Wt::Auth for this :) On the other hand, you chose to simply pass login and password as http parameters, which is probably OK if you only allow https access to this resource. If your resource is accessible over non-https connections, did you consider alternative methods, such as http://en.wikipedia.org/wiki/Digest_access_authentication ? (note I'm not an expert on this matter, and haven't done this in combination with Wt's resources).

BR,

Wim.

RE: RESTful API authentification example - Added by Thomas Saquet over 11 years ago

Hello Wim,

Thank you for your answer. This is exactly what I need, a static resource accessed from an https connection. I indeed read about digest authentication in an other post from Wt forum but I didn't go further since we won't allow http connection. I think I will add a control to limit access to POST requests.

It was important for me to reuse Wt::Auth because this API will be accessible for users registered through a "normal" Wt::Auth use, and it is great to benefit from the authentication mechanism you implemented in Wt.

If it makes sense to add such an example to Wt, feel free to take the code :)

Best regards,

Thomas

    (1-3/3)