返回 Skills
firebase/agent-skills· Apache-2.0 内容可用

firebase-auth-basics

Guide for setting up and using Firebase Authentication. Use this skill when the user's app requires user sign-in, user management, or secure data access using auth rules.

安装

与 skills.sh 相同的 Command / Prompt 安装方式


name: firebase-auth-basics description: Guide for setting up and using Firebase Authentication. Use this skill when the user's app requires user sign-in, user management, or secure data access using auth rules. compatibility: This skill is best used with the Firebase CLI, but does not require it. Firebase CLI can be accessed through npx -y firebase-tools@latest.

Prerequisites

  • Firebase Project: Created via npx -y firebase-tools@latest projects:create (see firebase-basics).
  • Firebase CLI: Installed and logged in (see firebase-basics).

Core Concepts

Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app.

Users

A user is an entity that can sign in to your app. Each user is identified by a unique ID (uid) which is guaranteed to be unique across all providers. User properties include:

  • uid: Unique identifier.
  • email: User's email address (if available).
  • displayName: User's display name (if available).
  • photoURL: URL to user's photo (if available).
  • emailVerified: Boolean indicating if the email is verified.

Identity Providers

Firebase Auth supports multiple ways to sign in:

  • Email/Password: Basic email and password authentication.
  • Federated Identity Providers: Google, Facebook, Twitter, GitHub, Microsoft, Apple, etc.
  • Phone Number: SMS-based authentication.
  • Anonymous: Temporary guest accounts that can be linked to permanent accounts later.
  • Custom Auth: Integrate with your existing auth system.

Google Sign In is recommended as a good and secure default provider.

Tokens

When a user signs in, they receive an ID Token (JWT). This token is used to identify the user when making requests to Firebase services (Realtime Database, Cloud Storage, Firestore) or your own backend.

  • ID Token: Short-lived (1 hour), verifies identity.
  • Refresh Token: Long-lived, used to get new ID tokens.

Workflow

1. Provisioning

Option 1. Enabling Authentication via CLI

Only Google Sign In, anonymous auth, and email/password auth can be enabled via CLI. For other providers, use the Firebase Console.

Configure Firebase Authentication in firebase.json by adding an 'auth' block:

{
  "auth": {
    "providers": {
      "anonymous": true,
      "emailPassword": true,
      "googleSignIn": {
        "oAuthBrandDisplayName": "Your Brand Name",
        "supportEmail": "support@example.com",
        "authorizedRedirectUris": ["https://example.com", "http://localhost"]
      }
    }
  }
}

[!NOTE] If the Google Sign-In popup opens and immediately closes with the error [firebase_auth/unauthorized-domain], it means the domain is not authorized. For local development, ensure localhost is included in the Authorized Domains list in the Firebase Console or via the authorizedDomains field in firebase.json. CRITICAL: Do NOT include the protocol or port number in the Authorized Domains list (e.g., use localhost, NOT http://localhost:9090).

CRITICAL: After configuring firebase.json, you MUST deploy the auth configuration to the Firebase backend for the changes to take effect. This is essential for auth providers like Google Sign-In, email/password, etc. to auto-generate the necessary OAuth clients for your app platforms. Run:

npx -y firebase-tools@latest deploy --only auth

Option 2. Enabling Authentication in Console

Enable other providers in the Firebase Console.

  1. Go to the https://console.firebase.google.com/project/_/authentication/providers
  2. Select your project.
  3. Enable the desired Sign-in providers (e.g., Email/Password, Google).

2. Client Setup & Usage

Web See references/client_sdk_web.md.

Flutter See references/flutter_setup.md. Android (Kotlin) See references/client_sdk_android.md.

3. Security Rules

Secure your data using request.auth in Firestore/Storage rules.

See references/security_rules.md.

附带文件

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;
```