-
Notifications
You must be signed in to change notification settings - Fork 0
api auth
Trust is a difficult part in web communication. Both party need to be sure of:
- Who is speaking to.
- Validity of the content exchanged.
- Keep this insurance during the whole exchange.
This protocol try to respond to that problem.
- Salt = R( Timestamp / Lifetime ) * Lifetime
- Secret = K( Public_Key Private_Key )
- Token = K( Challenge Secret Salt )
- H(Data) = K( Nonce Data Secret )
- S(Data) = K( Token Data Secret )
- L(Data) = serialize(Data)
NOTE:
- S(X) is called the X signature.
- H(X) is called the X hmac.
- K(X) is the MD5 hash of X.
- R(X) is the next highest integer value by rounding up X — aka
ceil()
in some programming language.- L(X) is the serialization of the data, see above in documentation the serialization algorithm.
We use a Public_Key for tell who we are trying to authenticate.
We use use a Private_Key, Both are use to create a strong Secret.
We use a Timestamp. Always useful to locate things in time.
You already known:
Timestamp = 1329866347
Public_Key = 3123059c1c816471780539f6b6b738dc
Private_Key = 59cc30ad02c25bb7a8757e20d03bd621
Key = 6fe74546dfa5d730ce5cdfc02fe19dd4
And you are able to calculate:
Secret = a9283746b094e03e17e4e584fc6a9d8a
To authenticate yourself, you have to resolve a Challenge provided by the server: Calculate an authentication Token. This token can be calculated only if you know a Secret. This secret is only known by you and the server.
So only you and the server can calculate that token. Tokens have a Lifetime, by creating a Salt with help of Timestamp and using it in the token generation, tokens have a limited usage in time.
Session is a string of your choice, we talk about it's utility when we start talking about nonce.
Call API auth.request:
>> Public_Key = 3123059c1c816471780539f6b6b738dc
>> Session = Authention Wiki Example
<< Lifetime = 300
<< Challenge = 2c07899ba4d1b28d70c75a767a0a38c0
And you are able to calculate:
Time = 1329866400
Token = 7ed52e0636229a210eea607f7fbf5f10
S(Challenge) = 7ba3d30b361a659fa135307aeaaa9502
Be able to calculate a token proves you know the shared secret, or, once that the token has been transmitted through the web, you're able to intercept this token.
To ensure each part is who he pretend to be, Signature helps.
By signing a content, you prove that you know the shared secret and that you know the authentication token.
So, before use the authentication token in its rôle. Both parts exchange a signature. The client signs the challenge, send it and in the reply, the server signs the nonce provided in the reply.
Each part can verify the signature that he receives and now is sure to talk with the good person.
Call API auth.token:
>> Challenge = 2c07899ba4d1b28d70c75a767a0a38c0
>> S(Challenge) = 7ba3d30b361a659fa135307aeaaa9502
<< Nonce = d41d8cd98f00b204e9800998ecf8427e
<< S(Nonce) = 303893e7d2e4f29b04f71798887d0720
During the communication, it is important that no one modifies or replays a message by using your token.
So, after the authentication, each message have a Hmac. This time, this is the message which is signed. Hmac ensure that the message is not modified during the communication and that's really you who sending the message.
Hmac is calculate with a Nonce. You get a new Nonce at each valid request in the server's reply. This Nonce protect against a replay attack.
This is here where act Session. Nonce by its function, make obviouly the request/response strean linear. It is impossible to paralelize request because you need the response nonce in the previous one for create the signature of the next one.
API offers to the client to use session and have multiple authentication token at a time. Each session have its own nonce.
Assume that req.method
and req.request
are respectively the method and request content of the request
hmac = H(req.method L(req.request))
Assume that rep.nonce
and rep.response
are respectively the nonce and response content of the response
hmac = H(rep.nonce L(rep.response))
Better than long explanation, here we provided a PHP example. This serialization is like json format.
function serialize(array $data = array())
{
$serial = '';
foreach($data as $key => $val)
{
if (!is_scalar($val))
{
$val = $this->serialize((array) $val);
}
if (is_bool($val))
{
$val = $val ? 'true' : 'false';
}
$serial .= sprintf('%s:%s,', $key, $val);
}
return sprintf('{%s}', $serial);
}
{
"method" : "photo.version",
"token" : "5f15896d48ad3b0b6c033d769753734a",
"hmac" : "fa10c4d7b02a13e27a2ae5a5bbe8130b",
"request" : ""
}
{
"status" : "valid",
"hmac" : "ef30c4d7b02a13e27a2ae5a5bbe0129f",
"nonce" : "8ad52e0636229a210eea607f7fbf542c",
"response" :
{
"version" : 0.0.1
}
}
When a token is too old. You can use the API auth.refresh.
This API method can be called whenever you want. It changes token and nonce authentication. To validate the new token and the nonce you have to calculate the new token, sign it, send the refresh request.
In reply, you receive a nonce signed by the new token. If the signature checking succeed, you knows that now the server have the same new token as you.
NOTE:
You don't have to always check if you're token is too old. When you send a request with a such token. You got in reply a error code
old_token
. Then you knows you should call nowauth.refresh
before send again you request.
When you uses query
method (GET
or POST
) for calling the API, you need to pass the authentication data as extra parameters. Here is an example:
GET http://example.com/api/get/auth.config.set/json/token:5f15896d48ad3b0b6c033d769753734a/hash:fa10c4d7b02a13e27a2ae5a5bbe8130b/?lifetime=30
A PHP API client can be found here: https://gist.github.com/1917812
Here a example of how use it:
<?php
require_once 'api_client.php'; // https://gist.github.com/1917812
class API_client extends Pixelpost_Api_Client
{
const API_URL = 'http://localhost/pp/api/';
const PUB_KEY = '3123059c1c816471780539f6b6b738dc';
const PRIV_KEY = '6fe74546dfa5d730ce5cdfc02fe19dd5';
}
$c = new api_client('session name');
$response = $c->request('auth.config.set', array('lifetime' => 30));
echo $response->message; // configuration updated
?>