references/client_sdk_android.md
# Firebase Authentication on Android (Kotlin)
This guide walks you through using Firebase Authentication in your Android app
using Kotlin DSL (`build.gradle.kts`) and Kotlin code.
### 1, Enable Authentication via CLI
Before adding dependencies in your app, make sure you enable the Auth service in
your Firebase Project using the Firebase CLI:
```bash
npx -y firebase-tools@latest init auth
```
______________________________________________________________________
### 2. Add Dependencies
In your module-level `build.gradle.kts` (usually `app/build.gradle.kts`), add
the dependency for Firebase Authentication:
```kotlin
dependencies {
// [AGENT] Fetch the latest available BoM version from https://firebase.google.com/support/release-notes/android before adding this
implementation(platform("com.google.firebase:firebase-bom:<latest_bom_version>"))
// Add the dependency for the Firebase Authentication library
// When using the BoM, you don't specify versions in Firebase library dependencies
implementation("com.google.firebase:firebase-auth")
}
```
______________________________________________________________________
### 3. Initialize FirebaseAuth
In your Activity or Fragment, initialize the `FirebaseAuth` instance:
```kotlin
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
class MainActivity : AppCompatActivity() {
private lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val auth = Firebase.auth
setContent {
MaterialTheme {
Text("Auth initialized!")
}
}
}
}
```
#### Jetpack Compose (Modern)
Initialize inside a `ComponentActivity` using `setContent`:
```kotlin
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import com.google.firebase.Firebase
import com.google.firebase.auth.auth
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val auth = Firebase.auth
setContent {
MaterialTheme {
Text("Auth initialized!")
}
}
}
}
```
______________________________________________________________________
### 4. Check Current Auth State
You should check if a user is already signed in when your activity starts:
```kotlin
public override fun onStart() {
super.onStart()
// Check if user is signed in (non-null) and update UI accordingly.
val currentUser = auth.currentUser
if (currentUser != null) {
// User is signed in, navigate to main screen or update UI
} else {
// No user is signed in, prompt for login
}
}
```
______________________________________________________________________
### 5. Sign Up New Users (Email/Password)
Use `createUserWithEmailAndPassword` to register new users:
```kotlin
fun signUpUser(email: String, password: String) {
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign up success, update UI with the signed-in user's information
val user = auth.currentUser
// Navigate to main screen
} else {
// If sign up fails, display a message to the user.
Toast.makeText(baseContext, "Authentication failed.", Toast.LENGTH_SHORT).show()
}
}
}
```
______________________________________________________________________
### 6. Sign In Existing Users (Email/Password)
Use `signInWithEmailAndPassword` to log in existing users:
```kotlin
fun signInUser(email: String, password: String) {
auth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
val user = auth.currentUser
// Navigate to main screen
} else {
// If sign in fails, display a message to the user.
Toast.makeText(baseContext, "Authentication failed.", Toast.LENGTH_SHORT).show()
}
}
}
```
______________________________________________________________________
### 7. Sign Out
To sign out a user, call `signOut()` on the `FirebaseAuth` instance:
```kotlin
auth.signOut()
// Navigate to login screen
```
references/client_sdk_web.md
# Firebase Authentication Web SDK
## Initialization
First, ensure you have initialized the Firebase App (see `firebase-basics`
skill). Then, initialize the Auth service:
```javascript
import { getAuth } from "firebase/auth";
import { app } from "./firebase"; // Your initialized Firebase App
const auth = getAuth(app);
export { auth };
```
## Connect to Emulator
If you are running the Authentication emulator (usually on port 9099), connect
to it immediately after initialization.
```javascript
import { getAuth, connectAuthEmulator } from "firebase/auth";
const auth = getAuth();
// Connect to emulator if running locally
if (location.hostname === "localhost") {
connectAuthEmulator(auth, "http://localhost:9099");
}
```
## Sign Up with Email/Password
```javascript
import { getAuth, createUserWithEmailAndPassword } from "firebase/auth";
const auth = getAuth();
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
const user = userCredential.user;
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ..
});
```
## Sign In with Google (Popup)
```javascript
import { getAuth, signInWithPopup, GoogleAuthProvider } from "firebase/auth";
const auth = getAuth();
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
// This gives you a Google Access Token. You can use it to access the Google API.
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
// ...
})
.catch((error) => {
// Handle Errors here.
const errorCode = error.code;
const errorMessage = error.message;
// ...
});
```
> [!IMPORTANT] **Troubleshooting `auth/unauthorized-domain`**: If the popup
> opens and immediately closes with error `[firebase_auth/unauthorized-domain]`,
> it means the domain hosting your app is not authorized for OAuth operations in
> your Firebase project.
>
> - **Fix**: Add your domain (e.g., `localhost` for local testing) to the
> Authorized Domains list in the Firebase Console (Authentication > Settings >
> Authorized domains) or in your `firebase.json` auth config.
> - **CRITICAL**: Do NOT include the protocol or port number when adding the
> domain (e.g., use `localhost`, NOT `http://localhost:9090`).
## Sign In with Facebook (Popup)
```javascript
import { getAuth, signInWithPopup, FacebookAuthProvider } from "firebase/auth";
const auth = getAuth();
const provider = new FacebookAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
// The signed-in user info.
const user = result.user;
// This gives you a Facebook Access Token. You can use it to access the Facebook API.
const credential = FacebookAuthProvider.credentialFromResult(result);
const accessToken = credential.accessToken;
})
.catch((error) => {
// Handle Errors here.
});
```
## Sign In with Apple (Popup)
```javascript
import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
const auth = getAuth();
const provider = new OAuthProvider('apple.com');
signInWithPopup(auth, provider)
.then((result) => {
const user = result.user;
// Apple credential
const credential = OAuthProvider.credentialFromResult(result);
const accessToken = credential.accessToken;
})
.catch((error) => {
// Handle Errors here.
});
```
## Sign In with Twitter (Popup)
```javascript
import { getAuth, signInWithPopup, TwitterAuthProvider } from "firebase/auth";
const auth = getAuth();
const provider = new TwitterAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
const user = result.user;
// Twitter credential
const credential = TwitterAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
const secret = credential.secret;
})
.catch((error) => {
// Handle Errors here.
});
```
## Sign In with GitHub (Popup)
```javascript
import { getAuth, signInWithPopup, GithubAuthProvider } from "firebase/auth";
const auth = getAuth();
const provider = new GithubAuthProvider();
signInWithPopup(auth, provider)
.then((result) => {
const user = result.user;
const credential = GithubAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
})
.catch((error) => {
// Handle Errors here.
});
```
## Sign In with Microsoft (Popup)
```javascript
import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
const auth = getAuth();
const provider = new OAuthProvider('microsoft.com');
signInWithPopup(auth, provider)
.then((result) => {
const user = result.user;
const credential = OAuthProvider.credentialFromResult(result);
const accessToken = credential.accessToken;
})
.catch((error) => {
// Handle Errors here.
});
```
## Sign In with Yahoo (Popup)
```javascript
import { getAuth, signInWithPopup, OAuthProvider } from "firebase/auth";
const auth = getAuth();
const provider = new OAuthProvider('yahoo.com');
signInWithPopup(auth, provider)
.then((result) => {
const user = result.user;
const credential = OAuthProvider.credentialFromResult(result);
const accessToken = credential.accessToken;
})
.catch((error) => {
// Handle Errors here.
});
```
## Sign In Anonymously
```javascript
import { getAuth, signInAnonymously } from "firebase/auth";
const auth = getAuth();
signInAnonymously(auth)
.then(() => {
// Signed in..
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
});
```
## Email Link Authentication
**1. Send Auth Link**
```javascript
import { getAuth, sendSignInLinkToEmail } from "firebase/auth";
const auth = getAuth();
const actionCodeSettings = {
// URL you want to redirect back to. The domain must be in the authorized domains list in Firebase Console.
url: 'https://www.example.com/finishSignUp?cartId=1234',
handleCodeInApp: true,
};
sendSignInLinkToEmail(auth, email, actionCodeSettings)
.then(() => {
// Save the email locally so you don't need to ask the user for it again
window.localStorage.setItem('emailForSignIn', email);
})
.catch((error) => {
// Error
});
```
**2. Complete Sign In (on landing page)**
```javascript
import { getAuth, isSignInWithEmailLink, signInWithEmailLink } from "firebase/auth";
const auth = getAuth();
if (isSignInWithEmailLink(auth, window.location.href)) {
let email = window.localStorage.getItem('emailForSignIn');
if (!email) {
email = window.prompt('Please provide your email for confirmation');
}
signInWithEmailLink(auth, email, window.location.href)
.then((result) => {
window.localStorage.removeItem('emailForSignIn');
// You can check result.user
})
.catch((error) => {
// Error
});
}
```
## Observe Auth State
Recommended way to get the current user. This listener triggers whenever the
user signs in or out.
```javascript
import { getAuth, onAuthStateChanged } from "firebase/auth";
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
const uid = user.uid;
// ...
} else {
// User is signed out
// ...
}
});
```
## Sign Out
```javascript
import { getAuth, signOut } from "firebase/auth";
const auth = getAuth();
signOut(auth).then(() => {
// Sign-out successful.
}).catch((error) => {
// An error happened.
});
```
references/flutter_setup.md
# Firebase Auth & Google Sign-In for Flutter
When integrating Firebase Authentication and Google Sign-In into Flutter apps
targeting cross-platform environments (like Mobile + Web), you must navigate
several breaking changes introduced in `google_sign_in` 7.x+ and some
platform-specific quirks.
## 1. `google_sign_in` 7.2.0 API Changes
- **Method Renamed**: The `signIn()` method is deprecated/removed and has been
replaced with `authenticate()`.
- **Token Separation**: The `GoogleSignInAuthentication` object no longer
packages both identity and authorization tokens together. Initial
authentication now only provides the `idToken`. If an `accessToken` is
required for Google APIs, you must explicitly request server authorization
separately.
## 2. Initialization & Web Hang/Crash Pitfalls
- **Initialization Requirement**: In 7.x, you must call
`await GoogleSignIn.instance.initialize();` globally before using the plugin.
- **Web Client ID Constraint**: On Flutter Web, if you call `initialize()`
without passing a `clientId` argument OR specifying the
`<meta name="google-signin-client_id" ... />` tag in `web/index.html`, the
Dart Web Debug Service (DWDS) and the app will throw an assertion error and
**hang infinitely**, resulting in a blank screen.
- **Common Workaround**: If you intend to use Firebase Auth's
`signInWithPopup(GoogleAuthProvider())` for the web, you can conditionally
skip the local `GoogleSignIn` package initialization entirely:
```dart
import 'package:flutter/foundation.dart' show kIsWeb;
if (!kIsWeb) {
await GoogleSignIn.instance.initialize();
}
```
## 3. Web Logout Crashes
- If you bypassed `GoogleSignIn` initialization on the web (as demonstrated
above), you cannot call its `signOut()` method later. Attempting to execute
`await GoogleSignIn.instance.signOut();` during the user's logout flow on the
Web platform evaluates against an uninitialized context or unsupported
environment, crashing the app.
- **Solution**: Conditionally separate the logout logic for Web to rely entirely
on `FirebaseAuth`:
```dart
if (!kIsWeb) {
await GoogleSignIn.instance.signOut();
}
await FirebaseAuth.instance.signOut();
```
## 4. Prototyping Workaround: Bypassing Firestore Composite Indices
*Note: This is a Firestore consideration frequently encountered while fetching
user-specific auth data.*
When querying data via `FirebaseFirestore.instance`, using
`.where('userId', isEqualTo: uid)` combined with a sort on a different field
like `.orderBy('createdAt', descending: true)` mandates a custom composite
index.
- **Quick Alternative**: During local development, you can avoid defining
indexes by pulling the data using only `.where()` and applying the `.sort()`
operation client-side on the resulting `List` in Dart.
## 5. Robust `AuthService` Boilerplate
Here is a comprehensive `AuthService` implementation that properly handles the
initialization and platform differences between Flutter Web and Mobile:
```dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:google_sign_in/google_sign_in.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
AuthService() {
if (!kIsWeb) {
GoogleSignIn.instance.initialize();
}
}
// Stream to listen to auth state changes
Stream<User?> get authStateChanges => _auth.authStateChanges();
// Get current user
User? get currentUser => _auth.currentUser;
// Google Sign-In
Future<UserCredential?> signInWithGoogle() async {
try {
if (kIsWeb) {
// Web uses popup to avoid DWDS hangs and manual client ID config
GoogleAuthProvider authProvider = GoogleAuthProvider();
return await _auth.signInWithPopup(authProvider);
} else {
// Mobile uses standard flow
final GoogleSignInAccount? googleUser = await GoogleSignIn.instance.authenticate();
if (googleUser == null) return null; // Cancelled
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
idToken: googleAuth.idToken,
);
return await _auth.signInWithCredential(credential);
}
} catch (e) {
print("Error during Google Sign-In: \$e");
return null;
}
}
// Sign out
Future<void> signOut() async {
try {
if (!kIsWeb) {
await GoogleSignIn.instance.signOut();
}
await _auth.signOut();
} catch (e) {
print("Error signing out: \$e");
}
}
}
```
## 6. Troubleshooting `auth/unauthorized-domain` on Flutter Web
When running Flutter Web locally and using `signInWithPopup`, you might
encounter a situation where the Google Sign-In popup opens and immediately
closes.
- **Symptom**: The console shows
`Sign-in failed: [firebase_auth/unauthorized-domain] This domain is not authorized for OAuth operation for your Firebase project.`
- **Cause**: The domain (usually `localhost` during local testing) is not listed
in the Authorized Domains in the Firebase Console.
- **Solution**: Add `localhost` to the Authorized Domains list in the Firebase
Console (Authentication > Settings > Authorized domains) or in your
`firebase.json` auth config.
- **CRITICAL**: Do NOT include the protocol or port number when adding the
domain (e.g., use `localhost`, NOT `http://localhost:9090`). Flutter Web often
runs on random ports or specific ports, but Firebase Auth only cares about the
domain.
references/ios_setup.md
# Firebase Auth iOS Setup Guide
# ⛔️ CRITICAL RULE: NO INLINE INITIALIZATION ⛔️
NEVER write `let auth = Auth.auth()` as an inline class or struct property if
there is ANY chance the object is instantiated before `FirebaseApp.configure()`
executes in the app root.
- **FATAL CRASH:** `@Observable class AuthManager { let auth = Auth.auth() }`
initialized as a `@State` in the App root.
- **SAFE PATTERN:** Initialize `Auth.auth()` lazily
(`lazy var auth = Auth.auth()`) OR explicitly initialize the manager *after*
`FirebaseApp.configure()` finishes.
## 1. Import and Initialize
Ensure you have installed the `FirebaseAuth` SDK. Use the `xcode-project-setup`
skill to automate adding the SPM dependency to the Xcode project.
> **Note:** Ensure `FirebaseApp.configure()` has been executed in your app's
> entry point before calling any `Auth.auth()` methods, otherwise your app will
> crash. Do not initialize Auth objects in SwiftUI `@State` properties at the
> App root level.
```swift
import FirebaseAuth
```
## 2. Authentication State
To listen for authentication state changes (recommended way to check if a user
is signed in):
```swift
var handle: AuthStateDidChangeListenerHandle?
handle = Auth.auth().addStateDidChangeListener { auth, user in
if let user = user {
print("User is signed in with uid: \(user.uid)")
} else {
print("User is signed out")
}
}
// To remove the listener when no longer needed:
if let handle = handle {
Auth.auth().removeStateDidChangeListener(handle)
}
```
## 3. Email and Password Authentication (Modern Concurrency)
Modern Swift projects should prioritize `async/await` for authentication calls
to avoid nested completion handlers and improve readability.
### Sign Up
```swift
do {
let authResult = try await Auth.auth().createUser(withEmail: "user@example.com", password: "password")
print("User created successfully with uid: \(authResult.user.uid)")
} catch {
print("Error creating user: \(error.localizedDescription)")
}
```
### Sign In
```swift
do {
let authResult = try await Auth.auth().signIn(withEmail: "user@example.com", password: "password")
print("User signed in successfully with uid: \(authResult.user.uid)")
} catch {
print("Error signing in: \(error.localizedDescription)")
}
```
## 4. Sign Out
```swift
do {
try Auth.auth().signOut()
print("Successfully signed out")
} catch let signOutError as NSError {
print("Error signing out: \(signOutError)")
}
```
references/security_rules.md
# Authentication in Security Rules
Firebase Security Rules work with Firebase Authentication to provide rule-based
access control. For better advice on writing safe security rules, enable the
`firebase-firestore-basics` or `firebase-storage-basics` skills.
The `request.auth` variable contains authentication information for the user
requesting data.
## Basic Checks
### Check if user is signed in
```
allow read, write: if request.auth != null;
```
### Check if user owns the data
Access data only if the document ID matches the user's UID.
```
allow read, write: if request.auth != null && request.auth.uid == userId;
```
(Where `userId` is a path variable, e.g., `match /users/{userId}`)
### Check if user owns the document (field-based)
Access data only if the document has a `owner_uid` field matching the user's
UID.
```
allow read, write: if request.auth != null && request.auth.uid == resource.data.owner_uid;
```
## Token Properties
`request.auth.token` contains standard JWT claims and custom claims.
- `request.auth.token.email`: The user's email address.
- `request.auth.token.email_verified`: If the email is verified.
- `request.auth.token.name`: The user's display name.
### Example: Email Verification Check
```
allow create: if request.auth.token.email_verified == true;
```