ENH - add CityMarkt password authentication

Authenticate CityMarkt profiles with the linked iFruit account password, including server-side validation and rate limiting. Update the shared auth UI, localized copy, frontend state, browser mocks, and regression coverage for login and registration flows.
This commit is contained in:
smx.pusha
2026-08-18 07:31:55 +02:00
parent 0f39d6ab8a
commit 77c1790f8b
11 changed files with 811 additions and 279 deletions
+24
View File
@@ -47,4 +47,28 @@ describe('marketplace store offers', () => {
inquiryId: 'inquiry-id',
})
})
it('authenticates CityMarkt with the iFruit password and stores the profile', async () => {
const profile = {
avatar_media_id: null,
avatar_url: null,
bio: '',
display_name: 'demo',
email: 'demo@ifruit.com',
exists: true,
listing_count: 0,
}
mockNuiCall.mockResolvedValueOnce({ data: profile, success: true })
const marketplace = useMarketplaceStore()
const response = await marketplace.authenticate('register', 'secret12', 7)
expect(response).toEqual({ data: profile, success: true })
expect(mockNuiCall).toHaveBeenCalledWith('marketplace:auth', {
avatarMediaId: 7,
mode: 'register',
password: 'secret12',
})
expect(marketplace.profile).toEqual(profile)
})
})
+13
View File
@@ -28,6 +28,19 @@ export const useMarketplaceStore = defineStore('marketplace', {
profile: null as MarketplaceProfile | null,
}),
actions: {
async authenticate(
mode: 'login' | 'register',
password: string,
avatarMediaId = 0,
): Promise<NuiResponse<MarketplaceProfile>> {
const response = await nuiCall<MarketplaceProfile>('marketplace:auth', {
avatarMediaId,
mode,
password,
})
if (response.success && response.data) this.profile = response.data
return response
},
async loadProfile(): Promise<boolean> {
const response = await nuiCall<MarketplaceProfile>('marketplace:profile')
if (response.success && response.data) this.profile = response.data
+19 -8
View File
@@ -3159,18 +3159,29 @@ const defaultLocales: LocaleTree = {
signInTitle: 'Sign in to Sky Cloud',
signInBody: 'Log in to your CityMarkt profile to sell, save or message.',
authEyebrow: 'CityMarkt account',
authTitle: 'Welcome to CityMarkt',
authBody:
'Your iFruit email is linked automatically. Use your CityMarkt username to continue.',
login: 'Login',
register: 'Register',
authUsername: 'Username',
authLoginTitle: 'Welcome back',
authRegisterTitle: 'Create your profile',
authLoginBody: 'Sign in with the iFruit account linked to this phone.',
authRegisterBody:
'Confirm your linked iFruit account to create a CityMarkt profile.',
login: 'Sign in',
register: 'Create profile',
authPassword: 'iFruit password',
authPasswordPlaceholder: 'Enter your iFruit password',
authPasswordHelp: 'Use 664 characters.',
authConfirmPassword: 'Repeat password',
authConfirmPlaceholder: 'Repeat your iFruit password',
passwordsMismatch: 'The passwords do not match.',
authErrors: {
no_ifruit_account: 'Connect a Sky Cloud account in Settings first.',
invalid_username: 'Enter the username of your CityMarkt profile.',
invalid_password: 'Password must be 664 characters.',
invalid_credentials: 'The iFruit email or password is incorrect.',
invalid_profile_image: 'Choose a valid photo from this phone.',
profile_not_found: 'No CityMarkt profile exists for this iFruit email.',
profile_exists:
'A CityMarkt profile already exists. Use Login instead.',
'A CityMarkt profile already exists. Use Sign in instead.',
rate_limited: 'Too many attempts. Try again in a minute.',
default: 'CityMarkt authentication failed.',
},
noMessages: 'No conversations',
noMessagesBody: 'Messages about offers will appear here.',