import KeycloakProvider, { type KeycloakProfile } from "next-auth/providers/keycloak"; import { NextAuthOptions } from "next-auth"; import { jwtDecode } from "jwt-decode"; import { type JWT } from "next-auth/jwt"; import NextAuth, { User, Profile } from "next-auth"; import { configs } from "../config/configs"; import { extractCurrentlyEntitledTiers, mapTierIdsToRoles } from "./patreon"; import { getTierNameFromTierId } from "./keycloak"; import type { OAuthConfig, OAuthUserConfig } from "next-auth/providers/oauth"; declare module "next-auth" { interface Session { user: User; token: JWT | undefined; profile: Profile; } interface Profile { realm_access: any; preferred_username: string; username_visibility?: "public" | "private"; } interface Session { roles: any } } declare module "next-auth/jwt" { interface JWT { expires_at: number; access_token: string; refresh_token: string; profile: Profile; } } export interface PatreonProfile extends Record { sub: string nickname: string email: string picture: string } export default function Patreon

( options: OAuthUserConfig

): OAuthConfig

{ return { id: "patreon", name: "Patreon", type: "oauth", version: "2.0", authorization: { url: "https://www.patreon.com/oauth2/authorize", params: { scope: "identity" }, }, token: "https://www.patreon.com/api/oauth2/token", userinfo: "https://www.patreon.com/api/oauth2/v2/identity?fields%5Buser%5D=about,created,email,first_name,full_name,image_url,last_name,thumb_url,url,vanity&include=memberships,memberships.currently_entitled_tiers,memberships.currently_entitled_tiers.benefits", profile(profile) { console.log(`profile callback!!!! userinfo as follows`) // console.log(profile) const tiers = extractCurrentlyEntitledTiers(profile) console.log(tiers) return { id: profile.data.id, name: profile.data.attributes.full_name, email: profile.data.attributes.email, image: profile.data.attributes.image_url, currently_entitled_tiers: tiers } }, style: { logo: "/patreon.svg", bg: "#e85b46", text: "#fff" }, options, } } export const authOptions: NextAuthOptions = { session: { strategy: "jwt" }, providers: [ Patreon({ clientId: configs.patreonClientId, clientSecret: configs.patreonClientSecret, }) ], callbacks: { // Using the `...rest` parameter to be able to narrow down the type based on `trigger` jwt({ token, trigger, session, account, profile, user }) { console.log(`next-auth jwt callback! trigger=${trigger}`) if (account) { console.log(`>>>> account was present!`) console.log(account) console.log(user) token.currently_entitled_tiers = user.currently_entitled_tiers } if (trigger === 'signIn' && profile) { console.log('trigger is signIn and profile as follows') console.log(profile) token.test = true return token } if (trigger === "update" && session?.name) { // Note, that `session` can be any arbitrary object, remember to validate it! token.name = session.name } token.test = 'yessir' return token }, async session({ session, token, user }) { console.log('Send properties to the client, like an access_token and user id from a provider.') console.log(token) console.log(session) console.log(user) session.token = token if (token?.currently_entitled_tiers) { session.roles = mapTierIdsToRoles(token.currently_entitled_tiers) } return session } } } export const { handler, signIn, signOut, auth } = NextAuth(authOptions) // async jwt({ token, account, profile }) { // try { // if (account) { // const decodedToken = jwtDecode(account.access_token as any) // if (token == null){ // throw new Error("Unable to decode token") // } // console.log('decodedToken as follows') // console.log(JSON.stringify(decodedToken, null, 2)) // // Do something here to add more info, maybe just overwrite profile (thats the one that should have this info) // profile = decodedToken // token.account = account // } // if (profile) { // console.log('profile as follows') // console.log(profile) // token.profile = profile // // Then do here the assignation of roles elements to token so session has access // // This can be modified so uses by client, realm or account BE AWARE OF THAT! // // Modify the "resource_access['next-auth-AFB']" value to the one your resource/realm/accout // // json scope roles you need // // While the info is already on profile, we could make a new key on the json response of session // const clientRoles = profile.realm_access.roles // token.client_roles = clientRoles // } // } catch (error) { // console.log(error) // } // return token // },