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 { 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);
|
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";
|
||||
|
||||
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;
|
||||
};
|
||||
|
@ -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
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