0

(I am not talking about authenticating calls to RESTful API. I am talking about creating login logic for user via RESTful API.)

When user access any page of my site, a servlet filter will intercept the request and check if necessary authentication info exists in session. If not exists, user will be directed to login page.

On the login page, an ajax call is made to a RESTful API on server with username and password. Depending on the return status of that RESTful API, the JavaScript on the page will decide whether let user into the site. Note that the actual authentication logic is still carried out on server side. Client JS only acts based on the result from server.

On the server, the RESTful login API will check the submitted username/password and see if it is contained in DB. If exists, it will store necessary authentication info into the session so future requests from the same client will not be blocked.

My questions are:

  • Is this login logic sound?

  • Because session is involved, the RESTful login API is kind of not stateless. So is it still RESTful?

Here's my code:

login.js

// login.js
$(function () {
    $('#submitDiv').click(doLogin);
});

function doLogin() {
    $('#resultDiv').text("start!");
    user = new Object();
    user.username = $('#txtUsername').val();
    user.pwd = $('#txtPassword').val();

    $.ajax({
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        },
        'type': 'POST',
        'url': 'doLogin.sm',
        'data': JSON.stringify(user),
        'dataType': 'text',
        'success': loginSuccessful,
        'complete': function (jqXHR, textStatus) {
            $('#resultDiv').text("complete with:" + textStatus);
        }
    });
}

function loginSuccessful() {
    //if referrer is null, jump to dashboard, else jump to referrer.
    var referer = getUrlVars()['referer'];
    if (referer) {
        window.location.replace(referer);
    }
    else {
        window.location.replace('dashboard.html');
    }
}

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>My Cloud - Login page</title>
    <link rel="stylesheet" type="text/css"
          href="resources2/css/bootstrap.css">
    <meta charset="utf-8">
    <!--force to use the latest IE engine-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="resources2/js/jquery-1.11.3.js"></script>
    <script src="resources2/js/bootstrap.js"></script>
    <script src="resources2/js/pagejs/common.js"></script>
    <script src="resources2/js/pagejs/login.js"></script>
</head>
<body>
    <h1>My Login Page</h1>
    <div id="loginDiv">
        <input id="txtUsername" type="text" value="test">
        <input id="txtPassword" type="password" value="test">
        <div id="submitDiv" class="btn btn-default">
            Login
        </div>
        <div id="resultDiv"></div>
    </div>
</body>
</html>
smwikipedia
  • 52,824
  • 76
  • 267
  • 432
  • Why do you need a session in your server? In RESTful applications, the session state is completely managed by the client. – cassiomolin Dec 08 '15 at 10:49

2 Answers2

1

If your JavaScript decides whether to allow the user to get pages from your site, what is stopping the user from setting a breakpoint in the JavaScript code and changing the outcome of the authentication process?

Authorization decisions need to happen on the server as you can never trust the client (browser).

Your site needs to verify at the server side that the user is authenticated. This is typically done by sending some kind of implicit authentication info along with the request that the server can verify and the client cannot forge.

I don't see that happening in your design. But why are you trying to design your own authentication mechanism? Why not use a standard protocol like WS-Federation, SAML-P, or OpenID Connect for this?

MvdD
  • 17,926
  • 5
  • 51
  • 83
  • If user set a breakpoint in JS and changing the auth result return from server, he still cannot access the site because he cannot modify the `session` object on server. And the `login servlet filter` will intercept `all requests` and notice there's no valid auth info in session object and thus block him. I implement my own because I only need a simple enough login mechanism. – smwikipedia Dec 08 '15 at 02:48
  • @smwikipedia Okay, so you intercept requests and check a session object. You redirect to a login page when there's no session object. How does your server validate the session. Does it look up the session identifier in a store? Using a session would definitively violate RESTful principles. An OAuth2 flow will give you a JWT token that does not need any state on the server. You can use libraries to simplify the implementation. – MvdD Dec 08 '15 at 03:12
  • I didn't use a session store. I noticed that upon every first request to my site, server sets a `JSESSIONID` cookie on the client. I don't know who did this but I guess it's done by tomcat rather than by my application. Anyway with this sessionID, I can always call `HttpServletRequest.getSession()` in the servlet filter to get a session object and check auth info in it. – smwikipedia Dec 08 '15 at 03:20
  • Found it about who creates `JSESSIONID`. It is ultimately still by my code. Not tomcat. http://stackoverflow.com/questions/595872/under-what-conditions-is-a-jsessionid-created – smwikipedia Dec 08 '15 at 04:10
  • The login-function, by definition, creates a session. REST, by definition, can not have state. I can't see how you can combine these concepts. On the other hand, I don't see why breaking this part of REST is a problem. You can avoid using the Session-object on the server if you put the login-state somewhere else (as MvdD suggests), but that seems like an implementation detail. – Rolf Rander Dec 08 '15 at 07:35
  • @RolfRander Thanks. But I recently read about some `stateless authentication`. Such as JWT(JSON Web Token) based authentication. I am still learning it. And I am trying to stick to REST principle as much as I can. – smwikipedia Dec 17 '15 at 02:15
