Refactor api route path and types
This commit is contained in:
parent
0fc5ac8f0c
commit
00ebc426d3
@ -1,49 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
|
||||||
import crypto from "crypto";
|
|
||||||
import { withIronSessionApiRoute } from "iron-session/next";
|
|
||||||
import { base64URLEncode } from "../../../utils/encoding";
|
|
||||||
import { buildQueryString } from "../../../utils/url";
|
|
||||||
import { ironOptions } from "../../../iron.config";
|
|
||||||
import {
|
|
||||||
twitterOAuthBaseUrl,
|
|
||||||
twitterOAuthScopes,
|
|
||||||
} from "../../../constants/twitter";
|
|
||||||
|
|
||||||
export default withIronSessionApiRoute(async function handler(
|
|
||||||
req: NextApiRequest,
|
|
||||||
res: NextApiResponse,
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
!process.env.TWITTER_AUTH_STATE ||
|
|
||||||
!process.env.TWITTER_AUTH_CODE_CHALLENGE
|
|
||||||
) {
|
|
||||||
return res.status(500).send("No state or code_challenge");
|
|
||||||
}
|
|
||||||
|
|
||||||
const codeVerifier = base64URLEncode(crypto.randomBytes(32));
|
|
||||||
req.session.code_verifier = codeVerifier;
|
|
||||||
await req.session.save();
|
|
||||||
const codeChallenge = base64URLEncode(
|
|
||||||
crypto.createHash("sha256").update(codeVerifier).digest(),
|
|
||||||
);
|
|
||||||
const authUrlObj = new URL(twitterOAuthBaseUrl);
|
|
||||||
authUrlObj.search = buildQueryString({
|
|
||||||
client_id: process.env.TWITTER_CLIENT_ID,
|
|
||||||
redirect_uri: process.env.TWITTER_AUTH_CALLBACK_URI,
|
|
||||||
state: process.env.TWITTER_AUTH_STATE,
|
|
||||||
scope: twitterOAuthScopes.join(" "),
|
|
||||||
response_type: "code",
|
|
||||||
code_challenge_method: "s256",
|
|
||||||
code_challenge: codeChallenge,
|
|
||||||
});
|
|
||||||
const authUrl = authUrlObj.toString();
|
|
||||||
|
|
||||||
res.status(200).json({ authUrl });
|
|
||||||
},
|
|
||||||
ironOptions);
|
|
||||||
|
|
||||||
declare module "iron-session" {
|
|
||||||
interface IronSessionData {
|
|
||||||
code_verifier?: string;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from "next";
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
import { withIronSessionApiRoute } from "iron-session/next";
|
import { withIronSessionApiRoute } from "iron-session/next";
|
||||||
import { request } from "../../../utils/url";
|
import { request } from "../../utils/url";
|
||||||
import { ironOptions } from "../../../iron.config";
|
import { ironOptions } from "../../iron.config";
|
||||||
import { twitterApiBaseUrl } from "../../../constants/twitter";
|
import { twitterApiBaseUrl } from "../../constants/twitter";
|
||||||
|
|
||||||
interface TwitterOAuth2TokenData {
|
interface TwitterOAuth2TokenData {
|
||||||
token_type: string;
|
token_type: string;
|
||||||
@ -16,7 +16,6 @@ export default withIronSessionApiRoute(async function handler(
|
|||||||
req: NextApiRequest,
|
req: NextApiRequest,
|
||||||
res: NextApiResponse,
|
res: NextApiResponse,
|
||||||
) {
|
) {
|
||||||
try {
|
|
||||||
if (
|
if (
|
||||||
!process.env.TWITTER_CLIENT_ID ||
|
!process.env.TWITTER_CLIENT_ID ||
|
||||||
!process.env.TWITTER_CLIENT_SECRET ||
|
!process.env.TWITTER_CLIENT_SECRET ||
|
||||||
@ -33,6 +32,7 @@ export default withIronSessionApiRoute(async function handler(
|
|||||||
return res.status(401).send("No OAuth2.0 code verifier");
|
return res.status(401).send("No OAuth2.0 code verifier");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
const { code, state } = req.query;
|
const { code, state } = req.query;
|
||||||
if (state !== process.env.TWITTER_AUTH_STATE) {
|
if (state !== process.env.TWITTER_AUTH_STATE) {
|
||||||
return res.status(401).send("State isn't matching");
|
return res.status(401).send("State isn't matching");
|
||||||
@ -61,6 +61,7 @@ export default withIronSessionApiRoute(async function handler(
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
res.status(500).send("Internal server error ");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ironOptions);
|
ironOptions);
|
54
pages/api/twitter-auth-url.ts
Normal file
54
pages/api/twitter-auth-url.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import crypto from "crypto";
|
||||||
|
import { withIronSessionApiRoute } from "iron-session/next";
|
||||||
|
import { base64URLEncode } from "../../utils/encoding";
|
||||||
|
import { buildQueryString } from "../../utils/url";
|
||||||
|
import { ironOptions } from "../../iron.config";
|
||||||
|
import {
|
||||||
|
twitterOAuthBaseUrl,
|
||||||
|
twitterOAuthScopes,
|
||||||
|
} from "../../constants/twitter";
|
||||||
|
|
||||||
|
export default withIronSessionApiRoute(async function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse,
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!process.env.TWITTER_AUTH_STATE ||
|
||||||
|
!process.env.TWITTER_AUTH_CODE_CHALLENGE
|
||||||
|
) {
|
||||||
|
return res.status(500).send("No state or code_challenge");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const codeVerifier = base64URLEncode(crypto.randomBytes(32));
|
||||||
|
req.session.code_verifier = codeVerifier;
|
||||||
|
await req.session.save();
|
||||||
|
const codeChallenge = base64URLEncode(
|
||||||
|
crypto.createHash("sha256").update(codeVerifier).digest(),
|
||||||
|
);
|
||||||
|
const authUrlObj = new URL(twitterOAuthBaseUrl);
|
||||||
|
authUrlObj.search = buildQueryString({
|
||||||
|
client_id: process.env.TWITTER_CLIENT_ID,
|
||||||
|
redirect_uri: process.env.TWITTER_AUTH_CALLBACK_URI,
|
||||||
|
state: process.env.TWITTER_AUTH_STATE,
|
||||||
|
scope: twitterOAuthScopes.join(" "),
|
||||||
|
response_type: "code",
|
||||||
|
code_challenge_method: "s256",
|
||||||
|
code_challenge: codeChallenge,
|
||||||
|
});
|
||||||
|
const authUrl = authUrlObj.toString();
|
||||||
|
|
||||||
|
res.status(200).json({ authUrl });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
res.status(500).send("Internal server error");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ironOptions);
|
||||||
|
|
||||||
|
declare module "iron-session" {
|
||||||
|
interface IronSessionData {
|
||||||
|
code_verifier?: string;
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,11 @@
|
|||||||
import styles from "../styles/Home.module.css";
|
import styles from "../styles/Home.module.css";
|
||||||
|
import { TwitterAuthUrlResponse } from "../types/api-response";
|
||||||
interface AuthResponse {
|
|
||||||
authUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const handleSignInWithTwitter = async () => {
|
const handleSignInWithTwitter = async () => {
|
||||||
const { authUrl }: AuthResponse = await (await fetch("/api/auth")).json();
|
const { authUrl }: TwitterAuthUrlResponse = await (
|
||||||
|
await fetch("/api/twitter-auth-url")
|
||||||
|
).json();
|
||||||
|
|
||||||
window.location.href = authUrl;
|
window.location.href = authUrl;
|
||||||
};
|
};
|
||||||
|
@ -1,14 +1,11 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import { TwitterAuthInfoResponse } from "../../types/api-response";
|
||||||
interface AccessTokenResponse {
|
|
||||||
accessToken: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function VerificationPage() {
|
export default function VerificationPage() {
|
||||||
const [accessToken, setAccessToken] = useState<string>();
|
const [accessToken, setAccessToken] = useState<string>();
|
||||||
|
|
||||||
const fetchAccessToken = async (state: string, code: string) => {
|
const fetchAccessToken = async (state: string, code: string) => {
|
||||||
const { accessToken }: AccessTokenResponse = await (
|
const { accessToken }: TwitterAuthInfoResponse = await (
|
||||||
await fetch(`/api/auth/access-token?state=${state}&code=${code}`)
|
await fetch(`/api/auth/access-token?state=${state}&code=${code}`)
|
||||||
).json();
|
).json();
|
||||||
|
|
||||||
|
7
types/api-response.ts
Normal file
7
types/api-response.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface TwitterAuthUrlResponse {
|
||||||
|
authUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TwitterAuthInfoResponse {
|
||||||
|
accessToken: string;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user