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;
|
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()
|
void UUILoginScreen_Base::NativePreConstruct()
|
||||||
@@ -210,12 +197,10 @@ void UUILoginScreen_Base::HandleLoginClicked()
|
|||||||
// padrão dos launchers comerciais).
|
// padrão dos launchers comerciais).
|
||||||
SaveOrClearRememberedAccess(User);
|
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;
|
FZeusPacketWriter W;
|
||||||
W.WriteStringUtf8(Token);
|
W.WriteUInt8(ZMMOCharAuthMethod::PASSWORD);
|
||||||
|
W.WriteStringUtf8(User.TrimStartAndEnd());
|
||||||
|
W.WriteStringUtf8(Pass);
|
||||||
|
|
||||||
const bool bSent = Char->SendCharRequest(
|
const bool bSent = Char->SendCharRequest(
|
||||||
ZMMOCharOp::C_CHAR_AUTH_REQUEST, W.GetBuffer());
|
ZMMOCharOp::C_CHAR_AUTH_REQUEST, W.GetBuffer());
|
||||||
@@ -229,7 +214,7 @@ void UUILoginScreen_Base::HandleLoginClicked()
|
|||||||
bAwaitingAuth = true;
|
bAwaitingAuth = true;
|
||||||
SetBusy(true);
|
SetBusy(true);
|
||||||
SetStatus(AuthenticatingText);
|
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()
|
void UUILoginScreen_Base::HandleBackClicked()
|
||||||
@@ -278,9 +263,22 @@ void UUILoginScreen_Base::HandleCharRawMessage(int32 Opcode, const TArray<uint8>
|
|||||||
R.ReadUInt16(Reason);
|
R.ReadUInt16(Reason);
|
||||||
UE_LOG(LogZMMO, Warning, TEXT("Login: S_CHAR_AUTH_REJECT (reason=%u)."), Reason);
|
UE_LOG(LogZMMO, Warning, TEXT("Login: S_CHAR_AUTH_REJECT (reason=%u)."), Reason);
|
||||||
SetBusy(false);
|
SetBusy(false);
|
||||||
SetStatus(FText::Format(
|
|
||||||
NSLOCTEXT("ZMMO", "LoginRejectFmt", "{0} (cód. {1})"),
|
FText Message;
|
||||||
RejectedText, FText::AsNumber(Reason)));
|
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
|
* UI_CheckBox_Master). Camada Abstract; o WBP concreto (WBP_Login) herda
|
||||||
* DIRETO desta classe (ARQUITETURA.md §3.3).
|
* DIRETO desta classe (ARQUITETURA.md §3.3).
|
||||||
*
|
*
|
||||||
* Auth (escopo atual — dev): ao clicar "Entrar", envia C_CHAR_AUTH_REQUEST
|
* Auth: ao clicar "Entrar", envia C_CHAR_AUTH_REQUEST (opcode 2000) pelo
|
||||||
* (opcode 2000) pelo WebSocket do UZeusCharServerSubsystem com um token
|
* WebSocket do UZeusCharServerSubsystem como `uint8(PASSWORD) +
|
||||||
* `dev:<n>` (StubTokenValidator aceita em dev; auth real virá em tarefa
|
* string(user) + string(password)`. CharServer chama AccountService.login
|
||||||
* dedicada). S_CHAR_AUTH_OK → Flow::RequestEnterServerSelect; reject →
|
* (Argon2id + lockout). S_CHAR_AUTH_OK → Flow::RequestEnterServerSelect;
|
||||||
* mensagem de erro. Sem hard-ref de asset de tema (§5).
|
* 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)
|
UCLASS(Abstract, Blueprintable)
|
||||||
class ZMMO_API UUILoginScreen_Base : public UUIActivatableScreen_Base
|
class ZMMO_API UUILoginScreen_Base : public UUIActivatableScreen_Base
|
||||||
@@ -51,6 +53,18 @@ protected:
|
|||||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login")
|
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Login")
|
||||||
FText RejectedText = FText::FromString(TEXT("Acesso negado pelo Gateway."));
|
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) ----
|
// ---- Widgets do WBP (nomes esperados) ----
|
||||||
UPROPERTY(meta = (BindWidgetOptional))
|
UPROPERTY(meta = (BindWidgetOptional))
|
||||||
TObjectPtr<UBorder> Border_Bg;
|
TObjectPtr<UBorder> Border_Bg;
|
||||||
|
|||||||
Reference in New Issue
Block a user