173 lines
5.0 KiB
TypeScript
173 lines
5.0 KiB
TypeScript
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<string, any> {
|
|
sub: string
|
|
nickname: string
|
|
email: string
|
|
picture: string
|
|
}
|
|
|
|
|
|
|
|
export default function Patreon<P extends PatreonProfile>(
|
|
options: OAuthUserConfig<P>
|
|
): OAuthConfig<P> {
|
|
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
|
|
// },
|