Skip to content

An HTTP API client authentication scheme based on RSA signatures

Notifications You must be signed in to change notification settings

meyercm/sig_auth

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SigAuth

An HTTP API client authentication scheme based on RSA signatures.

In an HTTPS environment, the server's identity is trusted; proving the identity of the client can be accomplished in many ways; This scheme is inspired by the AWS authentication scheme, with a few modifications for simplicity's sake.

To prove identity, the client holds the private key of an RSA keypair, and creates a digital signature of the request as it is submitted. The server can then validate the signature using the public key of the client claiming to submit the request.

This library makes no assumptions about your HTTP client or server, except that they allow you to specify and read the headers, body, method and path of a request.

Example Use

Generate Keys

The ssh-keygen command creates RSA public/private keypairs, saving the results in files that SigAuth can load using load_key/1

The bash commands for generating new keys. Note that SigAuth expects the keyfiles to be unencrypted (i.e. no passphrase).

$ ssh-keygen -t rsa <enter>

Enter file in which to save the key: <type filename, enter>

Enter passphrase (empty for no passphrase): <enter>
Enter same passphrase again: <enter>
Your identification has been saved in <filename>.
Your public key has been saved in <filename>.pub.

Client Request Signing

This client is using HTTPotion, but any client library that allows specifying custom headers can be used (SigAuth provides headers as binary 2-tuples, e.g.: [{"authorization", "<authorization-token-stuff>"}, ...]).

priv_key = SigAuth.load_key("./test/testing_id_rsa")
nonce = System.system_time(:milliseconds)
username = "bob"
body = ""
path = "/api/users.27.json"
headers = SigAuth.sign("GET", path, nonce, body, username, priv_key)
# headers contains "authorization", and "x-sigauth-nonce" headers
HTTPotion.get("www.myapp.com" <> path, [headers: headers])

Server Request Validation

SigAuth provides the SigAuth.Plug module to streamline request validation and nonce maintenance. Here is an example usage, with a public, non-authenticated API endpoint followed by a private, authenticated endpoint.

defmodule MyApp.ApiRouter
  use Plug.Router

  plug :match
  plug :dispatch

  # Not Authenticated:
  forward "/public", to: MyApp.PublicApiRouter

  plug SigAuth.Plug, credential_server: MyApp.CredServer # See below for details

  # Authenticated:
  forward "/private", to: MyApp.PrivateApiRouter
  # ...

IMPORTANT NOTES

  • This plug must read the body of the request to verify the signature. This may well break your plug pipeline (Parsers, especially). Currently, the body is stored in conn.assigns[:body] after it is read. If you have an idea for a more elegant solution, feel free to provide a pull-request.

  • The username in the "authorization" header is stored for convenience in conn.assigns[:username]; this field can be used for user / role based authentication of individual endpoints; SigAuth has proven that the requestor possesses the private key associated with that username.

CredentialServer

Rather than owning the enrollment and key management problem, SigAuth offloads this work to you. The SigAuth.CredentialServer module specifies the method signatures that your credential server must expose for SigAuth.Plug to use it.

An example in-memory credential server is provided in SigAuth.ExampleCredentialServer.

Installation (Not yet)

{:sig_auth, "~> 0.1.0"},

Credits

I found the following links helpful in the construction of this application:

About

An HTTP API client authentication scheme based on RSA signatures

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages