Google Contact Web Form
Creating a web page with Cloudflare Workers to handle form submissions and make HTTP requests to the Google Contacts API requires a few steps. Here's a basic outline of how you can achieve this:
- Set up a Cloudflare Workers environment.
- Create an HTML form on your web page.
- Write JavaScript code to handle form submission and make an HTTP request to the Google Contacts API.
- Deploy your Cloudflare Worker.
Let's go through each step in more detail:
1. Set up a Cloudflare Workers environment:
First, you need to set up a Cloudflare Workers environment. If you haven't already done this, you can sign up for a Cloudflare account and follow their documentation to set up Workers.
2. Create an HTML form:
Create an HTML form on your web page where users can input the contact information. Here's an example:
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create Google Contact</title>
</head>
<body>
<h1>Create Google Contact</h1>
<form id="contactForm">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br>
<button type="submit">Submit</button>
</form>
<script>
// JavaScript code to handle form submission
document.getElementById('contactForm').addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent default form submission
const formData = new FormData(this);
const requestData = {
name: formData.get('name'),
email: formData.get('email')
};
try {
const response = await fetch('/create-contact', {
method: 'POST',
body: JSON.stringify(requestData),
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) {
alert('Contact created successfully!');
} else {
alert('Failed to create contact. Please try again later.');
}
} catch (error) {
console.error('Error:', error);
alert('Failed to create contact. Please try again later.');
}
});
</script>
</body>
</html>
3. Write JavaScript code:
Write JavaScript code in your Cloudflare Worker to handle form submissions and make an HTTP request to the Google Contacts API. You'll need to obtain an access token to authenticate your requests to the Google Contacts API. This typically involves using OAuth 2.0. Here's an example of how you can make the request:
javascriptaddEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
if (request.method === 'POST' && request.url.endsWith('/create-contact')) {
const { name, email } = await request.json();
// Replace with your own Google Contacts API endpoint and access token
const apiUrl = 'https://www.google.com/m8/feeds/contacts/default/full';
const accessToken = 'YOUR_ACCESS_TOKEN';
const requestBody = `
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:gd="http://schemas.google.com/g/2005">
<category scheme="http://schemas.google.com/g/2005#kind"
term="http://schemas.google.com/contact/2008#contact"/>
<gd:name>
<gd:fullName>${name}</gd:fullName>
</gd:name>
<gd:email address="${email}" primary="true" />
</entry>
`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/atom+xml',
'Authorization': `Bearer ${accessToken}`
},
body: requestBody
});
if (response.ok) {
return new Response('Contact created successfully!', { status: 200 });
} else {
return new Response('Failed to create contact.', { status: 500 });
}
}
// Return a 404 for other requests
return new Response('Not Found', { status: 404 });
}
4. Deploy your Cloudflare Worker:
Once you have written your Cloudflare Worker code, deploy it through the Cloudflare Workers dashboard.
Remember to replace 'YOUR_ACCESS_TOKEN' with your actual access token obtained from Google. Also, ensure that your Cloudflare Worker has the necessary permissions to make outbound HTTP requests.
That's it! You now have a web page with a form that allows users to submit contact information, and a Cloudflare Worker that handles the form submission and creates a contact in your Google account using the Google Contacts API.
Certainly! Obtaining an access token for the Google Contacts API typically involves using OAuth 2.0 authentication. Here's a step-by-step guide to obtaining the access token:
Step 1: Create a Google Cloud Platform (GCP) Project:
- Go to the Google Cloud Console: https://console.cloud.google.com/.
- If you don't have a project yet, create a new project by clicking on the project dropdown menu at the top of the page and selecting "New Project". Follow the prompts to create the project.
Step 2: Enable the Google Contacts API:
- In the Google Cloud Console, navigate to the "APIs & Services" > "Library" section from the left sidebar menu.
- Search for "Google Contacts API" and click on it.
- Click the "Enable" button to enable the API for your project.
Step 3: Create OAuth 2.0 Credentials:
- Still in the Google Cloud Console, navigate to the "APIs & Services" > "Credentials" section from the left sidebar menu.
- Click on the "Create credentials" button and select "OAuth client ID" from the dropdown menu.
- Choose the application type as "Web application".
- Enter a name for your OAuth 2.0 client.
- Under "Authorized redirect URIs", add
https://developers.google.com/oauthplayground(you'll use this for testing). - Click the "Create" button. Google will generate your client ID and client secret.
Step 4: Obtain the Access Token:
- Go to the OAuth 2.0 Playground: https://developers.google.com/oauthplayground.
- In the settings (gear icon), make sure "Use your own OAuth credentials" is selected and enter your OAuth client ID and client secret obtained in the previous step.
- Scroll down and find "Google Contacts API v3". Click on it to expand and select the required scopes (e.g.,
https://www.google.com/m8/feeds/for read/write access to contacts). - Click the "Authorize APIs" button.
- You'll be prompted to sign in with your Google account and grant permission to the OAuth Playground.
- After authorization, you'll be redirected back to the OAuth Playground.
- Click the "Exchange authorization code for tokens" button. This will exchange the authorization code for an access token.
- You'll receive a response containing the access token.
Step 5: Use the Access Token:
Copy the access token you received and use it in your Cloudflare Worker code where it says 'YOUR_ACCESS_TOKEN'.
That's it! You now have an access token that allows your Cloudflare Worker to authenticate and access the Google Contacts API on behalf of your Google account. Make sure to keep your client ID, client secret, and access token secure.
If the access token expires, you will need to refresh it to continue making requests to the Google Contacts API. OAuth 2.0 provides a mechanism for refreshing access tokens without requiring the user to reauthenticate.
Here's how you can handle access token expiration and refresh it in your Cloudflare Worker:
Track Token Expiry: When you receive an access token, note its expiration time. Google typically includes an
expires_inparameter in the token response indicating the token's validity duration.Handle Expiry in the Worker: Before making a request to the Google Contacts API, check if the access token is expired. If it is expired, you'll need to refresh it.
Refresh Token: To refresh the access token, you need to send a request to Google's OAuth 2.0 token endpoint with your refresh token. Google will respond with a new access token.
Here's an updated version of your Cloudflare Worker code that includes token expiration handling and refreshing:
javascriptaddEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
let accessToken = '';
let tokenExpirationTime = 0;
async function getAccessToken() {
// Use your refresh token to obtain a new access token
const refreshToken = 'YOUR_REFRESH_TOKEN';
const tokenUrl = 'https://oauth2.googleapis.com/token';
const response = await fetch(tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
refresh_token: refreshToken
})
});
if (response.ok) {
const responseData = await response.json();
accessToken = responseData.access_token;
tokenExpirationTime = Date.now() + (responseData.expires_in * 1000);
return accessToken;
} else {
throw new Error('Failed to refresh access token');
}
}
async function handleRequest(request) {
if (tokenExpirationTime < Date.now()) {
// Access token has expired, refresh it
await getAccessToken();
}
if (request.method === 'POST' && request.url.endsWith('/create-contact')) {
const { name, email } = await request.json();
const apiUrl = 'https://www.google.com/m8/feeds/contacts/default/full';
const requestBody = `
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:gd="http://schemas.google.com/g/2005">
<category scheme="http://schemas.google.com/g/2005#kind"
term="http://schemas.google.com/contact/2008#contact"/>
<gd:name>
<gd:fullName>${name}</gd:fullName>
</gd:name>
<gd:email address="${email}" primary="true" />
</entry>
`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/atom+xml',
'Authorization': `Bearer ${accessToken}`
},
body: requestBody
});
if (response.ok) {
return new Response('Contact created successfully!', { status: 200 });
} else {
return new Response('Failed to create contact.', { status: 500 });
}
}
// Return a 404 for other requests
return new Response('Not Found', { status: 404 });
}
In this code:
getAccessToken()function is responsible for refreshing the access token using the refresh token.tokenExpirationTimekeeps track of when the access token will expire. If it's expired, the worker will automatically refresh it before making the request to the Google Contacts API.
Replace 'YOUR_REFRESH_TOKEN', 'YOUR_CLIENT_ID', and 'YOUR_CLIENT_SECRET' with your actual refresh token, client ID, and client secret respectively.
With this setup, your Cloudflare Worker will be able to automatically refresh the access token when it expires, ensuring seamless access to the Google Contacts API.
Thank you for bringing that to my attention. The Google Contacts API is indeed being deprecated, and developers are encouraged to migrate to the Google People API to retain programmatic access to Google Contacts.
To adapt your Cloudflare Worker to use the Google People API instead of the deprecated Google Contacts API, you'll need to make some adjustments to the API endpoints and request structure.
Here's an updated version of your Cloudflare Worker code that utilizes the Google People API:
javascriptaddEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
let accessToken = '';
let tokenExpirationTime = 0;
async function getAccessToken() {
// Use your refresh token to obtain a new access token
const refreshToken = 'YOUR_REFRESH_TOKEN';
const tokenUrl = 'https://oauth2.googleapis.com/token';
const response = await fetch(tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
refresh_token: refreshToken
})
});
if (response.ok) {
const responseData = await response.json();
accessToken = responseData.access_token;
tokenExpirationTime = Date.now() + (responseData.expires_in * 1000);
return accessToken;
} else {
throw new Error('Failed to refresh access token');
}
}
async function handleRequest(request) {
if (tokenExpirationTime < Date.now()) {
// Access token has expired, refresh it
await getAccessToken();
}
if (request.method === 'POST' && request.url.endsWith('/create-contact')) {
const { name, email } = await request.json();
const apiUrl = 'https://people.googleapis.com/v1/people:createContact';
const requestBody = {
names: [{ displayName: name }],
emailAddresses: [{ value: email }]
};
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
return new Response('Contact created successfully!', { status: 200 });
} else {
return new Response('Failed to create contact.', { status: 500 });
}
}
// Return a 404 for other requests
return new Response('Not Found', { status: 404 });
}
In this updated code:
- The Google People API endpoint
https://people.googleapis.com/v1/people:createContactis used to create a new contact. - The request body format is adjusted to match the requirements of the People API.
Replace 'YOUR_REFRESH_TOKEN', 'YOUR_CLIENT_ID', and 'YOUR_CLIENT_SECRET' with your actual refresh token, client ID, and client secret respectively.
With this adjustment, your Cloudflare Worker should now be able to create contacts using the Google People API without triggering the deprecation warning.
Certainly! To ensure that the HTML form submits JSON data, we can use JavaScript to intercept the form submission event, serialize the form data into JSON format, and then send it as a POST request. Here's the updated HTML page:
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create Google Contact</title>
</head>
<body>
<h1>Create Google Contact</h1>
<form id="contactForm">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br>
<button type="submit">Submit</button>
</form>
<script>
// JavaScript code to handle form submission
document.getElementById('contactForm').addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent default form submission
const formData = {
name: document.getElementById('name').value,
email: document.getElementById('email').value
};
try {
const response = await fetch('/create-contact', {
method: 'POST',
body: JSON.stringify(formData),
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) {
alert('Contact created successfully!');
} else {
alert('Failed to create contact. Please try again later.');
}
} catch (error) {
console.error('Error:', error);
alert('Failed to create contact. Please try again later.');
}
});
</script>
</body>
</html>
In this updated HTML page:
- JavaScript code intercepts the form submission event and prevents the default form submission behavior.
- Form data is collected directly from the form fields (
nameandemail) and stored in a JavaScript object (formData). - The form data is then serialized into JSON format using
JSON.stringify()and sent as the body of the POST request to/create-contactendpoint.
With this update, the HTML form will submit JSON data when the user fills out the form and clicks the submit button.
Sure, let's update the HTML form to include the fields you mentioned and make it look more visually appealing and responsive. We'll use some basic CSS to style the form with a blue color scheme. Here's the updated HTML along with some CSS:
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create Google Contact</title>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
.container {
background-color: #ffffff;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.1);
width: 100%;
max-width: 400px;
}
h1 {
text-align: center;
color: #4285f4;
}
label {
font-weight: bold;
color: #4285f4;
}
input[type="text"],
input[type="email"],
textarea {
width: 100%;
padding: 10px;
margin-top: 5px;
margin-bottom: 15px;
border: 1px solid #ccc;
border-radius: 5px;
box-sizing: border-box;
}
button[type="submit"] {
background-color: #4285f4;
color: white;
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
width: 100%;
}
button[type="submit"]:hover {
background-color: #357ae8;
}
</style>
</head>
<body>
<div class="container">
<h1>Create Google Contact</h1>
<form id="contactForm">
<label for="firstName">First Name:</label>
<input type="text" id="firstName" name="firstName" required>
<label for="lastName">Last Name:</label>
<input type="text" id="lastName" name="lastName" required>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<label for="streetAddress">Street Address:</label>
<input type="text" id="streetAddress" name="streetAddress">
<label for="streetAddress2">Street Address Line 2:</label>
<input type="text" id="streetAddress2" name="streetAddress2">
<label for="city">City:</label>
<input type="text" id="city" name="city">
<label for="state">State:</label>
<input type="text" id="state" name="state">
<label for="zip">Zip Code:</label>
<input type="text" id="zip" name="zip">
<label for="label">Label:</label>
<input type="text" id="label" name="label">
<label for="notes">Notes:</label>
<textarea id="notes" name="notes" rows="4"></textarea>
<button type="submit">Submit</button>
</form>
</div>
<script>
// JavaScript code to handle form submission
document.getElementById('contactForm').addEventListener('submit', async function(event) {
event.preventDefault(); // Prevent default form submission
const formData = {
firstName: document.getElementById('firstName').value,
lastName: document.getElementById('lastName').value,
email: document.getElementById('email').value,
streetAddress: document.getElementById('streetAddress').value,
streetAddress2: document.getElementById('streetAddress2').value,
city: document.getElementById('city').value,
state: document.getElementById('state').value,
zip: document.getElementById('zip').value,
label: document.getElementById('label').value,
notes: document.getElementById('notes').value
};
try {
const response = await fetch('/create-contact', {
method: 'POST',
body: JSON.stringify(formData),
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) {
alert('Contact created successfully!');
} else {
alert('Failed to create contact. Please try again later.');
}
} catch (error) {
console.error('Error:', error);
alert('Failed to create contact. Please try again later.');
}
});
</script>
</body>
</html>
This HTML code provides a simple and responsive layout for the form with the specified fields. It also includes basic CSS styles to achieve the blue color scheme similar to Google's design. You can adjust the styles further to match your preferences.
Certainly! Let's update the Cloudflare Worker script to handle the additional fields and make the necessary adjustments to use the Google People API. Here's the updated Cloudflare Worker script:
javascriptaddEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
let accessToken = '';
let tokenExpirationTime = 0;
async function getAccessToken() {
// Use your refresh token to obtain a new access token
const refreshToken = 'YOUR_REFRESH_TOKEN';
const tokenUrl = 'https://oauth2.googleapis.com/token';
const response = await fetch(tokenUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
refresh_token: refreshToken
})
});
if (response.ok) {
const responseData = await response.json();
accessToken = responseData.access_token;
tokenExpirationTime = Date.now() + (responseData.expires_in * 1000);
return accessToken;
} else {
throw new Error('Failed to refresh access token');
}
}
async function handleRequest(request) {
if (tokenExpirationTime < Date.now()) {
// Access token has expired, refresh it
await getAccessToken();
}
if (request.method === 'POST' && request.url.endsWith('/create-contact')) {
const requestData = await request.json();
const apiUrl = 'https://people.googleapis.com/v1/people:createContact';
const requestBody = {
names: [
{
givenName: requestData.firstName,
familyName: requestData.lastName
}
],
emailAddresses: [
{ value: requestData.email }
],
addresses: [
{
streetAddress: requestData.streetAddress,
extendedAddress: requestData.streetAddress2,
city: requestData.city,
region: requestData.state,
postalCode: requestData.zip,
type: 'home'
}
],
userDefined: [
{
key: 'Label',
value: requestData.label
}
],
biographies: [
{
value: requestData.notes
}
]
};
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${accessToken}`
},
body: JSON.stringify(requestBody)
});
if (response.ok) {
return new Response('Contact created successfully!', { status: 200 });
} else {
return new Response('Failed to create contact.', { status: 500 });
}
}
// Return a 404 for other requests
return new Response('Not Found', { status: 404 });
}
In this updated script:
- The request data received from the HTML form is used to construct the request body for the Google People API.
- Additional fields such as
streetAddress,streetAddress2,city,state,zip,label, andnotesare included in the request body. - The request is sent to the Google People API endpoint
https://people.googleapis.com/v1/people:createContact.
Replace 'YOUR_REFRESH_TOKEN', 'YOUR_CLIENT_ID', and 'YOUR_CLIENT_SECRET' with your actual refresh token, client ID, and client secret respectively.
With these changes, the Cloudflare Worker should now correctly handle the form submission with the updated fields and create contacts using the Google People API.
To add basic authentication to your Cloudflare Worker, you can follow a similar approach to the example provided by Cloudflare. Here's how you can integrate basic authentication into your Cloudflare Worker script:
javascriptaddEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
/**
* @param {string} AUTH_USERNAME Your desired username
* @param {string} AUTH_PASSWORD Your desired password
*/
const AUTH_USERNAME = "your_username";
const AUTH_PASSWORD = "your_password";
// Extracting the Authorization header from the request
const authHeader = request.headers.get('Authorization');
// Check if Authorization header is present and is in the format 'Basic <base64EncodedCredentials>'
if (authHeader && authHeader.startsWith('Basic ')) {
// Decode and split the credentials
const credentials = atob(authHeader.split(' ')[1]).split(':');
const username = credentials[0];
const password = credentials[1];
// Check if username and password match the predefined credentials
if (username === AUTH_USERNAME && password === AUTH_PASSWORD) {
// Correct credentials supplied. Proceed with handling the request.
// Replace the following line with your actual request handling logic
return new Response('Authenticated successfully!', { status: 200 });
}
}
// Incorrect or missing credentials. Reject the request with a 401 Unauthorized response.
return new Response("Sorry, you have supplied invalid credentials.", {
status: 401,
headers: {
'WWW-Authenticate': 'Basic realm="Restricted Area"'
}
});
}
In this script:
- Replace
'your_username'and'your_password'with your desired username and password respectively. - When a request is received, the script extracts the
Authorizationheader and checks if it starts withBasic, indicating it's using Basic authentication. - If Basic authentication is detected, the credentials are decoded from base64 and compared against the predefined username and password.
- If the credentials match, the script proceeds with handling the request. Otherwise, it returns a 401 Unauthorized response with a
WWW-Authenticateheader to prompt the client to provide valid credentials.
This basic authentication mechanism provides a simple way to protect your Cloudflare Worker with a username and password. Make sure to use HTTPS to encrypt the credentials during transmission.