OAuth

Introduction

So far, the credentials used when submitting messages to EMG have been pairs of usernames and passwords, identifying an account. This is easy to maintain, but has some issues when messages are sent from multiple applications or hosts (below called clients), using the same username.

  1. If delivery reports are needed, each client needs their own username. This results in various pieces of information having to be duplicated.
  2. It’s not possible to see in the logs which client sent which message.
  3. If the credentials leak from one client, all clients using the same credentials must be updated.

For these reasons and several more, there was a general need to separate an account from the credentials used by its clients. For HTTP based system, the protocol OAuth was published as an RFC in 2010. This was later updated to version 2.0 in 2012. More information is available at https://oauth.net. OAuth 2.0 was in 2015 extended for use with SMTP, using the OAUTHBEARER and XOAUTH2 capabilities.

We’re now including this protocol in EMG, both as a client and as a server. EMG enforces an encrypted connection in both cases, and the OAuth specification insists on at least TLS 1.2.

Pros

Using OAuth has a couple of important advantages.

  1. EMG can log the name of the client for each message.
  2. If the credentials for a client leaks, we can disable just that particular client. All other clients for the same account are unaffected.

The access token the resource server gets back is often a structured message, basically saying “the user with email username@example.se is allowed to use services A and B”. This message is then cryptographically signed by the authorization server so it cannot be modified by the client, and encrypted so it can only be read by the resource server. This way the resource server does not need to contain any passwords of any kind, and the same authorization server can be used by multiple resource servers.

Cons

It obviously also has some disadvantages, mainly that managing the constantly updated tokens requires some care.

Data flow

The OAuth 2.0 specification describes 4 different data flows, and the one relevant for us is called “Client Credentials Grant” from section section 4.4.

Before this data flow can begin, the new client must be registered. This step is application dependent, but should result in the client getting an id and a secret which must be kept safe.

  1. The clients sends a request for a new access token, identifying itself using HTTP Basic Auth with its client_id and client_secret as the username and password, respectively.
  2. The authorization server sends back an access token.
  3. The client uses this access token when performing operations such as sending a message. The access token is short lived and is normally only valid for an hour. After this, the client requests a new access token.

As access tokens are requested using the id/secret pair, there is no refresh token involved, as with the data flow “Authorization Code” used when the client is a web browser.

EMG as an OAuth server

Registering a new client is, for now, done manually, by adding a record in the new emguser_oauth_client table. Its definition is shown in the “Configuration” section below. A record in the table is linked to a record in the emguser table, and contains the id and a secret for the client. The secret should be provided in plain text to the client system, but in the database, EMG stores the SHA256 checksum.

The format and size of the access tokens are intentionally not described in the specifications. For HTTP and SMTP this is not a problem, as we can just use a random string of any desired length. In this case EMG creates a string of 64 random bytes.

However, for SMPP, the username can be at most 15 characters and the password at most 8. In this case we create a username and password as random strings of those lengths, and then combine them in an access token as “username-password“. It is then up to the client to separate these parts. The dash will not be part of neither the username nor the password. We were not able to find a published specification for this case with more than one string in the same access token, but this will do for now. The generated username will not be one that already exists in the database.

In either case, EMG again stores just the SHA256 checksum of the access token sent to the client. Now, let’s assume an attacker gets access to these checksums. For the SMPP case they would have to try about 2^138 (a bit over 3*10^41) usernames and passwords to find the right one, and after at most an hour the checksum is invalid. Even if a total of just 23 characters may feel a bit weak, it is clearly not. Besides, in most cases we’ve seen, existing SMPP usernames are typically shorter than 15 characters. For the HTTP and SMTP cases they would have to try 2^512 (just over 10^154) cases within the same time.

Both the client secret and refresh tokens also require 2^512 tests. They have a much longer validity time, but until a security flaw is found in SHA256, these cannot be found by brute force.

In order to separate the two types of access tokens, the OAuth 2.0 specification provides the parameter “scope”. This is sent in the first step for both data flows, and should be set to “smpp” or “smtp”. According to the specification this field should allow multiple scopes in the same request, but as the format of the two types of generated access tokens differ, we currently do not support that.

EMG is its own authorization server. This is automatically handled by any incoming HTTP connector, at the /oauth/2.0/token endpoint. The connector must have the connector option SSL so connections are encrypted, and must use USERDB. All information about the generated access token is stored in the emguser_oauth_token table, also shown in the “Configuration” section below.

You can of course implement your own such server if you want, as long as you populate the emguser_oauth_client and emguser_oauth_token tables in the same way.

EMG as an OAuth client

For the protocols HTTP, SMTP, and SMPP, EMG can use an access token for the authentication step. The connectors must have SSL set in this case as well. HTTP connectors must have XAUTH=BEARER. SMTP connectors must have XAUTH=XOAUTH2 or XAUTH=OAUTHBEARER, and in both cases USERNAME must be set to the username the client_id belongs to.

For the connector “smpp_out_1“, you put the JSON data containing the access_token in the file $EMGDIR/spool/oauth.smpp_out_1. This file is then reloaded automatically when the connection has been established, but before the login operation has been sent. The validity of the access token is taken from the last modification time of the file, plus the value in the expires_in field. Expired tokens are ignored.

As the access tokens typically have a validity period of one hour, you can create a shell script that is run every half hour or so, creating a new token file. It’s basically just a command such as the one below. This way the emgd process has no access to the client credentials.

curl --basic --data client_id=ClientName --data client_secret=VerySecret
--data grant_type=client_credentials --data scope=smpp
https://emg.example.se:9001/oauth/2.0/token
> /etc/emg/spool/oauth.smpp_out_1

Log files

As expected, the pdu log files looks exactly the same as before. However, for login operations the usernames and passwords are taken from the access token.

The connector.* log files work on a slightly higher level, but instead of just logging the username from the emguser table, you will not also get an additional part “client=client_id“, where the client_id is the name of the client in the emguser_oauth_client table.

The client_id is also added to all incoming messages, as the MGP option CLIENT/230. This value can then be used in reports or for routing.

Similarly to the MGP option XUSERNAME/119 which makes sure delivery reports are sent back to the same user that sent the original message, we have also added the MGP option XCLIENT/231 to make sure the OAuth client name also matches.

Configuration

In order to activate this feature, you must first create two new database tables, as shown below.

CREATE TABLE emguser_oauth_client (
id INTEGER NOT NULL AUTO_INCREMENT,
emguser_id INTEGER NOT NULL,
client_id VARCHAR (64) NOT NULL,
client_secret CHAR (64) NOT NULL,
PRIMARY KEY(id),
UNIQUE emguser_oauth_client_client_id_emguser_id (emguser_id, client_id)
);

CREATE TABLE emguser_oauth_token (
id INTEGER NOT NULL AUTO_INCREMENT,
client_id INTEGER NOT NULL,
scope_type TINYINT default 0,
token_type TINYINT default 0,
access_token CHAR (64) NOT NULL,
access_expires_at DATETIME NOT NULL,
PRIMARY KEY(id)
);

Next, you set the global configuration option ENABLE_OAUTH in your server.cfg file.

Finally, all related connectors must have SSL set, and use TLS 1.2 or higher. In order to achieve the latter, please also set SSL_MIN_PROTO=TLS1_2, either on each such connector, or in the top section of the server.cfg file.