Refactor api route path and types

This commit is contained in:
delivan 2022-12-05 20:01:24 +09:00
parent 0fc5ac8f0c
commit 00ebc426d3
6 changed files with 87 additions and 78 deletions

View File

@ -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;
}
}

View File

@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { withIronSessionApiRoute } from "iron-session/next";
import { request } from "../../../utils/url";
import { ironOptions } from "../../../iron.config";
import { twitterApiBaseUrl } from "../../../constants/twitter";
import { request } from "../../utils/url";
import { ironOptions } from "../../iron.config";
import { twitterApiBaseUrl } from "../../constants/twitter";
interface TwitterOAuth2TokenData {
token_type: string;
@ -16,23 +16,23 @@ export default withIronSessionApiRoute(async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (
!process.env.TWITTER_CLIENT_ID ||
!process.env.TWITTER_CLIENT_SECRET ||
!process.env.TWITTER_AUTH_CALLBACK_URI
) {
return res
.status(500)
.send(
"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");
}
try {
if (
!process.env.TWITTER_CLIENT_ID ||
!process.env.TWITTER_CLIENT_SECRET ||
!process.env.TWITTER_AUTH_CALLBACK_URI
) {
return res
.status(500)
.send(
"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");
}
const { code, state } = req.query;
if (state !== process.env.TWITTER_AUTH_STATE) {
return res.status(401).send("State isn't matching");
@ -61,6 +61,7 @@ export default withIronSessionApiRoute(async function handler(
});
} catch (error) {
console.log(error);
res.status(500).send("Internal server error ");
}
},
ironOptions);

View 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;
}
}

View File

@ -1,12 +1,11 @@
import styles from "../styles/Home.module.css";
interface AuthResponse {
authUrl: string;
}
import { TwitterAuthUrlResponse } from "../types/api-response";
export default function Home() {
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;
};

View File

@ -1,14 +1,11 @@
import { useEffect, useState } from "react";
interface AccessTokenResponse {
accessToken: string;
}
import { TwitterAuthInfoResponse } from "../../types/api-response";
export default function VerificationPage() {
const [accessToken, setAccessToken] = useState<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}`)
).json();

7
types/api-response.ts Normal file
View File

@ -0,0 +1,7 @@
export interface TwitterAuthUrlResponse {
authUrl: string;
}
export interface TwitterAuthInfoResponse {
accessToken: string;
}