config = config('restaurant-delivery.firebase'); $this->initializeFirebase(); } protected function initializeFirebase(): void { if (! $this->config['enabled']) { return; } try { $this->factory = (new Factory) ->withServiceAccount($this->config['credentials_path']); } catch (\Exception $e) { Log::error('Firebase Auth initialization failed', [ 'error' => $e->getMessage(), ]); } } public function getAuth(): ?Auth { if (! $this->auth && isset($this->factory)) { $this->auth = $this->factory->createAuth(); } return $this->auth; } /* |-------------------------------------------------------------------------- | Custom Token Generation |-------------------------------------------------------------------------- */ /** * Create a custom token for a user with custom claims * * @param string $uid User ID * @param array $claims Custom claims (role, permissions, etc.) * @return string|null JWT token */ public function createCustomToken(string $uid, array $claims = []): ?string { try { $auth = $this->getAuth(); if (! $auth) { return null; } $customToken = $auth->createCustomToken($uid, $claims); return $customToken->toString(); } catch (FirebaseException $e) { Log::error('Failed to create custom token', [ 'uid' => $uid, 'error' => $e->getMessage(), ]); return null; } } /** * Create a custom token for a rider with appropriate claims */ public function createRiderToken(string $riderId, array $additionalClaims = []): ?string { $claims = array_merge([ 'role' => 'rider', 'rider_id' => $riderId, 'can_update_location' => true, 'can_accept_deliveries' => true, ], $additionalClaims); return $this->createCustomToken($riderId, $claims); } /** * Create a custom token for a restaurant with appropriate claims */ public function createRestaurantToken(string $restaurantId, array $additionalClaims = []): ?string { $claims = array_merge([ 'role' => 'restaurant', 'restaurant_id' => $restaurantId, 'restaurant' => true, 'can_create_deliveries' => true, 'can_assign_riders' => true, ], $additionalClaims); return $this->createCustomToken($restaurantId, $claims); } /** * Create a custom token for a customer with appropriate claims */ public function createCustomerToken(string $customerId, array $additionalClaims = []): ?string { $claims = array_merge([ 'role' => 'customer', 'customer_id' => $customerId, 'can_track_deliveries' => true, 'can_rate_riders' => true, ], $additionalClaims); return $this->createCustomToken($customerId, $claims); } /** * Create a custom token for an admin with full permissions */ public function createAdminToken(string $adminId, array $additionalClaims = []): ?string { $claims = array_merge([ 'role' => 'admin', 'admin' => true, 'admin_id' => $adminId, 'full_access' => true, ], $additionalClaims); return $this->createCustomToken($adminId, $claims); } /* |-------------------------------------------------------------------------- | Token Verification |-------------------------------------------------------------------------- */ /** * Verify an ID token and return the decoded claims */ public function verifyIdToken(string $idToken): ?array { try { $auth = $this->getAuth(); if (! $auth) { return null; } $verifiedToken = $auth->verifyIdToken($idToken); return [ 'uid' => $verifiedToken->claims()->get('sub'), 'email' => $verifiedToken->claims()->get('email'), 'email_verified' => $verifiedToken->claims()->get('email_verified'), 'claims' => $verifiedToken->claims()->all(), ]; } catch (FailedToVerifyToken $e) { Log::warning('Failed to verify ID token', [ 'error' => $e->getMessage(), ]); return null; } catch (FirebaseException $e) { Log::error('Firebase error during token verification', [ 'error' => $e->getMessage(), ]); return null; } } /** * Check if token has specific role */ public function hasRole(string $idToken, string $role): bool { $decoded = $this->verifyIdToken($idToken); if (! $decoded) { return false; } return ($decoded['claims']['role'] ?? null) === $role; } /** * Check if token has admin access */ public function isAdmin(string $idToken): bool { $decoded = $this->verifyIdToken($idToken); if (! $decoded) { return false; } return ($decoded['claims']['admin'] ?? false) === true; } /* |-------------------------------------------------------------------------- | Custom Claims Management |-------------------------------------------------------------------------- */ /** * Set custom claims for a user */ public function setCustomClaims(string $uid, array $claims): bool { try { $auth = $this->getAuth(); if (! $auth) { return false; } $auth->setCustomUserClaims($uid, $claims); // Invalidate cached claims Cache::forget("firebase_claims_{$uid}"); return true; } catch (FirebaseException $e) { Log::error('Failed to set custom claims', [ 'uid' => $uid, 'error' => $e->getMessage(), ]); return false; } } /** * Get user's custom claims */ public function getCustomClaims(string $uid): array { // Try cache first $cached = Cache::get("firebase_claims_{$uid}"); if ($cached !== null) { return $cached; } try { $auth = $this->getAuth(); if (! $auth) { return []; } $user = $auth->getUser($uid); $claims = $user->customClaims ?? []; // Cache for 5 minutes Cache::put("firebase_claims_{$uid}", $claims, 300); return $claims; } catch (FirebaseException $e) { Log::error('Failed to get custom claims', [ 'uid' => $uid, 'error' => $e->getMessage(), ]); return []; } } /** * Add role to user */ public function addRole(string $uid, string $role): bool { $claims = $this->getCustomClaims($uid); $claims['role'] = $role; // Add role-specific flags switch ($role) { case 'admin': $claims['admin'] = true; break; case 'restaurant': $claims['restaurant'] = true; break; case 'rider': $claims['rider'] = true; break; case 'customer': $claims['customer'] = true; break; } return $this->setCustomClaims($uid, $claims); } /** * Remove role from user */ public function removeRole(string $uid, string $role): bool { $claims = $this->getCustomClaims($uid); if (isset($claims['role']) && $claims['role'] === $role) { unset($claims['role']); } // Remove role-specific flags unset($claims[$role]); return $this->setCustomClaims($uid, $claims); } /* |-------------------------------------------------------------------------- | User Management |-------------------------------------------------------------------------- */ /** * Create a new Firebase user */ public function createUser(array $properties): ?array { try { $auth = $this->getAuth(); if (! $auth) { return null; } $request = CreateRequest::new(); if (isset($properties['email'])) { $request = $request->withEmail($properties['email']); } if (isset($properties['password'])) { $request = $request->withPassword($properties['password']); } if (isset($properties['phone'])) { $request = $request->withPhoneNumber($properties['phone']); } if (isset($properties['display_name'])) { $request = $request->withDisplayName($properties['display_name']); } if (isset($properties['photo_url'])) { $request = $request->withPhotoUrl($properties['photo_url']); } if (isset($properties['disabled'])) { $request = $properties['disabled'] ? $request->markAsDisabled() : $request->markAsEnabled(); } if (isset($properties['email_verified'])) { $request = $properties['email_verified'] ? $request->markEmailAsVerified() : $request->markEmailAsUnverified(); } if (isset($properties['uid'])) { $request = $request->withUid($properties['uid']); } $user = $auth->createUser($request); return [ 'uid' => $user->uid, 'email' => $user->email, 'phone' => $user->phoneNumber, 'display_name' => $user->displayName, 'photo_url' => $user->photoUrl, 'disabled' => $user->disabled, 'email_verified' => $user->emailVerified, ]; } catch (FirebaseException $e) { Log::error('Failed to create Firebase user', [ 'error' => $e->getMessage(), ]); return null; } } /** * Get user by UID */ public function getUser(string $uid): ?array { try { $auth = $this->getAuth(); if (! $auth) { return null; } $user = $auth->getUser($uid); return [ 'uid' => $user->uid, 'email' => $user->email, 'phone' => $user->phoneNumber, 'display_name' => $user->displayName, 'photo_url' => $user->photoUrl, 'disabled' => $user->disabled, 'email_verified' => $user->emailVerified, 'custom_claims' => $user->customClaims, 'created_at' => $user->metadata->createdAt, 'last_login' => $user->metadata->lastLoginAt, ]; } catch (FirebaseException $e) { Log::error('Failed to get Firebase user', [ 'uid' => $uid, 'error' => $e->getMessage(), ]); return null; } } /** * Get user by email */ public function getUserByEmail(string $email): ?array { try { $auth = $this->getAuth(); if (! $auth) { return null; } $user = $auth->getUserByEmail($email); return [ 'uid' => $user->uid, 'email' => $user->email, 'phone' => $user->phoneNumber, 'display_name' => $user->displayName, 'disabled' => $user->disabled, ]; } catch (FirebaseException $e) { return null; } } /** * Update user properties */ public function updateUser(string $uid, array $properties): bool { try { $auth = $this->getAuth(); if (! $auth) { return false; } $auth->updateUser($uid, $properties); return true; } catch (FirebaseException $e) { Log::error('Failed to update Firebase user', [ 'uid' => $uid, 'error' => $e->getMessage(), ]); return false; } } /** * Disable a user */ public function disableUser(string $uid): bool { return $this->updateUser($uid, ['disabled' => true]); } /** * Enable a user */ public function enableUser(string $uid): bool { return $this->updateUser($uid, ['disabled' => false]); } /** * Delete a user */ public function deleteUser(string $uid): bool { try { $auth = $this->getAuth(); if (! $auth) { return false; } $auth->deleteUser($uid); // Clear cached claims Cache::forget("firebase_claims_{$uid}"); return true; } catch (FirebaseException $e) { Log::error('Failed to delete Firebase user', [ 'uid' => $uid, 'error' => $e->getMessage(), ]); return false; } } /* |-------------------------------------------------------------------------- | Password Management |-------------------------------------------------------------------------- */ /** * Generate password reset link */ public function generatePasswordResetLink(string $email): ?string { try { $auth = $this->getAuth(); if (! $auth) { return null; } return $auth->getPasswordResetLink($email); } catch (FirebaseException $e) { Log::error('Failed to generate password reset link', [ 'email' => $email, 'error' => $e->getMessage(), ]); return null; } } /** * Generate email verification link */ public function generateEmailVerificationLink(string $email): ?string { try { $auth = $this->getAuth(); if (! $auth) { return null; } return $auth->getEmailVerificationLink($email); } catch (FirebaseException $e) { Log::error('Failed to generate email verification link', [ 'email' => $email, 'error' => $e->getMessage(), ]); return null; } } /** * Revoke all refresh tokens for a user */ public function revokeRefreshTokens(string $uid): bool { try { $auth = $this->getAuth(); if (! $auth) { return false; } $auth->revokeRefreshTokens($uid); return true; } catch (FirebaseException $e) { Log::error('Failed to revoke refresh tokens', [ 'uid' => $uid, 'error' => $e->getMessage(), ]); return false; } } }