OAuth2 Implementation

The OAuth2 implementation on Snap! Websites makes use of a GET to log an application in, and a GET to log it out.

Log In

The Log In feature makes use of the Snap-Authorization header field which is 100% copied from the Basic Authorization. We use the name Snap-Authorization because Apache2 does not forward the Authorization header field to Snap!

GET /user/oauth2 HTTP 1.1
Host: snapwebsites.org
User-Agent: Application 1.0
Accept: application/json;q=0.7,application/xml;q=0.5
Snap-Authorization: Snap <base64 from "identifier:secret">

The OAuth2 module gives users two randomly generated and very large names. One is call the identifier and the other is called the secret. The Snap-Authorization field expects the identifier and secret to first be concatenated with a colon in between and then encoded using a one line base64 encoding scheme. At this time we do not support receiving that base64 data on more than one line.

To encode your identifier and secret, you may make use of the Qt library as follow:

QString const identifier("my-id");
QString const secret("top-secret");
QString const credentials(identifier + ":" + secret);
QByteArray const buffer(credentials.toUtf8());
QByteArray const base64(buffer.toBase64());
QString const snap_authorization("Snap-Authorization: Snap " + QString::fromUtf8(base64.data()));

The server accepts JSON or XML as the output format. The default is XML if no Accept header is specified.

The User-Agent is mandatory as it is used to make sure that the session created for the application to log in remains the same for the entire session.

The result looks as follow:

// XML
<?xml version="1.0"?>
<snap version="0.1.70" oauth2="1.0">
  <result>success</result>
  <oauth2-session type="Bearer">12345/12345</oauth2-session>
  <timeout>1234567890</timeout>
</snap>

// JSON
{
  "version": "0.1.70",
  "oauth2": "1.0",
  "result": "success",
  "session": "12345/12345",
  "session_type": "Bearer"
  "timeout": 1234567890,
}

The data is the same in both cases. With the session identifier you can now access pages that are otherwise forbidden to anonymous users. The application is logged in just as if the user with the specified OAuth2 email address as logged in the system.

The timeout parameter in the result specifies when this OAuth2 session will timeout. It is probably safer to hit the server at most 1 minute before that timeout is reached to avoid timing out while trying to access the server. The number represents the Unix data in seconds since Jan 1, 1970. Note that it is a 64 bit number (i.e. time_t).

Using REST and SOAP APIs that require an OAuth2 identifier

Further API calls using the session identifier add the following header:

Snap-Authorization: Bearer 12345/12345

The authorization type, "Bearer", is taken from the result (see above). You should use the type returned by OAuth2 to make sure your code works. Also, make sure that the User-Agent field remains the same.

Note: At this time, the random number (number after the slash) will never change. Also, the lifetime of a Bearer session number is never extended. Once used for some time (3h by default) the code becomes invalid and the application cannot do much more than Anonymous scrapping. However, querying for a new session code will not prevent continuation of manipulation of data started with a previous session code.

Logging an Application Out

Once an application is done, it should log out to make sure that the session does not get used by others (it is otherwise a security concern.)

To do so, an application can simply access the /logout page with a request as follow:

GET /logout HTTP 1.1
Host: snapwebsites.org
User-Agent: Application 1.0
Accept: application/json;q=0.7,application/xml;q=1.0
Snap-Authorization: Bearer 12345/12345

The Snap-Authorization must be specified since the server needs to know which one needs to be deleted. The server responds by setting the path to "/user/" instead of "/user/<email address>" and returns JSON or XML on success (XML by default). On an error, you get an error code and the body is likely HTML.

// XML
<?xml version="1.0"?>
<snap version="0.1.70" oauth2="1.0">
  <result>logged out</result>
</snap>

// JSON
{
  "version": "0.1.70",
  "oauth2": "1.0",
  "result": "logged out"
}

The result tag or field says "logged out" on success. After this call, attempting to use that same code will result in various types of errors. In most cases, you will get a redirection to the /login page unless you attempt to hit REST or SOAP pages in which case the result will certainly be a Permission Denied error.

Note that calling the /logout multiple times is likely to succeed each time, at least as long as the server keeps a copy of the sessions (Cassandra is asked to automatically delete old sessions.)

Why Not Use Cookies?

We weighted the concept of using the Cookies field instead of Snap-Authorization. However, that could cause problems with applications written in JavaScript and running in browsers. Those would eventually send the normal user cookies and be sent new once by the OAuth2 reply.

Also cookies are difficult to parse when just a simple session identifier is necessary to implement OAuth2.

Snap! Websites
An Open Source CMS System in C++

Contact Us Directly