Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mobile login handler #1207

Merged
merged 5 commits into from
Nov 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ AUTH0_CLIENT_SECRET=send request to hello at openbeta.io
NEXTAUTH_SECRET=vQyFR7gskaqxehN0cI/53r+duWc5Et0ktdoz6KozTCo=

# Auth0 Management API
AUTH0_MGMT_CLIENT_ID=seD3dNxnZ4jXik1dzdQEgPSNSvXGBsqA
AUTH0_MGMT_CLIENT_ID=Ecyj4oke3Cpk1khRsdKr8njen6ZZKePF
AUTH0_MGMT_CLIENT_SECRET=send request to hello at openbeta.io

######### Client-side vars ############
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"@turf/line-to-polygon": "^6.5.0",
"@udecode/zustood": "^1.1.3",
"@vercel/edge": "^1.1.1",
"auth0": "^2.42.0",
"auth0": "^4.12.0",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jumping from v2 -> v4 broke a lot of user management code

"awesome-debounce-promise": "^2.1.0",
"axios": "^0.24.0",
"classnames": "^2.3.1",
Expand Down
2 changes: 2 additions & 0 deletions src/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface AUTH_CONFIG_SERVER_TYPE {
clientSecret: string
mgmtClientId: string
mgmtClientSecret: string
mgmtClientAudience: string
nextauthSecret: string
}

Expand All @@ -26,6 +27,7 @@ if (typeof window === 'undefined') {
clientSecret: checkAndPrintWarning('AUTH0_CLIENT_SECRET', process.env.AUTH0_CLIENT_SECRET),
mgmtClientId: checkAndPrintWarning('AUTH0_MGMT_CLIENT_ID', process.env.AUTH0_MGMT_CLIENT_ID),
mgmtClientSecret: checkAndPrintWarning('AUTH0_MGMT_CLIENT_SECRET', process.env.AUTH0_MGMT_CLIENT_SECRET),
mgmtClientAudience: checkAndPrintWarning('AUTH0_MGMT_CLIENT_AUDIENCE', process.env.AUTH0_MGMT_CLIENT_AUDIENCE),
nextauthSecret: checkAndPrintWarning('NEXTAUTH_SECRET', process.env.NEXTAUTH_SECRET)
}
} else {
Expand Down
40 changes: 40 additions & 0 deletions src/app/api/mobile/login/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { NextRequest, NextResponse } from 'next/server'
import * as Auth0 from 'auth0'
import { auth0Client, isNullOrEmpty } from '@/js/auth/mobile'
import { withMobileAuth } from '@/js/auth/withMobileAuth'

/**
* Mobile login handler
*/
async function postHandler (request: NextRequest): Promise<NextResponse> {
let username: string, password: string
try {
const data = await request.json()
username = data.username
password = data.password

if (isNullOrEmpty(username) || isNullOrEmpty(password)) {
console.error('Empty username/password!')
throw new Error('Invalid payload')
}
} catch (error) {
return NextResponse.json({ error: 'Unexpected error', status: 400 })
}

let response: Auth0.JSONApiResponse<Auth0.TokenSet> | undefined
try {
response = await auth0Client.oauth.passwordGrant({
username,
password,
scope: 'openid profile email offline_access',
audience: 'https://api.openbeta.io'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be generalized to whatever the current environment is?

Copy link
Contributor Author

@vnugent vnugent Nov 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The settings are identical for both dev/stg and prod.

})

return NextResponse.json({ data: response.data })
} catch (error) {
console.error('#### Auth0 error ####', error)
return NextResponse.json({ error: 'Unexpected auth error', status: 403 })
}
}

export const POST = withMobileAuth(postHandler)
37 changes: 37 additions & 0 deletions src/app/api/mobile/refreshToken/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { NextRequest, NextResponse } from 'next/server'
import * as Auth0 from 'auth0'
import { auth0Client, isNullOrEmpty } from '@/js/auth/mobile'
import { withMobileAuth } from '@/js/auth/withMobileAuth'

/**
* Mobile refresh token handler
*/
async function postHandler (request: NextRequest): Promise<any> {
let refreshToken: string
try {
const data = await request.json()
refreshToken = data.refreshToken

if (isNullOrEmpty(refreshToken)) {
console.error('Empty refreshToken!')
throw new Error('Invalid payload')
}
} catch (error) {
return NextResponse.json({ error: 'Unexpected error', status: 400 })
}

let response: Auth0.JSONApiResponse<Auth0.TokenSet> | undefined
try {
response = await auth0Client.oauth.refreshTokenGrant({
refresh_token: refreshToken,
audience: 'https://api.openbeta.io'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same comment as above

})

return NextResponse.json({ data: response.data })
} catch (error) {
console.error('#### Auth0 error ####', error)
return NextResponse.json({ error: 'Unexpected auth error', status: 403 })
}
}

export const POST = withMobileAuth(postHandler)
79 changes: 0 additions & 79 deletions src/components/area/panel/panelList.tsx

This file was deleted.

86 changes: 0 additions & 86 deletions src/components/area/panel/sidePanel.tsx

This file was deleted.

10 changes: 5 additions & 5 deletions src/components/basecamp/UserForm.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import type { User } from 'auth0'
import type { UserProfile } from 'auth0'
import clx from 'classnames'
import { TextArea } from '../ui/form'
import { useForm, FormProvider } from 'react-hook-form'
import { MUUID_VALIDATION, conjoinedStringToArray } from './utils'
import axios from 'axios'
import useSWR from 'swr'
import { Role } from 'auth0'
import { UserInfoResponse } from 'auth0'
import { UserRole } from '../../js/types'
import MultiSelect from '../ui/form/MultiSelect'
import { useEffect } from 'react'

interface UserFormProps {
user: User
user: UserProfile
onClose: () => void
}

Expand All @@ -26,11 +26,11 @@ const fetcher = async (url: string): Promise<any> => (await axios.get(url)).data
* Form for updating users.
*/
export default function UserForm ({ user, onClose }: UserFormProps): JSX.Element {
const userId = user.user_id
const userId = user.user_id as string
if (userId == null) {
return <div>Can't load user. Missing user_id.</div>
}
const { data: roles, error, mutate } = useSWR<Role[]>(`/api/basecamp/userRoles?userId=${userId}`, fetcher)
const { data: roles, error, mutate } = useSWR<UserInfoResponse[]>(`/api/basecamp/userRoles?userId=${userId}`, fetcher)
if (error != null) {
return <div>{error}</div>
}
Expand Down
12 changes: 6 additions & 6 deletions src/components/basecamp/Users.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ import { formatDistanceToNow, parseISO } from 'date-fns'
import { useSession, signIn } from 'next-auth/react'
import useSWR from 'swr'
import axios from 'axios'
import { UserPage } from 'auth0'
import { GetUsers200ResponseOneOf, GetUsers200ResponseOneOfInner } from 'auth0'

import { MagnifyingGlassIcon, PencilSquareIcon } from '@heroicons/react/20/solid'
import { IUserMetadata, UserRole } from '../../js/types/User'
import { usersToCsv, saveAsCSVFile } from '../../js/utils/csv'
import { CLIENT_CONFIG } from '../../js/configs/clientConfig'
import { Input } from '../ui/form'
import { useForm, FormProvider } from 'react-hook-form'
import type { User } from 'auth0'
import type { UserProfile } from 'auth0'
import CreateUpdateModal from './CreateUpdateModal'
import UserForm from './UserForm'
import { RulesType } from '../../js/types'
Expand Down Expand Up @@ -55,7 +55,7 @@ const UserTable = (): JSX.Element => {
const [currentPage, setPage] = useState(0)
const [emailFilter, setEmailFilter] = useState('')
const [modalOpen, setModalOpen] = useState(false)
const [focussedUser, setFocussedUser] = useState<User | null>(null)
const [focussedUser, setFocussedUser] = useState<UserProfile | null>(null)

// React-hook-form declaration
const form = useForm<HtmlFormProps>({
Expand All @@ -67,7 +67,7 @@ const UserTable = (): JSX.Element => {
const { handleSubmit } = form
const submitHandler = ({ email }: HtmlFormProps): void => { setEmailFilter(email) }

const { isLoading, data: userPage, error, mutate } = useSWR<UserPage>(`/api/basecamp/users?page=${currentPage}&email=${emailFilter}&type=auth0`, fetcher)
const { isLoading, data: userPage, error, mutate } = useSWR<GetUsers200ResponseOneOf>(`/api/basecamp/users?page=${currentPage}&email=${emailFilter}&type=auth0`, fetcher)
if (isLoading) return <div className='my-8>'>Loading...</div>

const totalPages = Math.ceil((userPage?.total ?? 0) / (userPage?.limit ?? 0))
Expand Down Expand Up @@ -144,7 +144,7 @@ const UserTable = (): JSX.Element => {
interface UserRowProps {
index: number
user: any
setFocussedUser: (arg0: User) => void
setFocussedUser: (arg0: GetUsers200ResponseOneOfInner) => void
setModalOpen: (arg0: boolean) => void
}

Expand Down Expand Up @@ -182,7 +182,7 @@ const UserRow = ({ index, user, setFocussedUser, setModalOpen }: UserRowProps):

const PasswordlessUsers = (): JSX.Element => {
const [currentPage, setPage] = useState(0)
const { data: userPage } = useSWR<UserPage>(`/api/basecamp/users?page=${currentPage}&type=email`, fetcher)
const { data: userPage } = useSWR<GetUsers200ResponseOneOf>(`/api/basecamp/users?page=${currentPage}&type=email`, fetcher)

const onClickHandler = (userId: string): void => {
void axios.get(`/api/basecamp/migrate?id=${userId}`)
Expand Down
Loading
Loading