Skip to content

Commit dbc83dd

Browse files
committed
docs(firestore-send-email): update oauth guide
1 parent 732a015 commit dbc83dd

File tree

3 files changed

+508
-250
lines changed

3 files changed

+508
-250
lines changed

firestore-send-email/PREINSTALL.md

Lines changed: 75 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ For more details, see the [SendGrid Categories documentation](https://docs.sendg
4343

4444
This section will help you set up OAuth2 authentication for the extension, using GCP (Gmail) as an example.
4545

46+
The extension is agnostic with respect to OAuth2 provider. You just need to provide it with valid Client ID, Client Secret, and Refresh Token parameters.
47+
4648
##### Step 1: Create OAuth Credentials in Google Cloud Platform
4749

4850
1. Go to the [Google Cloud Console](https://console.cloud.google.com/)
@@ -51,7 +53,7 @@ This section will help you set up OAuth2 authentication for the extension, using
5153
4. Click Create Credentials and select **OAuth client ID**
5254
5. Set the application type to **Web application**
5355
6. Give your OAuth client a name (e.g., "Firestore Send Email Extension")
54-
7. Under **Authorized redirect URIs**, add the URI where you'll receive the OAuth callback, for example `http://localhost:8080/oauth/callback`.
56+
7. Under **Authorized redirect URIs**, add the URI where you'll receive the OAuth callback, for example, `http://localhost:8080/oauth/callback`.
5557

5658
**Note**: The redirect URI in your OAuth client settings MUST match exactly the callback URL in your code.
5759

@@ -68,141 +70,89 @@ This section will help you set up OAuth2 authentication for the extension, using
6870
6971
##### Step 3: Generate a Refresh Token
7072

71-
You'll need to create a simple web application to generate a refresh token. In this subsection we illustrate how to do so with Node.js.
73+
You can use a standalone helper script (`oauth2-refresh-token-helper.js`) that generates a refresh token without requiring any npm installations.
7274

73-
Here's how to set it up:
75+
**Prerequisites:**
76+
- You must have Node.js installed on your machine
7477

75-
1. Create a new Node.js project:
78+
**Download the script:**
79+
1. Download the script using curl, wget, or directly from your browser:
7680
```bash
77-
mkdir oauth-helper
78-
cd oauth-helper
79-
npm init -y
80-
npm install express google-auth-library dotenv
81-
```
82-
83-
2. Create a `.env` file with your credentials:
84-
```
85-
CLIENT_ID=your_client_id
86-
CLIENT_SECRET=your_client_secret
81+
# Using curl
82+
curl -o oauth2-refresh-token-helper.js https://raw.githubusercontent.com/firebase/extensions/refs/heads/master/firestore-send-email/scripts/oauth2-refresh-token-helper.js
83+
84+
# Using wget
85+
wget https://raw.githubusercontent.com/firebase/extensions/refs/heads/master/firestore-send-email/scripts/oauth2-refresh-token-helper.js
8786
```
8887

89-
3. Create an application with:
90-
- A root route that redirects users to Google's OAuth consent page
91-
- A callback route that receives the authorization code and exchanges it for tokens
92-
(See the sample application included below)
88+
You can also [view the script on GitHub](https://github.com/firebase/extensions/blob/master/firestore-send-email/scripts/oauth2-refresh-token-helper.js) and download it manually.
89+
90+
> **Note**: If you're working in a Node.js environment where you can use npm packages, consider using the official google-auth-library instead:
91+
>
92+
> 1. Install the library: `npm install google-auth-library`
93+
> 2. Then use it like this:
94+
> ```javascript
95+
> import { OAuth2Client } from "google-auth-library";
96+
>
97+
> // Initialize OAuth client
98+
> const REDIRECT_URI = "http://localhost:8080/oauth/callback";
99+
> const oAuth2Client = new OAuth2Client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI);
100+
>
101+
> // Generate authorization URL
102+
> const authorizeUrl = oAuth2Client.generateAuthUrl({
103+
> access_type: "offline",
104+
> prompt: "consent",
105+
> scope: ["https://mail.google.com/"], // Full Gmail access
106+
> });
107+
>
108+
> // After receiving the code from the callback:
109+
> const { tokens } = await oAuth2Client.getToken(code);
110+
> const refreshToken = tokens.refresh_token;
111+
> ```
112+
>
113+
> This approach integrates better with other Google services and handles token refresh automatically when using the library for API calls.
114+
115+
2. Run the script with Node.js:
93116
94-
4. **Important**: The redirect URI in your code (e.g., `http://localhost:8080/oauth/callback`) **MUST** match exactly what you configured in the Google Cloud Console OAuth client settings.
95-
96-
5. In your application code:
97-
- Use the `generateAuthUrl()` method with `access_type: "offline"` and `prompt: "consent"` to request a refresh token
98-
- Set the appropriate scope, such as `["https://mail.google.com/"]` for Gmail access
99-
- Create a callback handler that exchanges the authorization code for tokens using `oAuth2Client.getToken(code)`
100-
101-
6. Run the application and access it in your browser:
102117
```bash
103-
node index.js
118+
node oauth2-refresh-token-helper.js
104119
```
105120
106-
7. Complete the OAuth flow:
107-
- Navigate to your application URL (e.g., `http://localhost:8080`)
108-
- Click the login button and authorize the application
109-
- After successful authorization, you'll receive a JSON response containing your tokens
110-
- Copy the `refresh_token` value for use in the extension configuration
111-
112-
113-
##### Sample Typescript Refresh Token App
114-
115-
```javascript
116-
const express = require("express");
117-
const { OAuth2Client } = require("google-auth-library");
118-
require("dotenv").config();
119-
120-
// Load environment variables
121-
const CLIENT_ID = process.env.CLIENT_ID;
122-
const CLIENT_SECRET = process.env.CLIENT_SECRET;
123-
124-
// Initialize express
125-
const app = express();
126-
127-
const REDIRECT_URI = "http://localhost:8080/oauth/callback";
128-
129-
// Initialize OAuth client
130-
const oAuth2Client = new OAuth2Client(CLIENT_ID, CLIENT_SECRET, REDIRECT_URI);
131-
132-
// Root route handler - immediately redirect to Google OAuth
133-
const rootHandler = (_req, res) => {
134-
const authorizeUrl = oAuth2Client.generateAuthUrl({
135-
access_type: "offline",
136-
prompt: "consent",
137-
scope: ["https://mail.google.com/"], // Full Gmail access
138-
});
121+
3. The script supports the following command-line options:
122+
```
123+
--port, -p Port to run the server on (default: 8080 or PORT env var)
124+
--id, -i Google OAuth Client ID
125+
--secret, -s Google OAuth Client Secret
126+
--output, -o Output file to save the refresh token (default: refresh_token.txt)
127+
--help, -h Show help information
128+
```
139129

140-
// Redirect user directly to Google OAuth consent page
141-
res.redirect(authorizeUrl);
142-
};
130+
4. You can either provide your credentials as command-line arguments or set them as environment variables:
131+
```bash
132+
# Using environment variables
133+
export CLIENT_ID=your_client_id
134+
export CLIENT_SECRET=your_client_secret
135+
node oauth2-refresh-token-helper.js
143136

144-
// OAuth callback handler
145-
const callbackHandler = async (req, res) => {
146-
const code = req.query.code;
137+
# Using command-line arguments
138+
node oauth2-refresh-token-helper.js --id=your_client_id --secret=your_client_secret
139+
```
147140

148-
if (!code) {
149-
res.status(400).send("No code provided");
150-
return;
151-
}
141+
5. The script will:
142+
- Start a local web server
143+
- Open your browser to the OAuth consent page
144+
- Receive the authorization code
145+
- Exchange the code for tokens
146+
- Save the refresh token to a file (default: `refresh_token.txt`)
147+
- Display the refresh token in your browser
152148

153-
try {
154-
console.log("Exchanging code for tokens...");
155-
const { tokens } = await oAuth2Client.getToken(code);
156-
157-
// Log all tokens for debugging
158-
console.log("\nToken response:");
159-
console.log(tokens);
160-
161-
// Check if refresh token exists
162-
if (!tokens.refresh_token) {
163-
return res.status(400).send("No refresh token received. Make sure you've set 'prompt: consent' and 'access_type: offline' in the authorization URL.");
164-
}
165-
166-
// Send the refresh token in a simple HTML page with just a paragraph
167-
res.send(`
168-
<!DOCTYPE html>
169-
<html>
170-
<head>
171-
<title>OAuth Refresh Token</title>
172-
<style>
173-
body { font-family: sans-serif; margin: 20px; }
174-
p { word-break: break-all; font-family: monospace; }
175-
</style>
176-
</head>
177-
<body>
178-
<h1>Your OAuth Refresh Token</h1>
179-
<p>${tokens.refresh_token}</p>
180-
</body>
181-
</html>
182-
`);
183-
} catch (error) {
184-
console.error(
185-
"Error exchanging code for tokens:",
186-
error instanceof Error ? error.message : "Unknown error"
187-
);
188-
res.status(500).send("Error getting tokens");
189-
}
190-
};
149+
6. **Important**: The redirect URI in the script (`http://localhost:8080/oauth/callback` by default) **MUST** match exactly what you configured in the Google Cloud Console OAuth client settings.
191150

192-
// Routes
193-
app.get("/", rootHandler);
194-
app.get("/oauth/callback", callbackHandler);
195-
196-
// Start server
197-
const PORT = 8080;
198-
app.listen(PORT, () => {
199-
console.log(`Server running at http://localhost:${PORT}`);
200-
});
201-
```
151+
7. The script automatically requests the appropriate scope for Gmail access (`https://mail.google.com/`) and sets the authorization parameters to always receive a refresh token (`access_type: "offline"` and `prompt: "consent"`).
202152

203153
##### Step 4: Configure the Firestore Send Email Extension
204154

205-
When installing the extension, select "OAuth2" as the Authentication Type and provide the following parameters:
155+
When installing the extension, select "OAuth2" as the **Authentication Type** and provide the following parameters:
206156

207157
- **OAuth2 SMTP Host**: `smtp.gmail.com` (for Gmail)
208158
- **OAuth2 SMTP Port**: `465` (for SMTPS) or `587` (for STARTTLS)
@@ -221,15 +171,15 @@ Leave `Use secure OAuth2 connection?` as the default value `true`.
221171
- **Testing Status**: If your OAuth consent screen is in "Testing" status, refresh tokens expire after 7 days unless User Type is set to "Internal"
222172
- **Solution**: Either publish your app or ensure User Type is set to "Internal" in the OAuth consent screen settings
223173

224-
###### Scope Issues
174+
###### No Refresh Token Received
225175

226-
- **Problem**: If you see authentication errors, you might not have the correct scopes
227-
- **Solution**: Ensure you've added `https://mail.google.com/` as a scope in both the OAuth consent screen and in the OAuth URL generation code
176+
- **Problem**: If you don't receive a refresh token during the OAuth flow
177+
- **Solution**: Make sure you've revoked previous access or forced consent by going to [Google Account Security](https://myaccount.google.com/security) > Third-party apps with account access
228178

229-
###### Access Denied
179+
###### Scope Issues
230180

231-
- **Problem**: "Access denied" errors when sending emails
232-
- **Solution**: Make sure the Gmail account has allowed less secure app access or that you've correctly set up OAuth2
181+
- **Problem**: If you see authentication errors, you might not have the correct scopes
182+
- **Solution**: Ensure you've added `https://mail.google.com/` as a scope in the OAuth consent screen
233183

234184
###### Additional Resources
235185

0 commit comments

Comments
 (0)