Skip to content

AuthSodium is a Laravel package for authenticating API requests with LibSodium's public-key (ED25519) signatures

License

Notifications You must be signed in to change notification settings

ROTGP/auth-sodium

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

78 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AuthSodium

AuthSodium is a Laravel package for authenticating API requests with LibSodium's public-key (ED25519) signatures. Sodium is available natively in PHP since version 7.2 without an extension. Some objectives of the package:

  • use modern security standards
  • provide stateless RESTful API asymmetric authentication
  • remove complexity of clients having to request/manage/renew tokens
  • remove the need for sending sensitive credentials (such as passwords) to the server
  • offload memory/cpu intensive work (such as slow password hashing) from the server to the client
  • manage nonces transparently and automatically
  • throttle and block malicious users
  • highly customizable
  • non-invasive
  • no dependencies (other than Laravel itself)
  • thoroughly tested
  • fast and lightweight
  • support Laravel 7+

The traditional authentication model

The traditional model is for the client to send the user's password to 'log in', which is then hashed on the server (which is computationally expensive) and compared with a previously-saved hash. If they match, then a token (with an expiration date) is stored on the server and issued to the client. For future requests, the client provides the token. The issue with this is that the client must then store the token, send it with each request, renew it if it has expired, or request a new one if it is rejected. Furthermore, tokens are susceptible to misuse if they fall into the hands of an attacker, as they may be used to authenticate any number of requests.

What is a stateless API?

Put simply, each request must contain all of the information necessary to be understood by the server, and to authenticate the user making the request, rather than being dependent on the server 'remembering' previously successful authentications from the user.

How LibSodium works

The package works by verifying the signature sent with each authenticated request. Your user model should contain a public key for each user.

User registration

How you choose to manage the user's public and private keys is up to you, however, the following is potential workflow for user registration:

  • choose appropriate LibSodium bindings for your client
  • user provides email and chooses a password
  • input password into a PBKDF (several options are provided by LibSodium) to produce cryptographically secure pseudo-random bytes
  • input bytes into LibSodium's crypto_sign_seed_keypair function to deterministically produce the user's public and private keys
  • send the user's email address and public key to the server to be registered, being sure to sign the request with the user's private key, to ensure that the public key is valid

Each request must contain headers with the following metadata:

  • a unique identifier for the user to be authenticated (for example: an email address)
  • a nonce
  • a timestamp
  • a signature

