Skip to content

Commit

Permalink
refactor: initialize user data db in once (#1224)
Browse files Browse the repository at this point in the history
* refactor: initialize user profile in db once
* refactor: migrate next-auth handler to next 13
  • Loading branch information
vnugent authored Nov 16, 2024
1 parent 4ecfc61 commit b190931
Show file tree
Hide file tree
Showing 17 changed files with 105 additions and 116 deletions.
2 changes: 0 additions & 2 deletions src/app/(default)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { PageFooter } from './components/PageFooter'
import { NextAuthProvider } from '@/components/auth/NextAuthProvider'
import { ReactToastifyProvider } from './components/ReactToastifyProvider'
import { BlockingAlertUploadingInProgress } from './components/ui/GlobalAlerts'
import { OnboardingCheck } from '@/components/auth/OnboardingCheck'

export const metadata: Metadata = {
title: 'OpenBeta',
Expand Down Expand Up @@ -36,7 +35,6 @@ export default function RootLayout ({
<div>
{children}
</div>
<OnboardingCheck />
</NextAuthProvider>
<PageFooter />
<ReactToastifyProvider />
Expand Down
2 changes: 0 additions & 2 deletions src/app/(maps)/components/ProfileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import Link from 'next/link'
import { SessionProvider } from 'next-auth/react'
import { House } from '@phosphor-icons/react/dist/ssr'
import AuthenticatedProfileNavButton from '@/components/AuthenticatedProfileNavButton'
import { OnboardingCheck } from '@/components/auth/OnboardingCheck'

export const ProfileMenu: React.FC = () => {
return (
Expand All @@ -14,7 +13,6 @@ export const ProfileMenu: React.FC = () => {
<AuthenticatedProfileNavButton isMobile={false} />
</nav>
</div>
<OnboardingCheck />
</SessionProvider>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import axios from 'axios'
import type { NextAuthOptions } from 'next-auth'
import Auth0Provider from 'next-auth/providers/auth0'

import { AUTH_CONFIG_SERVER } from '../../../Config'
import { IUserMetadata, UserRole } from '../../../js/types/User'
import { AUTH_CONFIG_SERVER } from '../../../../Config'
import { IUserMetadata, UserRole } from '../../../../js/types/User'
import { initializeUserInDB } from '@/js/auth/initializeUserInDb'

const CustomClaimsNS = 'https://tacos.openbeta.io/'
const CustomClaimUserMetadata = CustomClaimsNS + 'user_metadata'
Expand All @@ -24,7 +25,6 @@ export const authOptions: NextAuthOptions = {
clientSecret,
issuer,
authorization: { params: { audience: 'https://api.openbeta.io', scope: 'offline_access access_token_authz openid email profile read:current_user create:current_user_metadata update:current_user_metadata read:stats update:area_attrs' } },

client: {
token_endpoint_auth_method: clientSecret.length === 0 ? 'none' : 'client_secret_basic'
}
Expand Down Expand Up @@ -84,6 +84,17 @@ export const authOptions: NextAuthOptions = {
throw new Error('Invalid auth data')
}

if (!(token.userMetadata?.initializedDb ?? false)) {
const { userMetadata, email, picture: avatar, id: auth0UserId } = token
const { nick: username, uuid: userUuid } = userMetadata
const { accessToken } = token

const success = await initializeUserInDB({ auth0UserId, accessToken, username, userUuid, avatar, email })
if (success) {
token.userMetadata.initializedDb = true
}
}

if ((token.expiresAt as number) < (Date.now() / 1000)) {
const { accessToken, refreshToken, expiresAt } = await refreshAccessTokenSilently(token.refreshToken as string)
token.accessToken = accessToken
Expand All @@ -109,7 +120,8 @@ export const authOptions: NextAuthOptions = {
}
}

export default NextAuth(authOptions)
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }

const refreshAccessTokenSilently = async (refreshToken: string): Promise<any> => {
const response = await axios.request<{
Expand Down
33 changes: 33 additions & 0 deletions src/app/api/user/me/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { withUserAuth } from '@/js/auth/withUserAuth'
import { NextRequest, NextResponse } from 'next/server'
import useUserProfileCmd from '@/js/hooks/useUserProfileCmd'

/**
* Direct `/api/user/me` to `/u/<user_id`
*/
const getHandler = async (req: NextRequest): Promise<any> => {
const uuid = req.headers.get('x-openbeta-user-uuid')
const accessToken = req.headers.get('x-auth0-access-token')

if (accessToken == null || uuid == null) {
return NextResponse.json({ status: 500 })
}

const url = req.nextUrl.clone()
url.pathname = '/'

try {
const { getUsernameById } = useUserProfileCmd({ accessToken })
const usernameInfo = await getUsernameById({ userUuid: uuid })
if (usernameInfo?.username == null) {
return NextResponse.rewrite(url)
} else {
url.pathname = `/u/${usernameInfo.username}`
return NextResponse.redirect(url)
}
} catch (e) {
return NextResponse.rewrite(url)
}
}

export const GET = withUserAuth(getHandler)
14 changes: 0 additions & 14 deletions src/components/auth/OnboardingCheck.tsx

This file was deleted.

44 changes: 44 additions & 0 deletions src/js/auth/initializeUserInDb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { getClient } from '../graphql/ServerClient'
import { MUTATION_UPDATE_PROFILE } from '../graphql/gql/users'
import { updateUser } from './ManagementClient'

export interface UpdateUsernameInput {
userUuid: string
username: string
email?: string | null
avatar?: string | null
}

interface InitializeUserInDBParams extends UpdateUsernameInput {
accessToken: string
auth0UserId: string
}

export const initializeUserInDB = async (params: InitializeUserInDBParams): Promise<boolean> => {
const { auth0UserId, accessToken, userUuid, username, email, avatar } = params
const res = await getClient().mutate<{ updateUserProfile?: boolean }, UpdateUsernameInput>({
mutation: MUTATION_UPDATE_PROFILE,
variables: {
userUuid,
username,
email,
avatar
},
context: {
headers: {
authorization: `Bearer ${accessToken}`
}
},
fetchPolicy: 'no-cache'
})
const success = res.data?.updateUserProfile ?? false
if (success) {
try {
await updateUser(auth0UserId, { initializedDb: true })
} catch (error) {
console.log('Error initializing user in db')
return false
}
}
return success
}
3 changes: 2 additions & 1 deletion src/js/auth/withUserAuth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getServerSession } from 'next-auth'
import { NextRequest, NextResponse } from 'next/server'
import { authOptions } from 'pages/api/auth/[...nextauth]'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'

type Next13APIHandler = (req: NextRequest) => Promise<any>

Expand All @@ -15,6 +15,7 @@ export const withUserAuth = (handler: Next13APIHandler): Next13APIHandler => {
// Passing useful session data downstream
req.headers.set('x-openbeta-user-uuid', session.user.metadata.uuid)
req.headers.set('x-auth0-userid', session.id)
req.headers.set('x-auth0-access-token', session.accessToken)
return await handler(req)
} else {
return NextResponse.json({ status: 401 })
Expand Down
2 changes: 1 addition & 1 deletion src/js/hooks/useUserProfileCmd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { UserPublicProfile } from '../types/User'
interface GetUsernameByIdInput {
userUuid: string
}
interface UpdateUsernameInput {
export interface UpdateUsernameInput {
userUuid: string
username: string
email?: string
Expand Down
52 changes: 0 additions & 52 deletions src/js/hooks/useUsernameCheck.ts

This file was deleted.

4 changes: 4 additions & 0 deletions src/js/types/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export interface IWritableUserMetadata {
bio: string
website?: string
ticksImported?: boolean
/**
* Indicate whether or not we have initialized user account in our own db
*/
initializedDb?: boolean
collections?: {
/** Users can organize entities into their own 'climbing playlists'
* Strictly speaking, this should always be mutated as a SET rather than an
Expand Down
2 changes: 0 additions & 2 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import '../../public/fonts/fonts.css'
import { useUserGalleryStore } from '../js/stores/useUserGalleryStore'
import { BlockingAlert } from '../components/ui/micro/AlertDialogue'
import { XMarkIcon } from '@heroicons/react/24/outline'
import { OnboardingCheck } from '@/components/auth/OnboardingCheck'

Router.events.on('routeChangeStart', () => NProgress.start())
Router.events.on('routeChangeComplete', () => NProgress.done())
Expand Down Expand Up @@ -42,7 +41,6 @@ export default function MyApp ({ Component, pageProps: { session, ...pageProps }
</>
)
}
<OnboardingCheck isAppDir={false} />
</SessionProvider>
<ToastContainer
position='bottom-right'
Expand Down
2 changes: 1 addition & 1 deletion src/pages/api/basecamp/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import withAuth from '../withAuth'
import { updateUser } from '../../../js/auth/ManagementClient'
import { UserRole } from '../../../js/types'
import { IUserMetadataOriginal } from '../../../js/types/User'
import { authOptions } from '../auth/[...nextauth]'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'

const handler: NextApiHandler<any> = async (req, res) => {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/api/basecamp/userRoles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getServerSession } from 'next-auth'
import withAuth from '../withAuth'
import { getUserRoles, setUserRoles } from '../../../js/auth/ManagementClient'
import { UserRole } from '../../../js/types'
import { authOptions } from '../auth/[...nextauth]'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'

const handler: NextApiHandler<any> = async (req, res) => {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/api/basecamp/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { getServerSession } from 'next-auth'
import withAuth from '../withAuth'
import { getAllUsersMetadata } from '../../../js/auth/ManagementClient'
import { UserRole } from '../../../js/types'
import { authOptions } from '../auth/[...nextauth]'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'

const handler: NextApiHandler<any> = async (req, res) => {
try {
Expand Down
2 changes: 1 addition & 1 deletion src/pages/api/media/get-signed-url.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { nolookalikesSafe } from 'nanoid-dictionary'
import { extname } from 'path'
import { getServerSession } from 'next-auth'
import withAuth from '../withAuth'
import { authOptions } from '../auth/[...nextauth]'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'
import { getSignedUrlForUpload } from '../../../js/media/storageClient'

export interface MediaPreSignedProps {
Expand Down
33 changes: 0 additions & 33 deletions src/pages/api/user/me.ts

This file was deleted.

2 changes: 1 addition & 1 deletion src/pages/api/withAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/
import { NextApiHandler } from 'next'
import { getServerSession } from 'next-auth'
import { authOptions } from './auth/[...nextauth]'
import { authOptions } from '@/app/api/auth/[...nextauth]/route'

const withAuth = (handler: NextApiHandler): NextApiHandler => {
return async (req, res) => {
Expand Down

0 comments on commit b190931

Please sign in to comment.