1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . Diagnostics ;
4+ using System . IO ;
5+ using System . Net . Http ;
6+ using System . Net . Http . Json ;
7+ using System . Security . Cryptography ;
8+ using System . Text ;
9+ using System . Text . Json ;
10+ using System . Threading . Tasks ;
11+ using System . Net ;
12+ using Microsoft . AspNetCore . Http ;
13+ using Microsoft . AspNetCore . Mvc ;
14+ using Newtonsoft . Json ;
15+ using Roblox . Exceptions ;
16+ using Roblox . Logging ;
17+ using Roblox . Models . Users ;
18+ using Roblox . Services ;
19+ using Roblox . Services . App . FeatureFlags ;
20+ using Roblox . Website . Middleware ;
21+ using BadRequestException = Roblox . Exceptions . BadRequestException ;
22+ using MVC = Microsoft . AspNetCore . Mvc ;
23+ using Roblox . Services . Exceptions ;
24+ using Roblox . Website . WebsiteModels . Discord ;
25+ using Npgsql ;
26+ using Dapper ;
27+ using Roblox . Dto . Users ;
28+
29+
30+ namespace Roblox . Website . Controllers
31+ {
32+ [ MVC . ApiController ]
33+ [ MVC . Route ( "/" ) ]
34+ public class MobileShitTesting : ControllerBase
35+ {
36+ [ HttpGetBypass ( "/mobileapi/check-app-version" ) ]
37+ [ HttpPostBypass ( "/mobileapi/check-app-version" ) ]
38+ public MVC . IActionResult MobileCheckAppVer ( )
39+ {
40+ return Ok ( new
41+ {
42+ data = new
43+ {
44+ UpgradeAction = "None"
45+ }
46+ } ) ;
47+ }
48+
49+ [ HttpGetBypass ( "/device/initialize" ) ]
50+ [ HttpPostBypass ( "/device/initialize" ) ]
51+ public MVC . IActionResult InitDevice ( )
52+ {
53+ return Ok ( new
54+ {
55+ browserTrackerId = 1 ,
56+ appDeviceIdentifier = ( string ? ) null ,
57+ } ) ;
58+ }
59+
60+ private async Task RateLimitCheck ( )
61+ {
62+ var loginKey = "LoginAttemptCountV1:" + GetIP ( ) ;
63+ var attemptCount = ( await services . cooldown . GetBucketDataForKey ( loginKey , TimeSpan . FromMinutes ( 10 ) ) ) . ToArray ( ) ;
64+
65+ if ( ! await services . cooldown . TryIncrementBucketCooldown ( loginKey , 15 , TimeSpan . FromMinutes ( 10 ) , attemptCount , true ) )
66+ {
67+ throw new ForbiddenException ( 15 , "Too many attempts, please wait about 10 minutes before retrying!" ) ;
68+ }
69+ }
70+
71+ public class MobileLoginReq
72+ {
73+ public string username { get ; set ; }
74+ public string password { get ; set ; }
75+ }
76+
77+ [ HttpPostBypass ( "mobileapi/login" ) ]
78+ public async Task < dynamic > MobileLogin ( [ FromBody ] MobileLoginReq request )
79+ {
80+ FeatureFlags . FeatureCheck ( FeatureFlag . LoginEnabled ) ;
81+ await RateLimitCheck ( ) ;
82+
83+ if ( string . IsNullOrEmpty ( request . username ) || string . IsNullOrEmpty ( request . password ) )
84+ throw new BadRequestException ( 3 , "Username and Password are required. Please try again." ) ;
85+
86+ UserInfo userInfo ;
87+ try
88+ {
89+ userInfo = await services . users . GetUserByName ( request . username ) ;
90+ }
91+ catch ( RecordNotFoundException )
92+ {
93+ throw new ForbiddenException ( 1 , "Incorrect username or password. Please try again." ) ;
94+ }
95+
96+ if ( await Login ( request . username , request . password , userInfo . userId ) )
97+ await CreateSessionAndSetCookie ( userInfo . userId ) ;
98+
99+ var userBalance = await services . economy . GetUserBalance ( userInfo . userId ) ;
100+
101+ return new
102+ {
103+ Status = "OK" ,
104+ UserInfo = new
105+ {
106+ UserName = request . username ,
107+ RobuxBalance = userBalance . robux ,
108+ TicketsBalance = userBalance . tickets ,
109+ IsAnyBuildersClubMember = true ,
110+ ThumbnailUrl = $ "{ Configuration . BaseUrl } /Thumbs/Avatar.ashx?userId={ userInfo . userId } ",
111+ UserID = userInfo . userId
112+ }
113+ } ;
114+ }
115+
116+ private async Task < bool > Login ( string username , string password , long userId )
117+ {
118+ try
119+ {
120+ FeatureFlags . FeatureCheck ( FeatureFlag . LoginEnabled ) ;
121+ }
122+ catch ( RobloxException )
123+ {
124+ throw new RobloxException ( 503 , 0 , "Login is currently disabled. Please try again later." ) ;
125+ }
126+ await RateLimitCheck ( ) ;
127+ try
128+ {
129+ if ( ! await services . users . VerifyPassword ( userId , password ) )
130+ throw new ForbiddenException ( 1 , "Incorrect username or password. Please try again" ) ;
131+ }
132+ catch ( RecordNotFoundException )
133+ {
134+ throw new ForbiddenException ( 4 , "Your account has been locked. Please reset your password to unlock your account." ) ;
135+ }
136+
137+ return true ;
138+ }
139+
140+ private async Task < string > CreateSessionAndSetCookie ( long userId )
141+ {
142+ var sessionCookie = Middleware . SessionMiddleware . CreateJwt ( new Middleware . JwtEntry ( )
143+ {
144+ sessionId = await services . users . CreateSession ( userId ) ,
145+ createdAt = DateTimeOffset . Now . ToUnixTimeSeconds ( ) ,
146+ } ) ;
147+
148+ HttpContext . Response . Cookies . Append ( Middleware . SessionMiddleware . CookieName , sessionCookie , new CookieOptions ( )
149+ {
150+ Domain = ".{Configuration.BaseUrl}" ,
151+ Secure = false ,
152+ Expires = DateTimeOffset . Now . Add ( TimeSpan . FromDays ( 364 ) ) ,
153+ IsEssential = true ,
154+ Path = "/" ,
155+ SameSite = SameSiteMode . Lax ,
156+ } ) ;
157+ return sessionCookie ;
158+ }
159+ }
160+ }
0 commit comments