The signature, which is generated by the client, is calculated by signing data related to each request. This data is comprised of:

  • the HTTP request method (get, put, post, or delete)
  • the fully qualified URL of the request (with the http:// or https://www), but without any query strings
  • query data (sorted alphabetically and json-encoded), or an empty string in the absense of query data
  • post data (json-encoded), or an empty string in the absense of post data
  • user identifier (such as the user's email address)
  • timestamp (in milliseconds, unless using a 32-bit version of PHP, in which case it should be in seconds) since midnight January 1st 1970 (UTC)
  • nonce (a number or string which should not be repeated)

This data is then concatenated to make a single string, which is then signed with the user's private key. The signature is then sent as a header.

On the server side, this string is reconstructed. Once the auth user has been retrieved, their public key is used to verify the signature. If the signature fails then the appropriate error response is returned, otherwise, the request continues as per normal.

Installation

  1. Require this package with composer.
composer require rotgp/auth-sodium
  1. Publish the AuthSodium config file to your project with the following command:
php artisan vendor:publish --provider="ROTGP\AuthSodium\AuthSodiumServiceProvider" --tag="config"
  1. Configure your user model

  2. Review the database schema options

  3. Run php artisan migrate


Configuration

Assuming you've performed the above installation, the config file can then be found at config/authsodium.php.

User Model

The only thing you're required to tell AuthSodium about is the class of your user model. This must extend Illuminate\Database\Eloquent\Model, and implement Illuminate\Contracts\Auth\Authenticatable. For convenience, the model may simply extend ROTGP\AuthSodium\Models\AuthSodiumUser which already meets these requirements.

'user.model' => App\Models\User::class

AuthSodium needs to know how to uniquely identify your auth user. By default, this will be with the user's 'email' attribute, but you may choose anything, such as username or even an id (assuming the user knows their own id).

'user.unique_identifier' => 'username'

You should also note that AuthSodium will look for the user's public key using the 'public_key' attribute of the user model. If you wish to call it something else, then you may do so as follows:

'user.public_key_identifier' => 'pub_key'


Specify a delegate

Customizing the package's config values should be enough for most use-cases, but if you require more fine-grained control of the AuthSodium's logic, you may specify a custom delegate. By default the delegate points to ROTGP\AuthSodium\AuthSodiumDelegate, but you can extend this class and override any functionality you like. Simply update the config value to point to your custom class as follows:

'delegate' => 'My\Custom\AuthDelegate::class'


Database schema

Before running AuthSodium's migration, you should consider the following options.

Nonce length

The length of the database column for nonce. By default it's 44, which is 32 base64-encoded bytes. For hex encoded nonces, the length should be 64. Note that this is just a plain string (or integer as a string). It is convenient to generate random bytes with a CSPRNG and encode them as hex or base64, but in the end it's just a string.

'schema.nonce.length' => 44

Nonce uniqueness

Whether or not the nonce should be unique per user/timestamp.

If true, then a unique constraint for user/nonce/timestamp will be created at database level, meaning that a nonce can be reused if it has a different timestamp. A request with a repeating user/nonce/timestamp will still be rejected if the timestamp does not fall within leeway of the system time. This allows for more margin or error (random nonces being repeated), as the nonces must only be unique within leeway of the system time.

If false (the default), then the unique constraint will be for the user/nonce, regardless of the timestamp. So, if user/nonce is repeated (even if days apart), an exception will occur. This means that in order to avoid conflicts, nonces should be cleared regularly (the default).

In either case, using 256-bit nonces generated by a CSPRNG should be more than sufficient to ensure no accidental collisions occur. More discussion here: here.

'schema.nonce.unique_per_timestamp' => false


Protecting routes with middleware

Named Middleware

By default, AuthSodium provides middleware called 'authsodium'. To protect a route, simply add the middleware in the same way you'd normally add middleware Route::resource('foos', FooController::class)->middleware('authsodium'); The name of the middleware can be customized as follows:

'middleware.name' => 'custom_middleware_name'

Global Middleware

If you want to protect all incoming requests automatically, then set 'middleware.global' to true:

'middleware.global' => true

Middleware groups

If you want to add AuthSodium to a particular middleware group (such as 'web', or 'api'), then you may do so as follows:

'middleware.group' => 'api'

Aborting requests

By default, AuthSodium will abort requests with invalid signatures automatically, with the appropriate status and error codes (which are customizable). There may however be situations where you wish to proceed with the request, without establishing an authenticated user. To achieve this - adjust the following value:

'middleware.abort_on_invalid_signature' => false


Leeway

The leeway (in milliseconds, unless you're using a 32-bit version of PHP, in which case it is in seconds), on either side of the timestamp, in which to allow valid requests. A leeway of 300000 milliseconds (the default) equates to a request timestamp within 5 minutes (before or after) the current system timestamp being accepted. The larger the value, the more forgiving the service, but this will also result in more nonces being stored at any given time. This, however, should not be a concern, as nonce deletion is managed automatically.

The value may be defined as desired, however please note that for security reasons - it is not recommended to use a value that exceeds one hour.

300000 milliseconds = 300 seconds = 5 minutes

'leeway' => 300000


Nonce pruning

For security reasons, AuthSodium must keep a record (in the database) of all the nonces used for a particular user (and possibly also timestamp, according to authsodium.schema.nonce.unique_per_timestamp), where the nonce is not older than the value of authsodium.leeway. Nonces that are older than this value can be safely (and automatically) deleted on a periodic basis. Below are a few of the options available, please check the config file for more.

Prune nonces after each request

'after_request' => true

Prune nonces on terminating (a long-running) application

'on_terminate' => false

Prune nonces daily at a specified time

'daily_at' => '23.45'

Request throttling

Failed authenticated requests may be throttled to limit malicious behaviour. If a request's signature is invalid (or missing), and throttling is enabled, then the client must wait for a config-defined number of seconds before attempting another request.

Enable throttling

'throttle.enabled' => true

Define decay

The invervals (in milliseconds, unless you're using a 32-bit version of PHP, in which case it is in seconds) after which a new authentication attempt can be made, after having made an initial failed one. Zero indicates that an attempt can be made immediately. Intervals are relative to the preceding one, so the default would allow three consecutive immediate attempts, then an attempt in 1 second, then 3 seconds following that, etc. After the last attempt fails, the user is considered to be blocked.

'throttle.decay' => [0, 0, 0, 1000, 3000]

User validation

Provide a route name such as 'auth/validate' which will point to the validate method of ROTGP\AuthSodium\Http\Controllers\AuthSodiumController. The request should be a simple signed GET request to the route name provided, with no query or post data. The user is then authenticated and returned. If the authentication should fail, then the appropriate codes will be returned.

'routes.validate' => 'auth/validate'

Force TLS

Options for enforcing secure HTTPS/TLS connections. While it's ideal to ensure this with a web-server configuration (such as Nginx) - sometimes that is not possible.

Environments to secure

'secure.environments' => 'auth/validate'

Environments to secure

'secure.environments' => ['production']

Valid schemes

The schemes which are acceptable in secure environments. This should only ever really be https, however, other schemes do exist, such as 'wss' (secure web sockets).

'secure.schemes' => ['https']






License

AuthSodium is provided under the MIT License.

About

AuthSodium is a Laravel package for authenticating API requests with LibSodium's public-key (ED25519) signatures

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages