From 33d78338add8bd716afb7d21a242b39c379a973d Mon Sep 17 00:00:00 2001 From: Rafael Bradley <84998222+Nekidev@users.noreply.github.com> Date: Fri, 26 Jan 2024 00:00:00 -0300 Subject: [PATCH] update(web): Add support for AniList OAuth2 login --- src/web/.env.example | 4 + src/web/package.json | 9 +- src/web/pnpm-lock.yaml | 47 +++ .../api/auth/oauth2/anilist/callback/route.ts | 46 +++ .../src/app/api/auth/oauth2/anilist/route.ts | 31 ++ src/web/src/app/code/page.js | 66 ++++ src/web/src/app/layout.js | 22 +- src/web/src/app/page.js | 284 +++++++++--------- src/web/tsconfig.json | 34 +++ 9 files changed, 396 insertions(+), 147 deletions(-) create mode 100644 src/web/.env.example create mode 100644 src/web/src/app/api/auth/oauth2/anilist/callback/route.ts create mode 100644 src/web/src/app/api/auth/oauth2/anilist/route.ts create mode 100644 src/web/src/app/code/page.js create mode 100644 src/web/tsconfig.json diff --git a/src/web/.env.example b/src/web/.env.example new file mode 100644 index 0000000..4578028 --- /dev/null +++ b/src/web/.env.example @@ -0,0 +1,4 @@ +# AniList's OAuth2 +PROVIDER_ANILIST_CLIENT_ID= +PROVIDER_ANILIST_CLIENT_SECRET= +PROVIDER_ANILIST_REDIRECT_URI= \ No newline at end of file diff --git a/src/web/package.json b/src/web/package.json index c51d833..e0ce2ec 100644 --- a/src/web/package.json +++ b/src/web/package.json @@ -9,8 +9,13 @@ "lint": "next lint" }, "dependencies": { + "next": "14.1.0", "react": "^18", - "react-dom": "^18", - "next": "14.1.0" + "react-dom": "^18" + }, + "devDependencies": { + "@types/node": "^20.11.6", + "@types/react": "18.2.48", + "typescript": "5.3.3" } } diff --git a/src/web/pnpm-lock.yaml b/src/web/pnpm-lock.yaml index 795678f..b938f57 100644 --- a/src/web/pnpm-lock.yaml +++ b/src/web/pnpm-lock.yaml @@ -15,6 +15,17 @@ dependencies: specifier: ^18 version: 18.2.0(react@18.2.0) +devDependencies: + '@types/node': + specifier: ^20.11.6 + version: 20.11.6 + '@types/react': + specifier: 18.2.48 + version: 18.2.48 + typescript: + specifier: 5.3.3 + version: 5.3.3 + packages: /@next/env@14.1.0: @@ -108,6 +119,28 @@ packages: tslib: 2.6.2 dev: false + /@types/node@20.11.6: + resolution: {integrity: sha512-+EOokTnksGVgip2PbYbr3xnR7kZigh4LbybAfBAw5BpnQ+FqBYUsvCEjYd70IXKlbohQ64mzEYmMtlWUY8q//Q==} + dependencies: + undici-types: 5.26.5 + dev: true + + /@types/prop-types@15.7.11: + resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==} + dev: true + + /@types/react@18.2.48: + resolution: {integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==} + dependencies: + '@types/prop-types': 15.7.11 + '@types/scheduler': 0.16.8 + csstype: 3.1.3 + dev: true + + /@types/scheduler@0.16.8: + resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==} + dev: true + /busboy@1.6.0: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} @@ -123,6 +156,10 @@ packages: resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} dev: false + /csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dev: true + /graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} dev: false @@ -249,3 +286,13 @@ packages: /tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} dev: false + + /typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + dev: true + + /undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + dev: true diff --git a/src/web/src/app/api/auth/oauth2/anilist/callback/route.ts b/src/web/src/app/api/auth/oauth2/anilist/callback/route.ts new file mode 100644 index 0000000..ef71230 --- /dev/null +++ b/src/web/src/app/api/auth/oauth2/anilist/callback/route.ts @@ -0,0 +1,46 @@ +import * as crypto from "crypto"; +import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; +import { type NextRequest } from "next/server"; + +export const dynamic = "force-dynamic"; // defaults to auto + +function encrypt(string: string, key: string, iv: string): string { + const cipher = crypto.createCipheriv("aes-256-cbc", key, iv); + let encryptedString = cipher.update(string, "utf-8", "hex"); + encryptedString += cipher.final("hex"); + return encryptedString; +} + +export async function GET(request: NextRequest) { + const res = await fetch("https://anilist.co/api/v2/oauth/token", { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + grant_type: "authorization_code", + client_id: process.env.PROVIDER_ANILIST_CLIENT_ID, + client_secret: process.env.PROVIDER_ANILIST_CLIENT_SECRET, + redirect_uri: process.env.PROVIDER_ANILIST_REDIRECT_URI, + code: request.nextUrl.searchParams.get("code"), + }), + }); + + if (!res.ok) { + redirect(`/code?status=fail`); + } + + const json = await res.json(); + + const cookieStore = cookies(); + + redirect( + `/code?status=success&key=${encrypt( + json.access_token, + cookieStore.get("key")?.value!, + cookieStore.get("iv")?.value! + )}` + ); +} diff --git a/src/web/src/app/api/auth/oauth2/anilist/route.ts b/src/web/src/app/api/auth/oauth2/anilist/route.ts new file mode 100644 index 0000000..bde5426 --- /dev/null +++ b/src/web/src/app/api/auth/oauth2/anilist/route.ts @@ -0,0 +1,31 @@ +import * as crypto from "crypto"; +import { cookies } from "next/headers"; +import { redirect } from "next/navigation"; +import { type NextRequest } from "next/server"; + +export const dynamic = "force-dynamic"; // defaults to auto + +export async function GET(request: NextRequest) { + const key = request.nextUrl.searchParams.get("key"); + const iv = request.nextUrl.searchParams.get("iv"); + + if (!key || !iv) { + redirect("/code?status=fail"); + } + + const cookieStore = cookies(); + cookieStore.set("key", key, { + expires: new Date(new Date().getTime() + 5 * 60 * 1000), + }); + cookieStore.set("iv", iv, { + expires: new Date(new Date().getTime() + 5 * 60 * 1000), + }); + + redirect( + `https://anilist.co/api/v2/oauth/authorize?client_id=${encodeURIComponent( + process.env.PROVIDER_ANILIST_CLIENT_ID + )}&redirect_uri=${encodeURIComponent( + process.env.PROVIDER_ANILIST_REDIRECT_URI + )}&response_type=code` + ); +} diff --git a/src/web/src/app/code/page.js b/src/web/src/app/code/page.js new file mode 100644 index 0000000..7116f6c --- /dev/null +++ b/src/web/src/app/code/page.js @@ -0,0 +1,66 @@ +"use client"; + +import { useSearchParams } from "next/navigation"; + +export default function Code() { + const searchParams = useSearchParams(); + + if (searchParams.get("status") !== "success") { + return ( + <> +

Something went wrong

+

+ For some reason you couldn't be authenticated. You can close + this tab. +

+

+ + If the issue persists, ask for help in our Discord + server. You can find the link here. + +

+ + ); + } + return ( + <> +

Success!

+

Go back to Naoka and paste the following code:

+
+ + +
+

+ You can close this tab after pasting the code in Naoka. +

+ + ); +} diff --git a/src/web/src/app/layout.js b/src/web/src/app/layout.js index 6eed206..5d40ced 100644 --- a/src/web/src/app/layout.js +++ b/src/web/src/app/layout.js @@ -9,13 +9,33 @@ export default function RootLayout({ children }) { justifyContent: "center", }} > +
{children} +
+

+

+ + Go to the top ·{" "} + Home{" "} + · Nyeki + + MIT License +
+

diff --git a/src/web/src/app/page.js b/src/web/src/app/page.js index 2961eea..61768ea 100644 --- a/src/web/src/app/page.js +++ b/src/web/src/app/page.js @@ -2,150 +2,146 @@ export default function Home() { return ( <> -

Naoka

- - Releases (Download) - {" "} - · Discord server{" "} - ·{" "} - GitHub repository -
-
-
- - - - - - -
-
- Hihi! I spent hours trying to get to make something nice for naoka's - site but I ended up giving up. Not like much is needed to host some - text anyways. -
-
- Naoka is an anime and manga tracking app with a good looking UI, - (not like this site, trust me). I'll eventually work on something - nicer to put here, but in the meantime the only thing you need is - the download link. -
-
- What features does the app have, you're probably asking yourself. - They're a few: - - I cannot remember any other thing to add to the list, but probably - by the time you're reading this much more features were introduced. -
-
- At the time of writing this, macOS and Linux aren't supported yet, - but the plan is to add support for them in the future so go check - the GitHub page if you use one of those platforms. -
-
I think there's nothing else to say here, if I remember - something I'll eventually add it here. -
-
- - Nyeki -
-
- PD: If you have a better idea of something to place in this site, - you can send me a DM on Discord. I'd recommend giving it a deeper - look. -
-
- - Go to the top ·{" "} - Nyeki - - MIT License -
+

Naoka

+

+ + Releases (Download) + {" "} + ·{" "} + Discord server{" "} + ·{" "} + GitHub repository{" "} + ·{" "} + Ko-fi +

+

+

+ + + + + + +
+

+

+ Hihi! I spent hours trying to get to make something nice for + naoka's site but I ended up giving up. Not like much is needed + to host some text anyways. +

+

+ Naoka is an anime and manga tracking app with a good looking UI, + (not like this site, trust me). I'll eventually work on + something nicer to put here, but in the meantime the only thing + you need is the download link. +

+

+ What features does the app have, you're probably asking + yourself. They're a few: +

+

+

+ I cannot remember any other thing to add to the list, but + probably by the time you're reading this much more features were + introduced. +

+

+ At the time of writing this, macOS and Linux aren't supported + yet, but the plan is to add support for them in the future so go + check the GitHub page if you use one of those platforms. +

+

+ I think there's nothing else to say here, if I remember + something I'll eventually add it here. +

+

- Nyeki

+

+ PD: If you have a better idea of something to place in this + site, you can send me a DM on Discord. I'd recommend giving it a + deeper look. +

); } diff --git a/src/web/tsconfig.json b/src/web/tsconfig.json new file mode 100644 index 0000000..14bd9ea --- /dev/null +++ b/src/web/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ] + }, + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +}