shadPS4/src/core/libraries/np/np_auth.cpp
georgemoralis 08168dc386
New config mode (part1 of 0.15.1 branch series) (#4145)
* using new emulator_settings

* the default user is now just player one

* transfer install, addon dirs

* fix load custom config issue

---------

Co-authored-by: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com>
2026-03-21 22:26:36 +02:00

387 lines
15 KiB
C++

// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <mutex>
#include "common/logging/log.h"
#include "core/emulator_settings.h"
#include "core/libraries/error_codes.h"
#include "core/libraries/libs.h"
#include "core/libraries/np/np_auth.h"
#include "core/libraries/np/np_auth_error.h"
#include "core/libraries/np/np_error.h"
#include "core/libraries/system/userservice.h"
namespace Libraries::Np::NpAuth {
static bool g_signed_in = false;
static s32 g_active_auth_requests = 0;
static std::mutex g_auth_request_mutex;
// Internal types for storing request-related information
enum class NpAuthRequestState {
None = 0,
Ready = 1,
Aborted = 2,
Complete = 3,
};
struct NpAuthRequest {
NpAuthRequestState state;
bool async;
s32 result;
};
static std::vector<NpAuthRequest> g_auth_requests;
s32 CreateNpAuthRequest(bool async) {
if (g_active_auth_requests == ORBIS_NP_AUTH_REQUEST_LIMIT) {
return ORBIS_NP_AUTH_ERROR_REQUEST_MAX;
}
std::scoped_lock lk{g_auth_request_mutex};
s32 req_index = 0;
while (req_index < g_auth_requests.size()) {
// Find first nonexistant request
if (g_auth_requests[req_index].state == NpAuthRequestState::None) {
// There is no request at this index, set the index to ready then break.
g_auth_requests[req_index].state = NpAuthRequestState::Ready;
g_auth_requests[req_index].async = async;
break;
}
req_index++;
}
if (req_index == g_auth_requests.size()) {
// There are no requests to replace.
NpAuthRequest new_request{NpAuthRequestState::Ready, async, 0};
g_auth_requests.emplace_back(new_request);
}
// Offset by one, first returned ID is 0x10000001
g_active_auth_requests++;
LOG_DEBUG(Lib_NpAuth, "called, async = {}", async);
return req_index + ORBIS_NP_AUTH_REQUEST_ID_OFFSET + 1;
}
s32 PS4_SYSV_ABI sceNpAuthCreateRequest() {
return CreateNpAuthRequest(false);
}
s32 PS4_SYSV_ABI sceNpAuthCreateAsyncRequest(const OrbisNpAuthCreateAsyncRequestParameter* param) {
if (param == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (param->size != sizeof(OrbisNpAuthCreateAsyncRequestParameter)) {
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
}
return CreateNpAuthRequest(true);
}
s32 GetAuthorizationCode(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameterA* param,
s32 flag, OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
if (param == nullptr || auth_code == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (param->size != sizeof(OrbisNpAuthGetAuthorizationCodeParameter)) {
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
}
if (param->user_id == -1 || param->client_id == nullptr || param->scope == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{g_auth_request_mutex};
// From here the actual authorization code request is performed.
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
auto& request = g_auth_requests[req_index];
if (request.state == NpAuthRequestState::Complete) {
request.result = ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
} else if (request.state == NpAuthRequestState::Aborted) {
request.result = ORBIS_NP_AUTH_ERROR_ABORTED;
return ORBIS_NP_AUTH_ERROR_ABORTED;
}
request.state = NpAuthRequestState::Complete;
if (!g_signed_in) {
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
// If the request is processed in some form, and it's an async request, then it returns OK.
if (request.async) {
return ORBIS_OK;
}
return ORBIS_NP_ERROR_SIGNED_OUT;
}
LOG_ERROR(Lib_NpAuth, "(STUBBED) called, req_id = {:#x}, async = {}", req_id, request.async);
// Not sure what values are expected here, so zeroing these for now.
std::memset(auth_code, 0, sizeof(OrbisNpAuthorizationCode));
if (issuer_id != nullptr) {
*issuer_id = 0;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceNpAuthGetAuthorizationCode(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameter* param,
OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
if (param == nullptr || auth_code == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (param->size != sizeof(OrbisNpAuthGetAuthorizationCodeParameter)) {
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
}
if (param->online_id == nullptr || param->client_id == nullptr || param->scope == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (!g_signed_in) {
// Calls sceNpManagerIntGetUserIdByOnlineId to get a user id, returning any errors.
// This call will not succeed while signed out because games cannot retrieve an online id.
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
// For simplicity, call sceUserServiceGetInitialUser to get the user_id.
s32 user_id = 0;
ASSERT(UserService::sceUserServiceGetInitialUser(&user_id) == 0);
// Library constructs an OrbisNpAuthGetAuthorizationCodeParameterA struct,
// then uses that to call GetAuthorizationCode.
OrbisNpAuthGetAuthorizationCodeParameterA internal_params;
std::memset(&internal_params, 0, sizeof(internal_params));
internal_params.size = sizeof(internal_params);
internal_params.client_id = param->client_id;
internal_params.user_id = user_id;
internal_params.scope = param->scope;
return GetAuthorizationCode(req_id, &internal_params, 0, auth_code, issuer_id);
}
s32 PS4_SYSV_ABI
sceNpAuthGetAuthorizationCodeA(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameterA* param,
OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
return GetAuthorizationCode(req_id, param, 0, auth_code, issuer_id);
}
s32 PS4_SYSV_ABI
sceNpAuthGetAuthorizationCodeV3(s32 req_id, const OrbisNpAuthGetAuthorizationCodeParameterA* param,
OrbisNpAuthorizationCode* auth_code, s32* issuer_id) {
return GetAuthorizationCode(req_id, param, 1, auth_code, issuer_id);
}
s32 GetIdToken(s32 req_id, const OrbisNpAuthGetIdTokenParameterA* param, s32 flag,
OrbisNpIdToken* token) {
if (param == nullptr || token == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (param->size != sizeof(OrbisNpAuthGetIdTokenParameterA)) {
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
}
if (param->user_id == -1 || param->client_id == nullptr || param->client_secret == nullptr ||
param->scope == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{g_auth_request_mutex};
// From here the actual authorization code request is performed.
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
auto& request = g_auth_requests[req_index];
if (request.state == NpAuthRequestState::Complete) {
request.result = ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
} else if (request.state == NpAuthRequestState::Aborted) {
request.result = ORBIS_NP_AUTH_ERROR_ABORTED;
return ORBIS_NP_AUTH_ERROR_ABORTED;
}
request.state = NpAuthRequestState::Complete;
if (!g_signed_in) {
request.result = ORBIS_NP_ERROR_SIGNED_OUT;
// If the request is processed in some form, and it's an async request, then it returns OK.
if (request.async) {
return ORBIS_OK;
}
return ORBIS_NP_ERROR_SIGNED_OUT;
}
LOG_ERROR(Lib_NpAuth, "(STUBBED) called, req_id = {:#x}, async = {}", req_id, request.async);
// Not sure what values are expected here, so zeroing this for now.
std::memset(token, 0, sizeof(OrbisNpIdToken));
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpAuthGetIdToken(s32 req_id, const OrbisNpAuthGetIdTokenParameter* param,
OrbisNpIdToken* token) {
if (param == nullptr || token == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (param->size != sizeof(OrbisNpAuthGetIdTokenParameter)) {
return ORBIS_NP_AUTH_ERROR_INVALID_SIZE;
}
if (param->online_id == nullptr || param->client_id == nullptr ||
param->client_secret == nullptr || param->scope == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
if (!g_signed_in) {
// Calls sceNpManagerIntGetUserIdByOnlineId to get a user id, returning any errors.
// This call will not succeed while signed out because games cannot retrieve an online id.
return ORBIS_NP_ERROR_USER_NOT_FOUND;
}
// For simplicity, call sceUserServiceGetInitialUser to get the user_id.
s32 user_id = 0;
ASSERT(UserService::sceUserServiceGetInitialUser(&user_id) == 0);
// Library constructs an OrbisNpAuthGetIdTokenParameterA struct,
// then uses that to call GetIdToken.
OrbisNpAuthGetIdTokenParameterA internal_params;
std::memset(&internal_params, 0, sizeof(internal_params));
internal_params.size = sizeof(internal_params);
internal_params.user_id = user_id;
internal_params.client_id = param->client_id;
internal_params.client_secret = param->client_secret;
internal_params.scope = param->scope;
return GetIdToken(req_id, &internal_params, 0, token);
}
s32 PS4_SYSV_ABI sceNpAuthGetIdTokenA(s32 req_id, const OrbisNpAuthGetIdTokenParameterA* param,
OrbisNpIdToken* token) {
return GetIdToken(req_id, param, 0, token);
}
s32 PS4_SYSV_ABI sceNpAuthGetIdTokenV3(s32 req_id, const OrbisNpAuthGetIdTokenParameterA* param,
OrbisNpIdToken* token) {
return GetIdToken(req_id, param, 1, token);
}
s32 PS4_SYSV_ABI sceNpAuthSetTimeout(s32 req_id, s32 resolve_retry, u32 resolve_timeout,
u32 conn_timeout, u32 send_timeout, u32 recv_timeout) {
LOG_ERROR(Lib_NpAuth, "(STUBBED) called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpAuthAbortRequest(s32 req_id) {
LOG_DEBUG(Lib_NpAuth, "called req_id = {:#x}", req_id);
std::scoped_lock lk{g_auth_request_mutex};
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
if (g_auth_requests[req_index].state == NpAuthRequestState::Complete) {
// If the request is already complete, abort is ignored.
return ORBIS_OK;
}
g_auth_requests[req_index].state = NpAuthRequestState::Aborted;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpAuthWaitAsync(s32 req_id, s32* result) {
if (result == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{g_auth_request_mutex};
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
if (!g_auth_requests[req_index].async ||
g_auth_requests[req_index].state == NpAuthRequestState::Ready) {
return ORBIS_NP_AUTH_ERROR_INVALID_ID;
}
// Since we're not actually performing any sort of network request here,
// we can just set result based on the request and return.
*result = g_auth_requests[req_index].result;
LOG_WARNING(Lib_NpAuth, "called req_id = {:#x}, returning result = {:#x}", req_id,
static_cast<u32>(*result));
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpAuthPollAsync(s32 req_id, s32* result) {
if (result == nullptr) {
return ORBIS_NP_AUTH_ERROR_INVALID_ARGUMENT;
}
std::scoped_lock lk{g_auth_request_mutex};
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
if (!g_auth_requests[req_index].async ||
g_auth_requests[req_index].state == NpAuthRequestState::Ready) {
return ORBIS_NP_AUTH_ERROR_INVALID_ID;
}
// Since we're not actually performing any sort of network request here,
// we can just set result based on the request and return.
*result = g_auth_requests[req_index].result;
LOG_WARNING(Lib_NpAuth, "called req_id = {:#x}, returning result = {:#x}", req_id,
static_cast<u32>(*result));
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceNpAuthDeleteRequest(s32 req_id) {
LOG_DEBUG(Lib_NpAuth, "called req_id = {:#x}", req_id);
std::scoped_lock lk{g_auth_request_mutex};
s32 req_index = req_id - ORBIS_NP_AUTH_REQUEST_ID_OFFSET - 1;
if (g_active_auth_requests == 0 || g_auth_requests.size() <= req_index ||
g_auth_requests[req_index].state == NpAuthRequestState::None) {
return ORBIS_NP_AUTH_ERROR_REQUEST_NOT_FOUND;
}
g_active_auth_requests--;
g_auth_requests[req_index].state = NpAuthRequestState::None;
return ORBIS_OK;
}
void RegisterLib(Core::Loader::SymbolsResolver* sym) {
g_signed_in = EmulatorSettings.IsPSNSignedIn();
LIB_FUNCTION("6bwFkosYRQg", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthCreateRequest);
LIB_FUNCTION("N+mr7GjTvr8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthCreateAsyncRequest);
LIB_FUNCTION("KxGkOrQJTqY", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetAuthorizationCode);
LIB_FUNCTION("qAUXQ9GdWp8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetAuthorizationCodeA);
LIB_FUNCTION("KI4dHLlTNl0", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetAuthorizationCodeV3);
LIB_FUNCTION("uaB-LoJqHis", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetIdToken);
LIB_FUNCTION("CocbHVIKPE8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetIdTokenA);
LIB_FUNCTION("RdsFVsgSpZY", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthGetIdTokenV3);
LIB_FUNCTION("PM3IZCw-7m0", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthSetTimeout);
LIB_FUNCTION("cE7wIsqXdZ8", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthAbortRequest);
LIB_FUNCTION("SK-S7daqJSE", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthWaitAsync);
LIB_FUNCTION("gjSyfzSsDcE", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthPollAsync);
LIB_FUNCTION("H8wG9Bk-nPc", "libSceNpAuth", 1, "libSceNpAuth", sceNpAuthDeleteRequest);
LIB_FUNCTION("KxGkOrQJTqY", "libSceNpAuthCompat", 1, "libSceNpAuth",
sceNpAuthGetAuthorizationCode);
LIB_FUNCTION("uaB-LoJqHis", "libSceNpAuthCompat", 1, "libSceNpAuth", sceNpAuthGetIdToken);
};
} // namespace Libraries::Np::NpAuth