Доделал сервер и документ для разработчиков по

Авторификациии и серверам

Всё работает
This commit is contained in:
AidarKC
2026-03-27 16:29:19 +03:00
parent 2f9cf2bff1
commit 51de9779e3
10 changed files with 326 additions and 109 deletions
@@ -20,7 +20,7 @@ import static org.junit.jupiter.api.Assertions.*;
* - и после завершения оставить в БД 3 активных сессии (S1,S2,S3)
*
* Протокол v2:
* - создание сессии: AuthChallenge -> CreateAuthSession (deviceKey подпись, + sessionPubKey)
* - создание сессии: AuthChallenge -> CreateAuthSession (deviceKey подпись, + deviceKey + sessionKey)
* - вход в сессию: SessionChallenge(sessionId) -> nonce, затем SessionLogin(sessionId,time,signature(sessionKey))
* - ListSessions и CloseActiveSession доступны только в AUTH_STATUS_USER (после SessionLogin)
*/
@@ -109,16 +109,15 @@ public class IT_02_Sessions {
String authNonce = JsonParsers.authNonce(nonceResp);
assertNotNull(authNonce, "authNonce must not be null for " + label);
// для тестов: sessionKey = deviceKey (в реале будет отдельный keypair)
String sessionPubKeyB64 = TestConfig.devicePublicKeyB64(login);
String sessionKey = TestConfig.sessionKey(login);
// storagePwd на клиенте (сохраняем, чтобы потом проверить, что сервер вернул именно его)
String storagePwd = TestConfig.fakeStoragePwd();
// шаг 2: CreateAuthSession (device подпись + sessionPubKey)
// шаг 2: CreateAuthSession (device подпись + deviceKey + sessionKey)
String createResp = ws.call(
"CreateAuthSession(" + label + ")",
JsonBuilders.createAuthSessionV2(login, authNonce, storagePwd, sessionPubKeyB64),
JsonBuilders.createAuthSessionV2(login, authNonce, storagePwd, sessionKey),
t
);
assertEquals(200, JsonParsers.status(createResp), "CreateAuthSession(" + label + ") must be 200");
@@ -128,10 +127,9 @@ public class IT_02_Sessions {
r.ok("Создана сессия " + label + ": sessionId=" + sid);
// для тестов используем devicePriv как sessionPriv
byte[] sessionPrivKey = TestConfig.getDevicePrivatKey(login);
byte[] sessionPrivKey = TestConfig.getSessionPrivatKey(login);
return new Session(sid, sessionPrivKey, storagePwd);
return new Session(sid, sessionKey, sessionPrivKey, storagePwd);
}
}
@@ -143,7 +141,7 @@ public class IT_02_Sessions {
assertNotNull(nonce, "SessionChallenge nonce must not be null");
// шаг 2: SessionLogin(sessionId, timeMs, signature(sessionKey, SESSION_LOGIN:...))
String loginResp = ws.call("SessionLogin " + label, JsonBuilders.sessionLogin(s.sessionId, nonce, s.sessionPrivKey), t);
String loginResp = ws.call("SessionLogin " + label, JsonBuilders.sessionLogin(s.sessionId, s.sessionKey, nonce, s.sessionPrivKey), t);
assertEquals(200, JsonParsers.status(loginResp), "SessionLogin must be 200");
String storagePwd = JsonParsers.storagePwd(loginResp);
@@ -153,5 +151,5 @@ public class IT_02_Sessions {
r.ok(label + ": SessionLogin OK, storagePwd verified");
}
private record Session(String sessionId, byte[] sessionPrivKey, String storagePwd) {}
}
private record Session(String sessionId, String sessionKey, byte[] sessionPrivKey, String storagePwd) {}
}
+5 -1
View File
@@ -119,6 +119,7 @@ public final class TestConfig {
// NEW: session pub b64 helper
public static String sessionPublicKeyB64(String login) { return Base64.getEncoder().encodeToString(getSessionPublicKey(login)); }
public static String sessionKey(String login) { return "ed25519/" + sessionPublicKeyB64(login); }
// ============ backward-compatible helpers for "user1" ============
public static String BCH_NAME() { return getBlockchainName(LOGIN()); }
@@ -143,6 +144,9 @@ public final class TestConfig {
public static String SESSION_PUBKEY_B64() { return sessionPublicKeyB64(LOGIN()); }
public static String SESSION2_PUBKEY_B64() { return sessionPublicKeyB64(LOGIN2()); }
public static String SESSION3_PUBKEY_B64() { return sessionPublicKeyB64(LOGIN3()); }
public static String SESSION_KEY() { return sessionKey(LOGIN()); }
public static String SESSION2_KEY() { return sessionKey(LOGIN2()); }
public static String SESSION3_KEY() { return sessionKey(LOGIN3()); }
// ============ misc ============
public static String fakeStoragePwd() {
@@ -154,4 +158,4 @@ public final class TestConfig {
if (v == null) throw new IllegalStateException("No key in " + mapName + " for login=" + login);
return v.clone();
}
}
}
@@ -104,20 +104,15 @@ public final class JsonBuilders {
}
// ---------------- CreateAuthSession (v2) ----------------
// v2: sessionKey генерируется/хранится на клиенте, на сервер отправляем sessionPubKeyB64 (base64).
//
// ВАЖНО (новое правило):
// Подпись CreateAuthSession делается ТОЛЬКО deviceKey над строкой:
// preimage = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce
//
// storagePwd и sessionPubKeyB64 НЕ входят в preimage.
// Подпись CreateAuthSession делается deviceKey над строкой:
// preimage = "AUTH_CREATE_SESSION:" + login + ":" + sessionKey + ":" + storagePwd + ":" + timeMs + ":" + authNonce
public static String createAuthSessionV2(String login, String authNonce, String storagePwd, String sessionPubKeyB64) {
public static String createAuthSessionV2(String login, String authNonce, String storagePwd, String sessionKey) {
long timeMs = System.currentTimeMillis();
// подпись делаем devicePrivKey
byte[] devicePriv = TestConfig.getDevicePrivatKey(login);
String sigB64 = signAuthCreateSession(login, timeMs, authNonce, devicePriv);
String deviceKey = TestConfig.devicePublicKeyB64(login);
String sigB64 = signAuthCreateSession(login, sessionKey, storagePwd, timeMs, authNonce, devicePriv);
String requestId = TestIds.next("create");
return """
@@ -125,18 +120,24 @@ public final class JsonBuilders {
"op": "CreateAuthSession",
"requestId": "%s",
"payload": {
"login": "%s",
"storagePwd": "%s",
"sessionPubKeyB64": "%s",
"sessionKey": "%s",
"timeMs": %d,
"authNonce": "%s",
"deviceKey": "%s",
"signatureB64": "%s",
"clientInfo": "%s"
}
}
""".formatted(
requestId,
login,
storagePwd,
sessionPubKeyB64,
sessionKey,
timeMs,
authNonce,
deviceKey,
sigB64,
TestConfig.TEST_CLIENT_INFO
);
@@ -161,7 +162,7 @@ public final class JsonBuilders {
// Подпись SessionLogin по-прежнему делается sessionPrivKey:
// preimage = "SESSION_LOGIN:" + sessionId + ":" + timeMs + ":" + nonce
public static String sessionLogin(String sessionId, String nonce, byte[] sessionPrivKey) {
public static String sessionLogin(String sessionId, String sessionKey, String nonce, byte[] sessionPrivKey) {
long timeMs = System.currentTimeMillis();
String sigB64 = signSessionLogin(sessionId, timeMs, nonce, sessionPrivKey);
@@ -172,12 +173,13 @@ public final class JsonBuilders {
"requestId": "%s",
"payload": {
"sessionId": "%s",
"sessionKey": "%s",
"timeMs": %d,
"signatureB64": "%s",
"clientInfo": "%s"
}
}
""".formatted(requestId, sessionId, timeMs, sigB64, TestConfig.TEST_CLIENT_INFO);
""".formatted(requestId, sessionId, sessionKey, timeMs, sigB64, TestConfig.TEST_CLIENT_INFO);
}
// ---------------- ListSessions ----------------
@@ -226,11 +228,11 @@ public final class JsonBuilders {
/**
* Подпись CreateAuthSession(v2):
* preimage = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce
* preimage = "AUTH_CREATE_SESSION:" + login + ":" + sessionKey + ":" + storagePwd + ":" + timeMs + ":" + authNonce
* подписываем devicePrivKey.
*/
public static String signAuthCreateSession(String login, long timeMs, String authNonce, byte[] devicePrivKey) {
String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce;
public static String signAuthCreateSession(String login, String sessionKey, String storagePwd, long timeMs, String authNonce, byte[] devicePrivKey) {
String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + sessionKey + ":" + storagePwd + ":" + timeMs + ":" + authNonce;
byte[] preimage = preimageStr.getBytes(StandardCharsets.UTF_8);
byte[] sig = Ed25519Util.sign(preimage, devicePrivKey);
return Base64.getEncoder().encodeToString(sig);
@@ -247,4 +249,4 @@ public final class JsonBuilders {
byte[] sig = Ed25519Util.sign(preimage, sessionPrivKey);
return Base64.getEncoder().encodeToString(sig);
}
}
}