UB authentication schema #
Starting from UB@5.24 UB authentication schema V2 with improved security is introduced.
It's uses algo, similar to Digest with SHA-256 as defined in [RFC 7616]. Differences from Digest SHA-256:
- hardcoded qop="auth"
- hardcoded algorithm="SHA-256"
- nonceCount (nc) is not used (always=1)
UB Auth v2 #
- Stage1: request a nonce and realm
-> GET|POST /auth?AUTHTYPE=UB&userName=username&v=2
<- 200 OK
{ "version": 2, "nonce": "opaqueServerNonceValue", "realm": "authRealmValue", forDigestMD5: false, "connectionID": "opaque" }
- Stage2: obtain session secret
Client calculate random client nonce
cnonce
6 chars at last. JS example:
const cnonce = Math.trunc(1e10 * Math.random()).toString(16)
and nc
parameter (MUST increases for each reply of stage2 request)
let nc = 1
if (isRepeat) nc++
calculate a response using nonce
and realm
from stage1
+ user password and nc
HA1 = secretWord = SHA256(username.toLowerCase():realm:password)
HA2 = SHA256('POST:auth') = 'e0ec69137e388cfad12e23c8f967754d3f20d222e2a995aa26810d1e1a0047ad' // constant for compatibility with Digest SHA-256
response = SHA256(HA1:nonce:nc:cnonce:HA2)
result of SHA256 must be a hex16 lower-cased string
and send response to server
-> POST /auth?AUTHTYPE=UB&userName=username&v=2&s=2
Content-Type: application/json
{
"AUTHTYPE": "UB",
"realm": "realmValue",
"userName": "usernameValue",
"cnonce": "cnonceValue",
"nc": "ncValue"
"response": "responseValue",
"prefUData": {...} // optional preferred user data
}
server reply with sessionID
, sessionPrivateKey
and user data
<- 200 Ok
{
"sessionID": "sessionIdValue",
"sessionPrivateKey": "sessionPrivateKeyValue",
"logonname": "UserName",
"uData", "stringified user data JSON",
"secondFactor": false // optional, if true - see https://unitybase.info/api/server-v5/tutorial-security.html#2fa
}
or with error message
<- 500 Internal Server Error
{ "success":false, "errCode":0, "errMsg":"<<<ubErrElsInvalidUserOrPwd>>>" }
UB Auth v1 (deprecated) #
Actually this is modified DIGEST schema with SHA256 hash algorithm and modified authorization mechanism.
In case of this schema usage UnityBase store client passwords hash in upasswordHashHexa
attribute of uba_user
entity.
Schema is protected from MIT type of attack and secure enough for most type of application.
On the UI client enter a userName
& password
, after this client must send a two request:
1) Request a nonce #
Client call auth
endpoint passing userName
as parameter.
-> GET|POST /auth?AUTHTYPE=UB&userName=admin
Server return a serverNonce
- one time public key valid for 5 minutes in a result field
<- 200 OK
{"result":"c0a94994a9baaf5f6f071a13c6b5f7c4f8219e1ba514d9f0b81df57cc4b4b81f"}
2) Sending hashed password and obtain a sessionWord
#
Client generate clientNonce
, calculate hash of his password
and call auth again, passing as parameters userName
, clientNonce
and password hashed with nonces
.
-> GET|POST /auth?AUTHTYPE=UB&
clientNonce=ffac6401331cce72e82ecfa8dd40c8cb4456098000392da2bac8c41d19b57467&
password=09561d07211a8ef1d125355bfb5e871028826484a30bde6c98562742d2e9460e
&userName=admin
here:
clientNonce = unique string client generate and memorize
secretWord = sha256('salt' + passwordWhatUserEnterDuringLogin)
password=sha256('/' + serverNonce + clientNonce + userName + secretWord)
Server return sessionPrivateKey
, used in future request as one of signature part.
<- 200 OK
{
"result":"537445910+634e82b0aa70c0ec67395d59935f1cec36c14cedc4cc824049e175109987d1c6",
"logonname":"admin",
"uData": {}
}
result
in response is a sessionPrivateKey
. First part of result before +
is clientSessionID
.
See UB authorization for authorization token calculation.
Consider what neither password, nor password hash not transferred other the wire, so the MIT attack is impossible.
JavaScript implementation:
var secretWord, sessionPrivateKey, hexa8ID;
promise = me.get('auth', {
params: {
AUTHTYPE: authSchema,
userName: authParams.login
}
}).then(function(resp){
var
serverNonce = resp.data.result,
SHA256 = CryptoJS.SHA256;
if (!serverNonce) throw new Error('invalid auth response');
var clientNonce = SHA256(new Date().toISOString().substr(0, 16)).toString();
var pwdHash = SHA256('salt' + authParams.password).toString();
secretWord = pwdHash;
return me.get('auth', {
params: {
AUTHTYPE: authSchema,
userName: authParams.login,
password: SHA256('/' + serverNonce + clientNonce + authParams.login + pwdHash).toString(),
clientNonce: clientNonce
}
});
}).then(function(response){
sessionPrivateKey = response.result;
hexa8ID = hexa8(sessionWord.split('+')[0]);
});