import { twitterRequestAccessToken } from "https://esm.town/v/andreterron/twitterRequestAccessToken";
import { redirectResponse } from "https://esm.town/v/andreterron/redirectResponse";
import { nanoid } from "https://esm.town/v/stevekrouse/nanoid?v=3";
export async function twitterAuthHandler(
req: Request,
{ client_id, client_secret, redirect_uri, scopes, storage }: {
client_id: string;
client_secret: string;
redirect_uri: string;
scopes: string[];
storage?: {
state: string;
challenge: string;
};
},
): Promise<{
res: Response;
storage?: {
state: string;
challenge: string;
};
token?: {
token_type: string;
expires_in: number;
access_token: string;
scope: string;
refresh_token: string;
};
}> {
const { Client, auth } = await import("npm:twitter-api-sdk");
const authClient = new auth.OAuth2User({
client_id,
client_secret,
callback: redirect_uri,
scopes: scopes as any[],
});
const client = new Client(authClient);
const reqUrl = new URL(req.url);
switch (reqUrl.pathname.replace(/\/$/, "")) {
case "": {
let challenge = await nanoid();
let state = await nanoid();
const authUrl = authClient.generateAuthURL({
state: state,
code_challenge_method: "plain",
code_challenge: challenge,
});
return {
res: redirectResponse(authUrl),
storage: {
state,
challenge,
},
};
}
case "/callback": {
const code = reqUrl.searchParams.get("code");
const state = reqUrl.searchParams.get("state");
if (state !== storage.state)
return { res: new Response("State isn't matching", { status: 500 }) };
const tokenResponse = await twitterRequestAccessToken({
code,
client_id,
client_secret,
redirect_uri,
challenge: storage.challenge,
});
return { res: new Response("OK", { status: 200 }), token: tokenResponse };
}
}
return { res: new Response("Not Found", { status: 404 }) };
}