Skip to content

Commit

Permalink
Fixed the issue of registering new users on simulator
Browse files Browse the repository at this point in the history
  • Loading branch information
Shengle-Dai committed Nov 16, 2024
1 parent 4a48bfc commit 3b92623
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 14 deletions.
36 changes: 30 additions & 6 deletions game/lib/api/game_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -211,13 +211,14 @@ class ApiClient extends ChangeNotifier {
_createSocket(false);
return loginResponse;
} else {
print(loginResponse.body);
}
authenticated = false;
notifyListeners();
print("LoginResponse:" + loginResponse.body);

print("Failed to connect to server!");
return null;
authenticated = false;
notifyListeners();

print("Failed to connect to server!");
return null;
}
/*
}
print("Failed to get location data!");
Expand All @@ -243,4 +244,27 @@ class ApiClient extends ChangeNotifier {
authenticated = false;
notifyListeners();
}

Future<bool> checkUserExists(String idToken) async {
try {
final response = await http.get(_googleLoginUrl);

if (response.statusCode == 200) {
final responseData = jsonDecode(response.body);
if (responseData['exists'] == true) {
print('User exists in the database.');
return true;
} else {
print('User does not exist.');
return false;
}
} else {
print('Failed to check user. Status code: ${response.statusCode}');
return false;
}
} catch (e) {
print('Error occurred while checking user: $e');
return false;
}
}
}
4 changes: 3 additions & 1 deletion game/lib/api/geopoint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,16 @@ class GeoPoint {
return Future.error(
'Location permissions are permanently denied, we cannot request permissions.');
}
print("Getting location");
final pos = await Geolocator.getCurrentPosition(
// Ideally we would use best accuracy, but it doesn't work for some reason
// desiredAccuracy: LocationAccuracy.best
desiredAccuracy: LocationAccuracy.medium);
print("Got location: ${pos.latitude}, ${pos.longitude}");
return GeoPoint(pos.latitude, pos.longitude, pos.heading);
} catch (e) {
print(e);
return Future.error(e.toString());
return Future.error("Error:" + e.toString());
}
}

Expand Down
28 changes: 24 additions & 4 deletions game/lib/splash_page/splash_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,31 @@ class SplashPageWidget extends StatelessWidget {
return;
}

final gRelogResult =
await apiClient.connectGoogleNoRegister(account);
final auth = await account.authentication;
final idToken = auth.idToken;

if (gRelogResult != null) {
return;
bool userExists =
await apiClient.checkUserExists(idToken ?? "");
if (userExists) {
// User exists, proceed with the login process
print("User exists, connecting...");
final gRelogResult =
await client.connectGoogleNoRegister(account);

if (gRelogResult != null) {
print("Login successful, navigating to home...");
return;
}
} else {
// User does not exist, navigate to registration page
print(
"User does not exist, navigating to registration...");
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => RegisterPageWidget(
user: account, idToken: idToken),
),
);
}

Navigator.of(context).push(
Expand Down
36 changes: 35 additions & 1 deletion server/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ interface IntermediatePayload {
email: string;
}

/**
* The `AuthService` class provides authentication-related functionality for the application,
* including login, token management, and user verification.
*
* Now supports Google and Apple (OAuth), and device login. Only Cornell emails are allowed.
*/
@Injectable()
export class AuthService {
constructor(
Expand All @@ -37,6 +43,13 @@ export class AuthService {
secret: process.env.JWT_ACCESS_SECRET,
};

/**
* Verifies an Apple ID token and extracts the user's ID and email.
*
* @param idToken - The ID token from Apple Sign-In.
* @returns An object containing the user’s `id` and `email` if verification is successful;
* otherwise, `null` if verification fails.
*/
async payloadFromApple(idToken: string): Promise<IntermediatePayload | null> {
try {
const payload = await appleSignin.verifyIdToken(
Expand All @@ -54,6 +67,14 @@ export class AuthService {
}
}

/**
* Verifies a Google ID token based on the audience (platform) and extracts the user's ID and email.
*
* @param idToken - The ID token received from Google.
* @param aud - The platform that issued the token: 'android', 'ios', or 'web'.
* @returns An object containing the user's `id` and `email` if verification is successful;
* otherwise, `null` if verification fails.
*/
async payloadFromGoogle(
idToken: string,
aud: 'android' | 'ios' | 'web',
Expand Down Expand Up @@ -89,6 +110,14 @@ export class AuthService {
}
}

/**
* Authenticates a user based on the user credentials, issues access and refresh tokens.
*
* @param authType - The type of authentication used.
* @param req - The login DTO containing user credentials.
* @returns A tuple `[accessToken, refreshToken]` if login is successful;
* otherwise, `null` if authentication fails.
*/
async login(
authType: AuthType,
req: LoginDto,
Expand Down Expand Up @@ -141,10 +170,15 @@ export class AuthService {
idToken.id,
req.enrollmentType,
);

if (!user) {
console.log('Unable to register user');
return null;
}
}

// if user is null here, then it means we're not registering this user now (req.noRegister === true)
if (!user) {
console.log('Unable to register user');
return null;
}

Expand Down
30 changes: 28 additions & 2 deletions server/src/auth/google/google.controller.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Controller, Post, Body } from '@nestjs/common';
import { Controller, Post, Body, Get, Query } from '@nestjs/common';
import { AuthType } from '@prisma/client';
import { AuthService } from '../auth.service';
import { LoginDto } from '../login.dto';
import { UserService } from '../../user/user.service';

@Controller('google')
export class GoogleController {
constructor(private readonly authService: AuthService) {}
constructor(
private readonly authService: AuthService,
private readonly userService: UserService,
) {}

// login
@Post()
Expand All @@ -16,4 +20,26 @@ export class GoogleController {

return tokens && { accessToken: tokens[0], refreshToken: tokens[1] };
}

// check user existence
@Get('check-user')
async checkUser(
@Query('idToken') idToken: string,
): Promise<{ exists: boolean; user?: any }> {
const idTokenPayload = await this.authService.payloadFromGoogle(
idToken,
'web',
);

if (!idTokenPayload) {
console.log('Invalid token or unable to decode token');
return { exists: false };
}

const user = await this.userService.byAuth(
AuthType.GOOGLE,
idTokenPayload.id,
);
return user ? { exists: true, user } : { exists: false };
}
}
7 changes: 7 additions & 0 deletions server/src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,13 @@ export class UserService {
});
}

async checkIfUserExists(
authType: AuthType,
id: string,
): Promise<User | null> {
return this.byAuth(authType, id);
}

async dtoForUserData(user: User, partial: boolean): Promise<UserDto> {
const joinedUser = await this.prisma.user.findUniqueOrThrow({
where: { id: user.id },
Expand Down

0 comments on commit 3b92623

Please sign in to comment.