diff --git a/pages/api/icns-verification.ts b/pages/api/icns-verification.ts new file mode 100644 index 0000000..904ff5c --- /dev/null +++ b/pages/api/icns-verification.ts @@ -0,0 +1,32 @@ +import { withIronSessionApiRoute } from "iron-session/next"; +import type { NextApiRequest, NextApiResponse } from "next"; +import { ironOptions } from "../../iron.config"; +import { IcnsVerificationInfoResponse } from "../../types/api-response"; +import { request } from "../../utils/url"; + +export default withIronSessionApiRoute(async function handler( + req: NextApiRequest, + res: NextApiResponse, +) { + try { + if (!process.env.ICNS_VERIFIER_URI) { + console.log(".env is not set"); + return res.status(500).json({ error: "Internal server error" }); + } + const icnsVerificationInfo = await request( + process.env.ICNS_VERIFIER_URI, + { + method: "post", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(req.body), + }, + ); + res.status(200).json(icnsVerificationInfo); + } catch (error) { + console.error(error); + res.status(500).json({ error: "Internal server error" }); + } +}, +ironOptions); diff --git a/pages/api/twitter-auth-info.ts b/pages/api/twitter-auth-info.ts index ff0300e..ca75901 100644 --- a/pages/api/twitter-auth-info.ts +++ b/pages/api/twitter-auth-info.ts @@ -13,29 +13,39 @@ export default withIronSessionApiRoute(async function handler( !process.env.TWITTER_CLIENT_SECRET || !process.env.TWITTER_AUTH_CALLBACK_URI ) { - return res - .status(500) - .send( + console.error(new Error(".env is not set")); + return res.status(500).send({ + error: "Twitter app client id or client secret or callback URI is not set", - ); + }); } if (!req.session.code_verifier) { - return res.status(401).send("No OAuth2.0 code verifier"); + return res.status(401).send({ + error: "No OAuth2.0 code verifier", + }); } try { - const { code, state } = req.query; - if (state !== process.env.TWITTER_AUTH_STATE) { - return res.status(401).send("State isn't matching"); + const params = new URLSearchParams(); + + if (req.session.refresh_token) { + params.append("grant_type", "refresh_token"); + params.append("refresh_token", req.session.refresh_token); + params.append("client_id", process.env.TWITTER_CLIENT_ID); + } else { + const { code, state } = req.query; + if (state !== process.env.TWITTER_AUTH_STATE) { + return res.status(401).send({ error: "State isn't matching" }); + } + + params.append("grant_type", "authorization_code"); + params.append("code", code as string); + params.append("redirect_uri", process.env.TWITTER_AUTH_CALLBACK_URI); + params.append("code_verifier", req.session.code_verifier); } - const params = new URLSearchParams(); - params.append("grant_type", "authorization_code"); - params.append("code", code as string); - params.append("redirect_uri", process.env.TWITTER_AUTH_CALLBACK_URI); - params.append("code_verifier", req.session.code_verifier); - const { access_token: accessToken } = + const { access_token: accessToken, refresh_token } = await request( `${twitterApiBaseUrl}/oauth2/token`, { @@ -49,6 +59,9 @@ export default withIronSessionApiRoute(async function handler( body: params, }, ); + + req.session.refresh_token = refresh_token; + await req.session.save(); const { data: { id, username }, } = await request(`${twitterApiBaseUrl}/users/me`, { @@ -62,8 +75,8 @@ export default withIronSessionApiRoute(async function handler( username, }); } catch (error) { - console.log(error); - res.status(500).send("Internal server error "); + console.error(error); + res.status(500).json({ error: "Internal server error " }); } }, ironOptions); diff --git a/pages/api/twitter-auth-url.ts b/pages/api/twitter-auth-url.ts index 556185e..5d82b7e 100644 --- a/pages/api/twitter-auth-url.ts +++ b/pages/api/twitter-auth-url.ts @@ -14,10 +14,12 @@ export default withIronSessionApiRoute(async function handler( res: NextApiResponse, ) { if ( - !process.env.TWITTER_AUTH_STATE || - !process.env.TWITTER_AUTH_CODE_CHALLENGE + !process.env.TWITTER_CLIENT_ID || + !process.env.TWITTER_AUTH_CALLBACK_URI || + !process.env.TWITTER_AUTH_STATE ) { - return res.status(500).send("No state or code_challenge"); + console.error(new Error(".env is not set")); + return res.status(500).json({ error: "Internal server error" }); } try { @@ -42,7 +44,7 @@ export default withIronSessionApiRoute(async function handler( res.status(200).json({ authUrl }); } catch (error) { console.error(error); - res.status(500).send("Internal server error"); + res.status(500).json({ error: "Internal server error" }); } }, ironOptions); @@ -50,5 +52,6 @@ ironOptions); declare module "iron-session" { interface IronSessionData { code_verifier?: string; + refresh_token?: string; } } diff --git a/pages/verification/index.tsx b/pages/verification/index.tsx index 113a477..cdd3068 100644 --- a/pages/verification/index.tsx +++ b/pages/verification/index.tsx @@ -1,25 +1,51 @@ import { useEffect, useState } from "react"; -import { TwitterAuthInfoResponse } from "../../types/api-response"; +import { + IcnsVerificationInfoResponse, + TwitterAuthInfoResponse, +} from "../../types/api-response"; +import { VerifierMsg } from "../../types/msg"; +import { request } from "../../utils/url"; export default function VerificationPage() { const [twitterAuthInfo, setTwitterAuthInfo] = useState(); - const fetchAccessToken = async (state: string, code: string) => { - const newTwitterAuthInfo: TwitterAuthInfoResponse = await ( - await fetch(`/api/twitter-auth-info?state=${state}&code=${code}`) - ).json(); - - setTwitterAuthInfo(newTwitterAuthInfo); - }; - useEffect(() => { - const [, state, code] = - window.location.search.match( - /^(?=.*state=([^&]+)|)(?=.*code=([^&]+)|).+$/, - ) || []; + const handleVerification = async () => { + const [, state, code] = + window.location.search.match( + /^(?=.*state=([^&]+)|)(?=.*code=([^&]+)|).+$/, + ) || []; + const newTwitterAuthInfo = await request( + `/api/twitter-auth-info?state=${state}&code=${code}`, + ); - fetchAccessToken(state, code); + console.log(newTwitterAuthInfo); + + setTwitterAuthInfo(newTwitterAuthInfo); + const verifierMsg: VerifierMsg = { + name: newTwitterAuthInfo.username, + claimer: "osmo1y5mm5nj5m8ttddt5ccspek6xgyyavehrkak7gq", + contract_address: "osmo1y5mm5nj5m8ttddt5ccspek6xgyyavehrkak7gq", + chain_id: "osmosis-1", + }; + const icnsVerificationInfo = await request( + "/api/icns-verification", + { + method: "post", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + msg: JSON.stringify(verifierMsg), + authToken: newTwitterAuthInfo.accessToken, + }), + }, + ); + console.log(icnsVerificationInfo); + }; + + handleVerification(); }, []); return ( diff --git a/types/api-response.ts b/types/api-response.ts index 5062d89..d0b4bd1 100644 --- a/types/api-response.ts +++ b/types/api-response.ts @@ -7,3 +7,8 @@ export interface TwitterAuthInfoResponse { id: string; username: string; } + +export interface IcnsVerificationInfoResponse { + signature: number[]; + algorithm: string; +} diff --git a/types/msg.ts b/types/msg.ts new file mode 100644 index 0000000..ff58829 --- /dev/null +++ b/types/msg.ts @@ -0,0 +1,6 @@ +export interface VerifierMsg { + name: string; + claimer: string; + contract_address: string; + chain_id: string; +}