1

Session state on the client (not on the server)

That's what Roy Thomas Fielding, The REST Godfather, said in his dissertation regarding the stateless constraint:

5.1.3 Stateless

[...] each request from client to server must contain all of the information necessary to understand the request, and cannot take advantage of any stored context on the server. Session state is therefore kept entirely on the client. [...]

So, if you are keeping the session state on the server, it's not REST.

In REST you won't have a session on the server and, consequently, you won't have a session identifier.

Each request must contain all data to be processed

Each request from client to server must contain all of the necessary information to be understood by the server. With it, you are not depending on any session context stored on the server.

When accessing protected resources that require authentication, for example, each request must contain all necessary data to be properly authenticated/authorized. It means the authentication will be performed for each request.

And authentication data should belong to the standard HTTP Authorization header. From the RFC 7235:

4.2. Authorization

The Authorization header field allows a user agent to authenticate itself with an origin server -- usually, but not necessarily, after receiving a 401 (Unauthorized) response. Its value consists of credentials containing the authentication information of the user agent for the realm of the resource being requested. [...]

Basic authentication

The Basic Authentication Scheme, defined in the RFC 7617, is a good start for securing a REST API:

2. The 'Basic' Authentication Scheme

The Basic authentication scheme is based on the model that the client needs to authenticate itself with a user-id and a password for each protection space ("realm"). [...] The server will service the request only if it can validate the user-id and password for the protection space applying to the requested resource.

[...]

To receive authorization, the client

  1. obtains the user-id and password from the user,

  2. constructs the user-pass by concatenating the user-id, a single colon (":") character, and the password,

  3. encodes the user-pass into an octet sequence,

  4. and obtains the basic-credentials by encoding this octet sequence using Base64 into a sequence of US-ASCII characters.

[...]

If the user agent wishes to send the user-id "Aladdin" and password "open sesame", it would use the following header field:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

[...]

Remember the HTTPS is your best friend to prevent the man-in-the-middle attack.

Tokens

If you don't want sending the username and the password over the wire for every request, you can consider creating a token based authentication. In this approach, you exchange your hard credentials (username and password) for a token which is sent in each request.

Again, the authentication must be performed for every request.

Basically, the token can be opaque (which reveals no details other than the value itself, like a random string) or can be self-contained (like JSON Web Token).

  • Random String: A token can be issued by generating a random string and persisting it to a database with an expiration date and with a user identifier associated to it.

  • JSON Web Token (JWT): Defined by the RFC 7519, it's a standard method for representing claims securely between two parties. JWT is a self-contained token and enables you to store a user identifier, an expiration date and whatever you want (but don't store passwords) in a payload, which is a JSON encoded as Base64. The payload can be read by the client and the integrity of the token can be easily checked by verifying its signature on the server. You won't need to persist JWT tokens if you don't need to track them. Althought, by persisting the tokens, you will have the possibility of invalidating and revoking the access of them. To find some great resources to work with JWT, have a look at http://jwt.io.

There are many databases where you can persist your tokens. Depending on your requirements, you can explore different solutions such as relational databases, key-value stores or document stores.

cassiomolin
  • 101,346
  • 24
  • 214
  • 283
  • If I store the token and token's expiration date `on server side`, doesn't that mean some context info being stored on server? Does this break the code of `all state entirely on client side`? Since one of REST's point is scalability, if I store such info on one server, I will have to figure out how to synchronize such context info across servers. Or just *affiliate* a request to a specific server. – smwikipedia Dec 14 '15 at 04:24