Last active
November 22, 2023 18:07
-
-
Save SecondThundeR/1814f8c4aae6936b6e801da8bd9628d8 to your computer and use it in GitHub Desktop.
Custom Shikimori NextAuth provider for T3 Stack
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Usage example with T3 Stack (NextAuth + Prisma) | |
import { PrismaAdapter } from "@next-auth/prisma-adapter"; | |
import { type GetServerSidePropsContext } from "next"; | |
import { | |
getServerSession, | |
type NextAuthOptions, | |
type DefaultSession, | |
} from "next-auth"; | |
import ShikimoriProvider from "@/providers/shikimori"; | |
import { env } from "@/env.mjs"; | |
import { prisma } from "@/server/db"; | |
/** | |
* Module augmentation for `next-auth` types. Allows us to add custom properties to the `session` | |
* object and keep type safety. | |
* | |
* @see https://next-auth.js.org/getting-started/typescript#module-augmentation | |
*/ | |
declare module "next-auth" { | |
interface Session extends DefaultSession { | |
user: { | |
id: string; | |
// ...other properties | |
// role: UserRole; | |
} & DefaultSession["user"]; | |
} | |
// interface User { | |
// // ...other properties | |
// // role: UserRole; | |
// } | |
} | |
/** | |
* Options for NextAuth.js used to configure adapters, providers, callbacks, etc. | |
* | |
* @see https://next-auth.js.org/configuration/options | |
*/ | |
export const authOptions: NextAuthOptions = { | |
callbacks: { | |
session: ({ session, user }) => ({ | |
...session, | |
user: { | |
...session.user, | |
id: user.id, | |
}, | |
}), | |
}, | |
adapter: PrismaAdapter(prisma), | |
providers: [ | |
ShikimoriProvider({ | |
clientId: env.SHIKIMORI_CLIENT_ID, | |
clientSecret: env.SHIKIMORI_CLIENT_SECRET, | |
}), | |
], | |
}; | |
/** | |
* Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file. | |
* | |
* @see https://next-auth.js.org/configuration/nextjs | |
*/ | |
export const getServerAuthSession = (ctx: { | |
req: GetServerSidePropsContext["req"]; | |
res: GetServerSidePropsContext["res"]; | |
}) => { | |
return getServerSession(ctx.req, ctx.res, authOptions); | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Default T3 Stack env.mjs with Shikimori credentials setup | |
import { createEnv } from "@t3-oss/env-nextjs"; | |
import { z } from "zod"; | |
export const env = createEnv({ | |
/** | |
* Specify your server-side environment variables schema here. This way you can ensure the app | |
* isn't built with invalid env vars. | |
*/ | |
server: { | |
DATABASE_URL: z.string().url(), | |
NODE_ENV: z.enum(["development", "test", "production"]), | |
NEXTAUTH_SECRET: | |
process.env.NODE_ENV === "production" | |
? z.string().min(1) | |
: z.string().min(1).optional(), | |
NEXTAUTH_URL: z.preprocess( | |
// This makes Vercel deployments not fail if you don't set NEXTAUTH_URL | |
// Since NextAuth.js automatically uses the VERCEL_URL if present. | |
(str) => process.env.VERCEL_URL ?? str, | |
// VERCEL_URL doesn't include `https` so it cant be validated as a URL | |
process.env.VERCEL ? z.string().min(1) : z.string().url() | |
), | |
// Add `.min(1) on ID and SECRET if you want to make sure they're not empty | |
SHIKIMORI_CLIENT_ID: z.string().min(1), | |
SHIKIMORI_CLIENT_SECRET: z.string().min(1), | |
}, | |
/** | |
* Specify your client-side environment variables schema here. This way you can ensure the app | |
* isn't built with invalid env vars. To expose them to the client, prefix them with | |
* `NEXT_PUBLIC_`. | |
*/ | |
client: { | |
// NEXT_PUBLIC_CLIENTVAR: z.string().min(1), | |
}, | |
/** | |
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g. | |
* middlewares) or client-side so we need to destruct manually. | |
*/ | |
runtimeEnv: { | |
DATABASE_URL: process.env.DATABASE_URL, | |
NODE_ENV: process.env.NODE_ENV, | |
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET, | |
NEXTAUTH_URL: process.env.NEXTAUTH_URL, | |
SHIKIMORI_CLIENT_ID: process.env.SHIKIMORI_CLIENT_ID, | |
SHIKIMORI_CLIENT_SECRET: process.env.SHIKIMORI_CLIENT_SECRET, | |
}, | |
/** | |
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. | |
* This is especially useful for Docker builds. | |
*/ | |
skipValidation: !!process.env.SKIP_ENV_VALIDATION, | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { | |
type OAuthConfig, | |
type OAuthUserConfig, | |
} from "next-auth/providers/oauth"; | |
export interface ShikimoriUserInfo { | |
id: number; | |
nickname: string; | |
avatar: string; | |
image: { | |
x160: string; | |
x148: string; | |
x80: string; | |
x64: string; | |
x48: string; | |
x32: string; | |
x16: string; | |
}; | |
last_online_at: Date; | |
url: string; | |
name: string | null; | |
sex: string | null; | |
website: string; | |
full_years: number | null; | |
birth_on: Date | null; | |
locale: string | null; | |
} | |
export interface ShikimoriUser | |
extends ShikimoriUserInfo, | |
Record<string, unknown> { | |
last_online: string; | |
location: string | null; | |
banned: boolean; | |
about: string; | |
about_html: string; | |
common_info: string[] | null; | |
show_comments: boolean; | |
in_friends: boolean | null; | |
is_ignored: boolean; | |
style_id: number; | |
} | |
export default function ShikimoriProvider<P extends ShikimoriUser>( | |
options: OAuthUserConfig<P>, | |
): OAuthConfig<P> { | |
return { | |
id: "shikimori", | |
name: "Shikimori", | |
type: "oauth", | |
token: "https://shikimori.one/oauth/token", | |
authorization: "https://shikimori.one/oauth/authorize?scope=", | |
userinfo: "https://shikimori.one/api/users/whoami", | |
profile(profile) { | |
return { | |
id: String(profile.id), | |
email: null, | |
name: profile.nickname, | |
image: profile.image.x160, | |
}; | |
}, | |
options, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment