fix: Use correct device auth payload format for signature
The signature payload was incorrect. Changed from: $nonce:$signedAt To gateway's expected v2 format: v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce This matches the buildDeviceAuthPayload() function in Clawdbot's gateway/device-auth.js module.
This commit is contained in:
parent
1f58f36470
commit
a1e94f559f
|
|
@ -20,7 +20,7 @@ import java.security.SecureRandom
|
||||||
* The key format matches the Clawdbot gateway protocol:
|
* The key format matches the Clawdbot gateway protocol:
|
||||||
* - Public key: 32 bytes raw, base64url-encoded
|
* - Public key: 32 bytes raw, base64url-encoded
|
||||||
* - Signature: 64 bytes raw, base64url-encoded
|
* - Signature: 64 bytes raw, base64url-encoded
|
||||||
* - Payload: "$nonce:$signedAt" (matching gateway's expected format)
|
* - Payload format (v2): "v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce"
|
||||||
*/
|
*/
|
||||||
class DeviceIdentity(context: Context) {
|
class DeviceIdentity(context: Context) {
|
||||||
|
|
||||||
|
|
@ -68,13 +68,43 @@ class DeviceIdentity(context: Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sign a challenge nonce for gateway authentication
|
* Sign a challenge nonce for gateway authentication.
|
||||||
|
*
|
||||||
|
* Builds the payload in gateway's expected format:
|
||||||
|
* v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
|
||||||
|
*
|
||||||
|
* @param nonce Challenge nonce from gateway
|
||||||
|
* @param clientId Client identifier (e.g., "clawdnode-android")
|
||||||
|
* @param clientMode Client mode ("node")
|
||||||
|
* @param role Role ("node")
|
||||||
|
* @param scopes Comma-separated scopes (empty for nodes)
|
||||||
|
* @param token Auth token (gateway token or device token)
|
||||||
*/
|
*/
|
||||||
fun signChallenge(nonce: String): SignedChallenge {
|
fun signChallenge(
|
||||||
|
nonce: String,
|
||||||
|
clientId: String,
|
||||||
|
clientMode: String,
|
||||||
|
role: String,
|
||||||
|
scopes: String = "",
|
||||||
|
token: String = ""
|
||||||
|
): SignedChallenge {
|
||||||
ensureKeyPair()
|
ensureKeyPair()
|
||||||
|
|
||||||
val signedAt = System.currentTimeMillis()
|
val signedAt = System.currentTimeMillis()
|
||||||
val payload = "$nonce:$signedAt"
|
|
||||||
|
// Build payload in gateway's expected format:
|
||||||
|
// v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
|
||||||
|
val payload = listOf(
|
||||||
|
"v2",
|
||||||
|
deviceId,
|
||||||
|
clientId,
|
||||||
|
clientMode,
|
||||||
|
role,
|
||||||
|
scopes,
|
||||||
|
signedAt.toString(),
|
||||||
|
token,
|
||||||
|
nonce
|
||||||
|
).joinToString("|")
|
||||||
|
|
||||||
Log.d(tag, "Signing payload: $payload")
|
Log.d(tag, "Signing payload: $payload")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,16 @@ class GatewayClient(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
deviceId = deviceIdentity.deviceId
|
deviceId = deviceIdentity.deviceId
|
||||||
signedChallenge = deviceIdentity.signChallenge(nonce)
|
// Sign with full device auth payload:
|
||||||
|
// v2|deviceId|clientId|clientMode|role|scopes|signedAtMs|token|nonce
|
||||||
|
signedChallenge = deviceIdentity.signChallenge(
|
||||||
|
nonce = nonce,
|
||||||
|
clientId = Protocol.CLIENT_ID,
|
||||||
|
clientMode = Protocol.MODE,
|
||||||
|
role = Protocol.ROLE,
|
||||||
|
scopes = "", // Nodes don't use scopes
|
||||||
|
token = token
|
||||||
|
)
|
||||||
publicKey = deviceIdentity.publicKey
|
publicKey = deviceIdentity.publicKey
|
||||||
log("Device identity ready: id=${deviceId.take(8)}...")
|
log("Device identity ready: id=${deviceId.take(8)}...")
|
||||||
log("DEBUG: ${signedChallenge.debugInfo}")
|
log("DEBUG: ${signedChallenge.debugInfo}")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue