Created
May 5, 2024 02:49
-
-
Save okikio/e958e9a7572d68adf73d7d6450883b0c to your computer and use it in GitHub Desktop.
Google OAuth2 w/ Deno & Hono
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { CLIENT_ID, CLIENT_SECRET } from "~/env.ts"; | |
import { gmail_v1, auth } from "@googleapis/gmail"; | |
import { Hono } from '@kyiro/hono'; | |
import open from 'open'; | |
const REDIRECT_URI = "http://localhost:8000/token"; | |
const SCOPE = "https://www.googleapis.com/auth/gmail.readonly"; | |
console.log({ | |
SCOPE, | |
REDIRECT_URI, | |
CLIENT_ID, | |
CLIENT_SECRET, | |
}) | |
// Setup the OAuth 2.0 client | |
const oauth2Client = new auth.OAuth2( | |
CLIENT_ID, // Client ID | |
CLIENT_SECRET, // Client Secret | |
REDIRECT_URI // Redirect URI set in the Google Cloud Console | |
); | |
// Generate authentication URL | |
const authUrl = oauth2Client.generateAuthUrl({ | |
access_type: 'offline', | |
scope: SCOPE, | |
}); | |
// Visit this URL to get authorization code. | |
console.log('Authorize this app by visiting this url:', authUrl); | |
const app = new Hono(); | |
// Initialize Deno KV storage | |
const kv = await Deno.openKv(); | |
// for await (const item of kv.list({ prefix: ["tokens"]})) { | |
// await kv.delete(item.key); | |
// } | |
app.get('/', async (c) => { | |
// Check if we already have a valid access token | |
const accessToken = await kv.get<string>(["tokens", "access_token"]); | |
if (accessToken.value) { | |
return c.redirect('/email'); | |
} | |
// No valid access token, need to authenticate or refresh | |
const refreshToken = await kv.get<string>(["tokens", "refresh_token"]); | |
if (refreshToken.value) { | |
// Try to refresh the token | |
const tokens = await refreshAccessToken(refreshToken.value); | |
if (tokens?.access_token) { | |
await kv.set(["tokens", "access_token"], tokens?.access_token, { | |
expireIn: tokens.expiry_date! - Date.now(), | |
}); | |
return c.redirect('/email'); | |
} | |
} | |
await open(authUrl.toString()); // Opens the URL in the default browser | |
return c.text('Please authorize the app to access your Gmail account.'); | |
}); | |
app.get('/token', async (c) => { | |
const code = c.req.query("code"); | |
if (code) { | |
const { tokens } = await oauth2Client.getToken(code); | |
oauth2Client.setCredentials(tokens); | |
console.log({ tokens }); | |
await kv.set(["tokens", "access_token"], tokens.access_token, { | |
expireIn: tokens.expiry_date! - Date.now(), | |
}); | |
if (tokens.refresh_token) { // Store refresh token only if present | |
await kv.set(["tokens", "refresh_token"], tokens.refresh_token); | |
} | |
return c.redirect('/email'); | |
} | |
return c.text('Failed to authenticate!'); | |
}); | |
app.get('/email', async (c) => { | |
console.time("Benchmark"); | |
const accessToken = await kv.get<string>(["tokens", "access_token"]); | |
if (!accessToken.value) return c.redirect('/'); | |
oauth2Client.setCredentials({ access_token: accessToken.value }); | |
try { | |
const gmail = new gmail_v1.Gmail({ auth: oauth2Client }); | |
const response = await gmail.users.messages.list({ | |
userId: 'me', | |
labelIds: ['INBOX'], | |
maxResults: 10, // Fetches the latest 10 emails | |
}); | |
const messages = response.data.messages; | |
console.log({ | |
messages | |
}) | |
if (messages && messages.length) { | |
const messageArr = await Promise.all( | |
messages.map(message => | |
gmail.users.messages.get({ | |
id: message.id, | |
userId: 'me', | |
}) | |
) | |
); | |
return c.json(messageArr.map(msg => msg.data)); | |
} else { | |
return c.text('No emails found.'); | |
} | |
} catch (error) { | |
console.error('Failed to fetch emails:', error); | |
return c.text('Failed to fetch emails.'); | |
} finally { | |
console.timeEnd("Benchmark"); | |
} | |
}); | |
async function refreshAccessToken(refreshToken: string) { | |
try { | |
const { tokens } = await oauth2Client.refreshToken(refreshToken); | |
return tokens; | |
} catch (error) { | |
console.error('Error refreshing access token:', error); | |
return null; | |
} | |
} | |
Deno?.serve?.(app.fetch); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment