feat(auth): login por credenciais (PASSWORD) na tela de Login
UILoginScreen_Base agora envia uint8(PASSWORD) + string(user) +
string(pass) no C_CHAR_AUTH_REQUEST (em vez de string(token dev:N)).
Servidor valida via AccountService.login (Argon2id + lockout) — contas
são criadas com 'npm run account' no ZeusCharServer.
CharServerOpcodes.h espelha do servidor:
- ZMMOCharAuthMethod::{TOKEN=0, PASSWORD=1}
- ZMMOCharRejectReason::{InvalidToken, AccountBanned, AlreadyOnline,
InvalidCredentials=4, AccountLocked=5, AccountSuspended=6,
CredentialsAuthDisabled=7}
S_CHAR_AUTH_REJECT mapeia por switch:
- 4 → InvalidCredentialsText (Usuário ou senha inválidos.)
- 5 → AccountLockedText (Conta bloqueada por excesso de tentativas.)
- 6 → AccountSuspendedText (Conta suspensa.)
- 7 → CredentialsDisabledText (Auth indisponível no servidor.)
- outros → RejectedText + código
MakeDevToken removido (StubTokenValidator continua aceitando dev:N
no servidor para testes diretos via outro cliente).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
46
Source/ZMMO/Game/UI/FrontEnd/CharServerOpcodes.h
Normal file
46
Source/ZMMO/Game/UI/FrontEnd/CharServerOpcodes.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
|
||||
/**
|
||||
* Opcodes do CharServer (WebSocket) consumidos pelo cliente. Espelha
|
||||
* `Server/ZeusCharServer/src/protocol/CharOpcodes.ts` (faixa 2000-2099).
|
||||
* Mantido mínimo: só os opcodes que o front-end usa hoje (auth da Login).
|
||||
* Ampliar conforme as telas (lista/criação/seleção de personagem) chegarem.
|
||||
*
|
||||
* Wire: string = uint16 LE (len) + UTF-8; inteiros LE. O cabeçalho de 32B
|
||||
* é responsabilidade do UZeusCharServerSubsystem (Send/OnRawMessage operam
|
||||
* sobre o payload já sem header).
|
||||
*/
|
||||
namespace ZMMOCharOp
|
||||
{
|
||||
// Autenticação — payload começa com uint8 method (ver ZMMOCharAuthMethod)
|
||||
constexpr int32 C_CHAR_AUTH_REQUEST = 2000;
|
||||
constexpr int32 S_CHAR_AUTH_OK = 2001; // string(accountId)+string(user)+uint64(expiresMs)
|
||||
constexpr int32 S_CHAR_AUTH_REJECT = 2002; // uint16(reason)
|
||||
}
|
||||
|
||||
/**
|
||||
* Método de autenticação enviado como uint8 prefix no payload do
|
||||
* C_CHAR_AUTH_REQUEST. TOKEN mantém o fluxo Stub/JWT (handoff de outro
|
||||
* serviço); PASSWORD chama AccountService.login direto no CharServer.
|
||||
* Espelha `CharAuthMethod` em CharOpcodes.ts.
|
||||
*/
|
||||
namespace ZMMOCharAuthMethod
|
||||
{
|
||||
constexpr uint8 TOKEN = 0;
|
||||
constexpr uint8 PASSWORD = 1;
|
||||
}
|
||||
|
||||
/** Espelha `CharRejectReason` em CharOpcodes.ts (subset usado no front-end). */
|
||||
namespace ZMMOCharRejectReason
|
||||
{
|
||||
constexpr uint16 Unknown = 0;
|
||||
constexpr uint16 InvalidToken = 1;
|
||||
constexpr uint16 AccountBanned = 2;
|
||||
constexpr uint16 AlreadyOnline = 3;
|
||||
constexpr uint16 InvalidCredentials = 4;
|
||||
constexpr uint16 AccountLocked = 5;
|
||||
constexpr uint16 AccountSuspended = 6;
|
||||
constexpr uint16 CredentialsAuthDisabled = 7;
|
||||
}
|
||||
@@ -51,19 +51,6 @@ namespace
|
||||
return bHas ? DesignStyle : Fallback;
|
||||
}
|
||||
|
||||
// Token de DEV: o StubTokenValidator aceita `dev:<numero>` ou `dev:<uuid>`.
|
||||
// Um nome de usuário comum NÃO valida — então em dev mapeamos para
|
||||
// `dev:<numero>` (o número digitado, se houver; senão 1). Auth real
|
||||
// (HTTP /auth/login + JWT) entra em tarefa dedicada.
|
||||
FString MakeDevToken(const FString& User)
|
||||
{
|
||||
const FString Trimmed = User.TrimStartAndEnd();
|
||||
if (!Trimmed.IsEmpty() && Trimmed.IsNumeric())
|
||||
{
|
||||
return FString::Printf(TEXT("dev:%s"), *Trimmed);
|
||||
}
|
||||
return TEXT("dev:1");
|
||||
}
|
||||
}
|
||||
|
||||
void UUILoginScreen_Base::NativePreConstruct()
|
||||
@@ -210,12 +197,10 @@ void UUILoginScreen_Base::HandleLoginClicked()
|
||||
// padrão dos launchers comerciais).
|
||||
SaveOrClearRememberedAccess(User);
|
||||
|
||||
// Escopo dev: token aceito pelo StubTokenValidator (senha ignorada no
|
||||
// stub; mantida na UI para o fluxo real futuro).
|
||||
const FString Token = MakeDevToken(User);
|
||||
|
||||
FZeusPacketWriter W;
|
||||
W.WriteStringUtf8(Token);
|
||||
W.WriteUInt8(ZMMOCharAuthMethod::PASSWORD);
|
||||
W.WriteStringUtf8(User.TrimStartAndEnd());
|
||||
W.WriteStringUtf8(Pass);
|
||||
|
||||
const bool bSent = Char->SendCharRequest(
|
||||
ZMMOCharOp::C_CHAR_AUTH_REQUEST, W.GetBuffer());
|
||||
@@ -229,7 +214,7 @@ void UUILoginScreen_Base::HandleLoginClicked()
|
||||
bAwaitingAuth = true;
|
||||
SetBusy(true);
|
||||
SetStatus(AuthenticatingText);
|
||||
UE_LOG(LogZMMO, Log, TEXT("Login: C_CHAR_AUTH_REQUEST enviado (token dev)."));
|
||||
UE_LOG(LogZMMO, Log, TEXT("Login: C_CHAR_AUTH_REQUEST enviado (PASSWORD)."));
|
||||
}
|
||||
|
||||
void UUILoginScreen_Base::HandleBackClicked()
|
||||
@@ -278,9 +263,22 @@ void UUILoginScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray<uint8>
|
||||
R.ReadUInt16(Reason);
|
||||
UE_LOG(LogZMMO, Warning, TEXT("Login: S_CHAR_AUTH_REJECT (reason=%u)."), Reason);
|
||||
SetBusy(false);
|
||||
SetStatus(FText::Format(
|
||||
NSLOCTEXT("ZMMO", "LoginRejectFmt", "{0} (cód. {1})"),
|
||||
RejectedText, FText::AsNumber(Reason)));
|
||||
|
||||
FText Message;
|
||||
switch (Reason)
|
||||
{
|
||||
case ZMMOCharRejectReason::InvalidCredentials: Message = InvalidCredentialsText; break;
|
||||
case ZMMOCharRejectReason::AccountLocked: Message = AccountLockedText; break;
|
||||
case ZMMOCharRejectReason::AccountSuspended: Message = AccountSuspendedText; break;
|
||||
case ZMMOCharRejectReason::AccountBanned: Message = AccountSuspendedText; break;
|
||||
case ZMMOCharRejectReason::CredentialsAuthDisabled: Message = CredentialsDisabledText; break;
|
||||
default:
|
||||
Message = FText::Format(
|
||||
NSLOCTEXT("ZMMO", "LoginRejectFmt", "{0} (cód. {1})"),
|
||||
RejectedText, FText::AsNumber(Reason));
|
||||
break;
|
||||
}
|
||||
SetStatus(Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,11 +18,13 @@ class UZeusCharServerSubsystem;
|
||||
* UI_CheckBox_Master). Camada Abstract; o WBP concreto (WBP_Login) herda
|
||||
* DIRETO desta classe (ARQUITETURA.md §3.3).
|
||||
*
|
||||
* Auth (escopo atual — dev): ao clicar "Entrar", envia C_CHAR_AUTH_REQUEST
|
||||
* (opcode 2000) pelo WebSocket do UZeusCharServerSubsystem com um token
|
||||
* `dev:<n>` (StubTokenValidator aceita em dev; auth real virá em tarefa
|
||||
* dedicada). S_CHAR_AUTH_OK → Flow::RequestEnterServerSelect; reject →
|
||||
* mensagem de erro. Sem hard-ref de asset de tema (§5).
|
||||
* Auth: ao clicar "Entrar", envia C_CHAR_AUTH_REQUEST (opcode 2000) pelo
|
||||
* WebSocket do UZeusCharServerSubsystem como `uint8(PASSWORD) +
|
||||
* string(user) + string(password)`. CharServer chama AccountService.login
|
||||
* (Argon2id + lockout). S_CHAR_AUTH_OK → Flow::RequestEnterServerSelect;
|
||||
* reject (InvalidCredentials/AccountLocked/Suspended/etc.) → status
|
||||
* mapeado. Sem hard-ref de asset de tema (§5). Contas são criadas no
|
||||
* servidor via `npm run account`.
|
||||
*/
|
||||
UCLASS(Abstract, Blueprintable)
|
||||
class ZMMO_API UUILoginScreen_Base : public UUIActivatableScreen_Base
|
||||
@@ -51,6 +53,18 @@ protected:
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login")
|
||||
FText RejectedText = FText::FromString(TEXT("Acesso negado pelo Gateway."));
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login")
|
||||
FText InvalidCredentialsText = FText::FromString(TEXT("Usuário ou senha inválidos."));
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login")
|
||||
FText AccountLockedText = FText::FromString(TEXT("Conta bloqueada por excesso de tentativas. Aguarde e tente novamente."));
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login")
|
||||
FText AccountSuspendedText = FText::FromString(TEXT("Conta suspensa. Contate o suporte."));
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login")
|
||||
FText CredentialsDisabledText = FText::FromString(TEXT("Autenticação por credenciais indisponível no servidor."));
|
||||
|
||||
// ---- Widgets do WBP (nomes esperados) ----
|
||||
UPROPERTY(meta = (BindWidgetOptional))
|
||||
TObjectPtr<UBorder> Border_Bg;
|
||||
|
||||
Reference in New Issue
Block a user